summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp6
-rw-r--r--Android.mk4
-rw-r--r--StubLibraries.bp77
-rw-r--r--apct-tests/perftests/contentcapture/Android.bp28
-rw-r--r--apct-tests/perftests/contentcapture/AndroidManifest.xml44
-rw-r--r--apct-tests/perftests/contentcapture/AndroidTest.xml27
-rw-r--r--apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml26
-rw-r--r--apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml50
-rw-r--r--apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java269
-rw-r--r--apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java88
-rw-r--r--apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java155
-rw-r--r--apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java181
-rw-r--r--apct-tests/perftests/core/Android.bp2
-rw-r--r--apct-tests/perftests/core/AndroidManifest.xml2
-rw-r--r--apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java52
-rw-r--r--apct-tests/perftests/core/src/android/view/ViewPerfTest.java12
-rw-r--r--apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java8
-rw-r--r--apct-tests/perftests/windowmanager/Android.bp1
-rw-r--r--apct-tests/perftests/windowmanager/AndroidTest.xml8
-rw-r--r--apct-tests/perftests/windowmanager/README.md27
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java2
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java65
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java130
-rw-r--r--apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java45
-rw-r--r--apex/Android.bp7
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobHandle.java9
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java37
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java87
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java106
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java141
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java18
-rw-r--r--apex/jobscheduler/framework/java/android/app/AlarmManager.java2
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobScheduler.java24
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java43
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java2
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java8
-rw-r--r--apex/jobscheduler/service/Android.bp4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java (renamed from services/core/java/com/android/server/AppStateTracker.java)64
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java25
-rw-r--r--apex/jobscheduler/service/java/com/android/server/TEST_MAPPING9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java78
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobStore.java12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java111
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java32
-rw-r--r--apex/jobscheduler/service/jni/Android.bp41
-rw-r--r--apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp (renamed from services/core/jni/com_android_server_alarm_AlarmManagerService.cpp)144
-rw-r--r--apex/jobscheduler/service/jni/onload.cpp41
-rw-r--r--apex/media/framework/java/android/media/MediaParser.java22
-rw-r--r--apex/media/framework/java/android/media/MediaSession2.java2
-rw-r--r--apex/statsd/TEST_MAPPING9
-rw-r--r--apex/statsd/framework/test/Android.bp11
-rw-r--r--apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java1
-rw-r--r--apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/StatsConfigUtils.java (renamed from apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java)2
-rw-r--r--api/current.txt17
-rw-r--r--api/module-lib-current.txt106
-rw-r--r--api/removed.txt4
-rwxr-xr-xapi/system-current.txt14
-rw-r--r--api/system-removed.txt5
-rw-r--r--api/test-current.txt11
-rw-r--r--api/test-lint-baseline.txt256
-rw-r--r--cmds/idmap2/Android.bp1
-rw-r--r--cmds/idmap2/idmap2/Commands.h1
-rw-r--r--cmds/idmap2/idmap2/Main.cpp6
-rw-r--r--cmds/idmap2/idmap2/Scan.cpp257
-rw-r--r--cmds/idmap2/include/idmap2/FileUtils.h14
-rw-r--r--cmds/idmap2/libidmap2/FileUtils.cpp60
-rw-r--r--cmds/idmap2/tests/FileUtilsTests.cpp57
-rw-r--r--cmds/idmap2/tests/Idmap2BinaryTests.cpp125
-rwxr-xr-xcmds/idmap2/valgrind.sh2
-rw-r--r--cmds/input/Android.bp5
-rwxr-xr-xcmds/input/input3
-rw-r--r--cmds/input/src/com/android/commands/input/Input.java435
-rw-r--r--cmds/statsd/Android.bp4
-rw-r--r--cmds/statsd/TEST_MAPPING12
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp2
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h4
-rw-r--r--cmds/statsd/src/StatsService.cpp24
-rw-r--r--cmds/statsd/src/anomaly/AlarmTracker.cpp4
-rw-r--r--cmds/statsd/src/atoms.proto63
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp81
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.h13
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp14
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h2
-rw-r--r--cmds/statsd/tests/anomaly/AlarmTracker_test.cpp30
-rw-r--r--cmds/statsd/tests/external/StatsPullerManager_test.cpp16
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp38
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp355
-rw-r--r--cmds/statsd/tests/metrics/metrics_test_helper.h9
-rw-r--r--cmds/statsd/tests/shell/ShellSubscriber_test.cpp4
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java3
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java14
-rw-r--r--config/boot-image-profile.txt5
-rw-r--r--config/preloaded-classes9
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java10
-rw-r--r--core/java/android/annotation/SystemApi.java2
-rw-r--r--core/java/android/app/Activity.java32
-rw-r--r--core/java/android/app/ActivityManager.java24
-rw-r--r--core/java/android/app/ActivityManagerInternal.java13
-rw-r--r--core/java/android/app/ActivityTaskManager.java20
-rw-r--r--core/java/android/app/ActivityThread.java79
-rw-r--r--core/java/android/app/AppOpsManager.java14
-rw-r--r--core/java/android/app/ApplicationExitInfo.java2
-rw-r--r--core/java/android/app/ApplicationLoaders.java28
-rw-r--r--core/java/android/app/ApplicationPackageManager.java31
-rw-r--r--core/java/android/app/ContextImpl.java39
-rw-r--r--core/java/android/app/Fragment.java12
-rw-r--r--core/java/android/app/IActivityManager.aidl13
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl7
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/LoadedApk.java59
-rw-r--r--core/java/android/app/Notification.java57
-rw-r--r--core/java/android/app/NotificationManager.java20
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java120
-rw-r--r--core/java/android/app/QueuedWork.java6
-rw-r--r--core/java/android/app/SystemServiceRegistry.java10
-rw-r--r--core/java/android/app/TEST_MAPPING9
-rw-r--r--core/java/android/app/UiAutomationConnection.java2
-rw-r--r--core/java/android/app/backup/BackupManager.java46
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl2
-rw-r--r--core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl1
-rw-r--r--core/java/android/app/timezonedetector/TimeZoneDetector.java19
-rw-r--r--core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java72
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java4
-rw-r--r--core/java/android/bluetooth/BluetoothMapClient.java69
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java7
-rw-r--r--core/java/android/content/ContextWrapper.java3
-rw-r--r--core/java/android/content/IntentFilter.java7
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageInstaller.java5
-rw-r--r--core/java/android/content/pm/PackageManager.java25
-rw-r--r--core/java/android/content/pm/PackageParser.java7
-rw-r--r--core/java/android/content/pm/PermissionInfo.java3
-rw-r--r--core/java/android/content/pm/SharedLibraryInfo.java25
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java31
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackage.java6
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageImpl.java44
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageRead.java13
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java37
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedServiceUtils.java15
-rw-r--r--core/java/android/content/res/AssetManager.java1
-rw-r--r--core/java/android/content/res/Configuration.java14
-rw-r--r--core/java/android/hardware/Camera.java84
-rw-r--r--core/java/android/hardware/GeomagneticField.java137
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java6
-rw-r--r--core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl3
-rw-r--r--core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl2
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java11
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java113
-rw-r--r--core/java/android/hardware/camera2/legacy/BurstHolder.java90
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceState.java362
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java805
-rw-r--r--core/java/android/hardware/camera2/legacy/CaptureCollector.java673
-rw-r--r--core/java/android/hardware/camera2/legacy/GLThreadManager.java264
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java886
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java138
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java265
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java321
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java1532
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyRequest.java67
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java688
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyResultMapper.java529
-rw-r--r--core/java/android/hardware/camera2/legacy/ParameterUtils.java1099
-rw-r--r--core/java/android/hardware/camera2/legacy/PerfMeasurement.java308
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestHandlerThread.java113
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestHolder.java283
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestQueue.java174
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestThreadManager.java1126
-rw-r--r--core/java/android/hardware/camera2/legacy/SizeAreaComparator.java72
-rw-r--r--core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java882
-rw-r--r--core/java/android/hardware/camera2/legacy/package.html3
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java21
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java5
-rw-r--r--core/java/android/hardware/camera2/utils/SurfaceUtils.java105
-rw-r--r--core/java/android/hardware/face/FaceManager.java264
-rw-r--r--core/java/android/hardware/face/FaceSensorProperties.aidl18
-rw-r--r--core/java/android/hardware/face/FaceSensorProperties.java67
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl39
-rw-r--r--core/java/android/hardware/face/IFaceServiceReceiver.aidl2
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java312
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorProperties.aidl18
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorProperties.java85
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl44
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl2
-rw-r--r--core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl28
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java62
-rw-r--r--core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl2
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl2
-rw-r--r--core/java/android/hardware/input/InputManager.java7
-rw-r--r--core/java/android/hardware/usb/UsbAccessory.java1
-rw-r--r--core/java/android/hardware/usb/UsbManager.java62
-rw-r--r--core/java/android/inputmethodservice/InlineSuggestionSession.java5
-rw-r--r--core/java/android/inputmethodservice/InlineSuggestionSessionController.java2
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java3
-rw-r--r--core/java/android/metrics/LogMaker.java2
-rw-r--r--core/java/android/net/ITetheredInterfaceCallback.aidl2
-rw-r--r--core/java/android/net/MacAddress.java14
-rw-r--r--core/java/android/net/NetworkTemplate.java12
-rw-r--r--core/java/android/net/ProxyInfo.java12
-rw-r--r--core/java/android/net/Uri.java71
-rw-r--r--core/java/android/os/Binder.java62
-rw-r--r--core/java/android/os/Debug.java25
-rw-r--r--core/java/android/os/DropBoxManager.java3
-rw-r--r--core/java/android/os/HwBinder.java9
-rw-r--r--core/java/android/os/Parcel.java42
-rw-r--r--core/java/android/os/Parcelable.java33
-rw-r--r--core/java/android/os/ParcelableHolder.java19
-rw-r--r--core/java/android/os/StrictMode.java7
-rw-r--r--core/java/android/os/TEST_MAPPING40
-rw-r--r--core/java/android/os/UserHandle.java2
-rw-r--r--core/java/android/os/VibrationAttributes.java7
-rw-r--r--core/java/android/os/incremental/V4Signature.java2
-rw-r--r--core/java/android/os/storage/StorageManager.java6
-rw-r--r--core/java/android/os/strictmode/LeakedClosableViolation.java5
-rw-r--r--core/java/android/permission/IPermissionManager.aidl2
-rw-r--r--core/java/android/permission/PermissionControllerManager.java18
-rwxr-xr-xcore/java/android/provider/Settings.java39
-rw-r--r--core/java/android/provider/Telephony.java15
-rw-r--r--core/java/android/service/autofill/Dataset.java7
-rw-r--r--core/java/android/service/autofill/InlinePresentation.java20
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java11
-rw-r--r--core/java/android/text/Html.java2
-rwxr-xr-xcore/java/android/text/format/DateFormat.java103
-rw-r--r--core/java/android/text/format/DateIntervalFormat.java126
-rw-r--r--core/java/android/text/format/DateTimeFormat.java60
-rw-r--r--core/java/android/text/format/DateUtils.java56
-rw-r--r--core/java/android/text/format/DateUtilsBridge.java194
-rw-r--r--core/java/android/text/format/OWNERS3
-rw-r--r--core/java/android/text/format/RelativeDateTimeFormatter.java359
-rw-r--r--core/java/android/text/format/Time.java40
-rw-r--r--core/java/android/text/format/TimeFormatter.java113
-rw-r--r--core/java/android/text/method/NumberKeyListener.java4
-rw-r--r--core/java/android/timezone/CountryTimeZones.java17
-rw-r--r--core/java/android/timezone/TelephonyLookup.java9
-rw-r--r--core/java/android/timezone/TelephonyNetwork.java4
-rw-r--r--core/java/android/timezone/TelephonyNetworkFinder.java6
-rw-r--r--core/java/android/timezone/TimeZoneFinder.java10
-rw-r--r--core/java/android/timezone/TzDataSetVersion.java14
-rw-r--r--core/java/android/timezone/ZoneInfoDb.java6
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/util/TimeUtils.java8
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java17
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java33
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java37
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java20
-rw-r--r--core/java/android/util/apk/ApkSigningBlockUtils.java15
-rw-r--r--core/java/android/view/FocusFinder.java19
-rw-r--r--core/java/android/view/IDisplayWindowInsetsController.aidl7
-rw-r--r--core/java/android/view/IPinnedStackListener.aidl5
-rw-r--r--core/java/android/view/IWindowManager.aidl10
-rw-r--r--core/java/android/view/ImeFocusController.java12
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java12
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java2
-rw-r--r--core/java/android/view/InsetsController.java36
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java3
-rw-r--r--core/java/android/view/InsetsSourceControl.java2
-rw-r--r--core/java/android/view/InsetsState.java22
-rw-r--r--core/java/android/view/MotionEvent.java29
-rw-r--r--core/java/android/view/SurfaceControl.java59
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java7
-rw-r--r--core/java/android/view/SurfaceView.java16
-rw-r--r--core/java/android/view/View.java119
-rw-r--r--core/java/android/view/ViewGroup.java89
-rw-r--r--core/java/android/view/ViewRootImpl.java114
-rw-r--r--core/java/android/view/WindowInsets.java25
-rw-r--r--core/java/android/view/WindowManager.java165
-rw-r--r--core/java/android/view/WindowManagerImpl.java2
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java7
-rw-r--r--core/java/android/view/WindowlessWindowManager.java5
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java4
-rw-r--r--core/java/android/view/autofill/AutofillManager.java65
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java3
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java10
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java85
-rw-r--r--core/java/android/webkit/WebView.java18
-rw-r--r--core/java/android/webkit/WebViewClient.java10
-rw-r--r--core/java/android/webkit/WebViewDelegate.java48
-rw-r--r--core/java/android/webkit/WebViewProvider.java7
-rw-r--r--core/java/android/widget/CalendarViewLegacyDelegate.java4
-rw-r--r--core/java/android/widget/CompoundButton.java8
-rw-r--r--core/java/android/widget/DatePickerSpinnerDelegate.java4
-rw-r--r--core/java/android/widget/DayPickerView.java6
-rw-r--r--core/java/android/widget/Editor.java5
-rw-r--r--core/java/android/widget/EditorTouchState.java9
-rw-r--r--core/java/android/widget/Magnifier.java3
-rw-r--r--core/java/android/widget/NumberPicker.java5
-rw-r--r--core/java/android/widget/ProgressBar.java15
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java26
-rw-r--r--core/java/android/widget/SimpleMonthView.java6
-rw-r--r--core/java/android/widget/Switch.java6
-rw-r--r--core/java/android/widget/TextClock.java28
-rw-r--r--core/java/android/widget/TextView.java5
-rw-r--r--core/java/android/widget/TextViewRichContentReceiver.java3
-rw-r--r--core/java/android/widget/TimePicker.java12
-rw-r--r--core/java/android/widget/TimePickerSpinnerDelegate.java12
-rw-r--r--core/java/android/widget/ToggleButton.java6
-rw-r--r--core/java/android/widget/inline/InlineContentView.java5
-rw-r--r--core/java/android/window/DisplayAreaOrganizer.java8
-rw-r--r--core/java/android/window/TaskEmbedder.java1
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java52
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java41
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java4
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java34
-rw-r--r--core/java/com/android/internal/app/ProcessMap.java4
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java57
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java95
-rw-r--r--core/java/com/android/internal/app/SuspendedAppActivity.java24
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java7
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java2
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodDebug.java8
-rw-r--r--core/java/com/android/internal/inputmethod/StartInputReason.java8
-rw-r--r--core/java/com/android/internal/listeners/ListenerExecutor.java136
-rw-r--r--core/java/com/android/internal/listeners/ListenerTransport.java4
-rw-r--r--core/java/com/android/internal/listeners/ListenerTransportManager.java145
-rw-r--r--core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java16
-rw-r--r--core/java/com/android/internal/logging/AndroidHandler.java29
-rw-r--r--core/java/com/android/internal/net/VpnProfile.java3
-rw-r--r--core/java/com/android/internal/os/BinderCallHeavyHitterWatcher.java414
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java40
-rw-r--r--core/java/com/android/internal/os/ClassLoaderFactory.java15
-rw-r--r--core/java/com/android/internal/os/IDropBoxManagerService.aidl6
-rw-r--r--core/java/com/android/internal/os/KernelWakelockReader.java29
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java16
-rw-r--r--core/java/com/android/internal/util/HeavyHitterSketch.java341
-rw-r--r--core/java/com/android/internal/util/StateMachine.java5
-rw-r--r--core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl54
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl3
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java2
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java17
-rw-r--r--core/java/com/android/server/SystemConfig.java82
-rw-r--r--core/jni/Android.bp3
-rw-r--r--core/jni/AndroidRuntime.cpp6
-rw-r--r--core/jni/android_hardware_Camera.cpp19
-rw-r--r--core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp841
-rw-r--r--core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp335
-rw-r--r--core/jni/android_hardware_camera2_utils_SurfaceUtils.cpp229
-rw-r--r--core/jni/android_hardware_input_InputApplicationHandle.cpp4
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.cpp31
-rw-r--r--core/jni/android_os_Debug.cpp5
-rw-r--r--core/jni/android_os_HwBinder.cpp7
-rw-r--r--core/jni/android_os_HwParcel.cpp16
-rw-r--r--core/jni/android_os_Parcel.cpp60
-rw-r--r--core/jni/android_os_VintfRuntimeInfo.cpp18
-rw-r--r--core/jni/android_util_AssetManager.cpp86
-rw-r--r--core/jni/android_util_Binder.cpp7
-rw-r--r--core/jni/android_view_InputChannel.cpp37
-rw-r--r--core/jni/android_view_InputChannel.h7
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp25
-rw-r--r--core/jni/android_view_InputEventSender.cpp22
-rw-r--r--core/jni/android_view_MotionEvent.cpp18
-rw-r--r--core/jni/com_android_internal_os_ClassLoaderFactory.cpp9
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp51
-rw-r--r--core/proto/android/app/settings_enums.proto7
-rw-r--r--core/proto/android/providers/settings/global.proto2
-rw-r--r--core/proto/android/providers/settings/secure.proto28
-rw-r--r--core/proto/android/server/jobscheduler.proto6
-rw-r--r--core/proto/android/server/windowmanagerservice.proto20
-rw-r--r--core/proto/android/typedef.proto30
-rw-r--r--core/proto/android/view/windowlayoutparams.proto25
-rw-r--r--core/res/AndroidManifest.xml13
-rw-r--r--core/res/res/anim/screen_rotate_180_enter.xml6
-rw-r--r--core/res/res/anim/screen_rotate_180_exit.xml7
-rw-r--r--core/res/res/layout/notification_material_action_list.xml1
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml7
-rw-r--r--core/res/res/values-af/strings.xml10
-rw-r--r--core/res/res/values-am/strings.xml10
-rw-r--r--core/res/res/values-ar/strings.xml16
-rw-r--r--core/res/res/values-as/strings.xml10
-rw-r--r--core/res/res/values-az/strings.xml12
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml14
-rw-r--r--core/res/res/values-be/strings.xml10
-rw-r--r--core/res/res/values-bg/strings.xml10
-rw-r--r--core/res/res/values-bn/strings.xml12
-rw-r--r--core/res/res/values-bs/strings.xml18
-rw-r--r--core/res/res/values-ca/strings.xml7
-rw-r--r--core/res/res/values-cs/strings.xml12
-rw-r--r--core/res/res/values-da/strings.xml10
-rw-r--r--core/res/res/values-de/strings.xml24
-rw-r--r--core/res/res/values-el/strings.xml10
-rw-r--r--core/res/res/values-en-rAU/strings.xml16
-rw-r--r--core/res/res/values-en-rCA/strings.xml12
-rw-r--r--core/res/res/values-en-rGB/strings.xml10
-rw-r--r--core/res/res/values-en-rIN/strings.xml18
-rw-r--r--core/res/res/values-en-rXC/strings.xml9
-rw-r--r--core/res/res/values-es-rUS/strings.xml14
-rw-r--r--core/res/res/values-es/strings.xml12
-rw-r--r--core/res/res/values-et/strings.xml10
-rw-r--r--core/res/res/values-eu/strings.xml20
-rw-r--r--core/res/res/values-fa/strings.xml18
-rw-r--r--core/res/res/values-fi/strings.xml14
-rw-r--r--core/res/res/values-fr-rCA/strings.xml12
-rw-r--r--core/res/res/values-fr/strings.xml16
-rw-r--r--core/res/res/values-gl/strings.xml12
-rw-r--r--core/res/res/values-gu/strings.xml12
-rw-r--r--core/res/res/values-hi/strings.xml14
-rw-r--r--core/res/res/values-hr/strings.xml10
-rw-r--r--core/res/res/values-hu/strings.xml10
-rw-r--r--core/res/res/values-hy/strings.xml10
-rw-r--r--core/res/res/values-in/strings.xml12
-rw-r--r--core/res/res/values-is/strings.xml10
-rw-r--r--core/res/res/values-it/strings.xml12
-rw-r--r--core/res/res/values-iw/strings.xml13
-rw-r--r--core/res/res/values-ja/strings.xml12
-rw-r--r--core/res/res/values-ka/strings.xml10
-rw-r--r--core/res/res/values-kk/strings.xml12
-rw-r--r--core/res/res/values-km/strings.xml14
-rw-r--r--core/res/res/values-kn/strings.xml12
-rw-r--r--core/res/res/values-ko/strings.xml10
-rw-r--r--core/res/res/values-ky/strings.xml18
-rw-r--r--core/res/res/values-lo/strings.xml10
-rw-r--r--core/res/res/values-lt/strings.xml10
-rw-r--r--core/res/res/values-lv/strings.xml10
-rw-r--r--core/res/res/values-mcc260/config.xml (renamed from packages/SystemUI/res/layout/tv_pip_custom_control.xml)16
-rw-r--r--core/res/res/values-mcc262/config.xml25
-rw-r--r--core/res/res/values-mk/strings.xml12
-rw-r--r--core/res/res/values-ml/strings.xml12
-rw-r--r--core/res/res/values-mn/strings.xml10
-rw-r--r--core/res/res/values-mr/strings.xml12
-rw-r--r--core/res/res/values-ms/strings.xml10
-rw-r--r--core/res/res/values-my/strings.xml14
-rw-r--r--core/res/res/values-nb/strings.xml10
-rw-r--r--core/res/res/values-ne/strings.xml18
-rw-r--r--core/res/res/values-nl/strings.xml12
-rw-r--r--core/res/res/values-or/strings.xml12
-rw-r--r--core/res/res/values-pa/strings.xml10
-rw-r--r--core/res/res/values-pl/strings.xml10
-rw-r--r--core/res/res/values-pt-rBR/strings.xml12
-rw-r--r--core/res/res/values-pt-rPT/strings.xml28
-rw-r--r--core/res/res/values-pt/strings.xml12
-rw-r--r--core/res/res/values-ro/strings.xml10
-rw-r--r--core/res/res/values-ru/strings.xml14
-rw-r--r--core/res/res/values-si/strings.xml10
-rw-r--r--core/res/res/values-sk/strings.xml10
-rw-r--r--core/res/res/values-sl/strings.xml10
-rw-r--r--core/res/res/values-sq/strings.xml14
-rw-r--r--core/res/res/values-sr/strings.xml14
-rw-r--r--core/res/res/values-sv/strings.xml12
-rw-r--r--core/res/res/values-sw/strings.xml10
-rw-r--r--core/res/res/values-ta/strings.xml10
-rw-r--r--core/res/res/values-te/strings.xml14
-rw-r--r--core/res/res/values-television/config.xml9
-rw-r--r--core/res/res/values-th/strings.xml10
-rw-r--r--core/res/res/values-tl/strings.xml12
-rw-r--r--core/res/res/values-tr/strings.xml10
-rw-r--r--core/res/res/values-uk/strings.xml12
-rw-r--r--core/res/res/values-ur/strings.xml16
-rw-r--r--core/res/res/values-uz/strings.xml18
-rw-r--r--core/res/res/values-vi/strings.xml20
-rw-r--r--core/res/res/values-zh-rCN/strings.xml14
-rw-r--r--core/res/res/values-zh-rHK/strings.xml10
-rw-r--r--core/res/res/values-zh-rTW/strings.xml16
-rw-r--r--core/res/res/values-zu/strings.xml12
-rw-r--r--core/res/res/values/attrs_manifest.xml23
-rw-r--r--core/res/res/values/config.xml47
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--core/res/res/values/symbols.xml13
-rw-r--r--core/tests/bluetoothtests/AndroidManifest.xml6
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java24
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java11
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java156
-rw-r--r--core/tests/bugreports/Android.bp6
-rw-r--r--core/tests/bugreports/AndroidTest.xml13
-rwxr-xr-xcore/tests/bugreports/run.sh61
-rw-r--r--core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java (renamed from core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java)49
-rw-r--r--core/tests/coretests/src/android/content/ContextTest.java23
-rw-r--r--core/tests/coretests/src/android/graphics/PaintNativeInstanceTest.kt161
-rw-r--r--core/tests/coretests/src/android/graphics/PathTest.java4
-rw-r--r--core/tests/coretests/src/android/net/UriTest.java17
-rw-r--r--core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java23
-rw-r--r--core/tests/coretests/src/android/text/format/DateFormatTest.java80
-rw-r--r--core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java671
-rw-r--r--core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java762
-rw-r--r--core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java2
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java45
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java40
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java37
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java19
-rw-r--r--core/tests/coretests/src/android/widget/EditorCursorDragTest.java6
-rw-r--r--core/tests/coretests/src/android/widget/EditorTouchStateTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java128
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java20
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java39
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java187
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/HeavyHitterSketchTest.java209
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java24
-rw-r--r--data/etc/Android.bp8
-rw-r--r--data/etc/com.android.cellbroadcastreceiver.xml26
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--data/etc/platform.xml6
-rw-r--r--data/etc/preinstalled-packages-platform-overlays.xml156
-rw-r--r--data/etc/privapp-permissions-platform.xml12
-rw-r--r--data/etc/services.core.protolog.json30
-rw-r--r--data/keyboards/Generic.kl5
-rw-r--r--data/keyboards/OWNERS1
-rw-r--r--data/keyboards/Vendor_046d_Product_c216.kl10
-rw-r--r--data/keyboards/Vendor_056e_Product_2010.kl48
-rw-r--r--data/keyboards/Vendor_056e_Product_2013.kl44
-rw-r--r--data/keyboards/Vendor_1532_Product_0705.kl64
-rw-r--r--data/keyboards/Vendor_1532_Product_0707.kl64
-rw-r--r--data/keyboards/Vendor_1532_Product_0709.kl51
-rw-r--r--data/keyboards/Vendor_1532_Product_1004.kl65
-rw-r--r--data/keyboards/Vendor_1532_Product_1007.kl65
-rw-r--r--data/keyboards/Vendor_1532_Product_1009.kl65
-rw-r--r--data/keyboards/Vendor_1532_Product_100a.kl65
-rw-r--r--data/keyboards/Vendor_27f8_Product_0bbf.kl54
-rw-r--r--data/keyboards/Vendor_2e95_Product_7725.kl64
-rw-r--r--data/keyboards/Virtual.kcm2
-rw-r--r--errorprone/Android.bp29
-rw-r--r--errorprone/TEST_MAPPING7
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java96
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java108
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java95
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java108
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/TargetSdkCheckerTest.java67
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java101
-rw-r--r--errorprone/tests/res/android/annotation/SystemService.java21
-rw-r--r--errorprone/tests/res/android/content/Context.java23
-rw-r--r--errorprone/tests/res/android/foo/IFooService.java24
-rw-r--r--errorprone/tests/res/android/os/Binder.java23
-rw-r--r--errorprone/tests/res/android/os/Build.java23
-rw-r--r--errorprone/tests/res/android/os/IInterface.java20
-rw-r--r--errorprone/tests/res/android/os/RemoteException.java23
-rw-r--r--errorprone/tests/res/android/os/UserHandle.java (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java)24
-rw-r--r--graphics/java/android/graphics/ColorFilter.java4
-rw-r--r--graphics/java/android/graphics/ComposeShader.java10
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java7
-rw-r--r--graphics/java/android/graphics/Paint.java7
-rw-r--r--graphics/java/android/graphics/RecordingCanvas.java45
-rw-r--r--graphics/java/android/graphics/Region.java4
-rw-r--r--graphics/java/android/graphics/Shader.java21
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java5
-rw-r--r--libs/WindowManager/Shell/Android.bp4
-rw-r--r--libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml (renamed from packages/SystemUI/res/anim/tv_pip_controls_focus_gain_animation.xml)3
-rw-r--r--libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml (renamed from packages/SystemUI/res/anim/tv_pip_controls_focus_loss_animation.xml)3
-rw-r--r--libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml (renamed from packages/SystemUI/res/anim/tv_pip_menu_fade_in_animation.xml)3
-rw-r--r--libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml (renamed from packages/SystemUI/res/anim/tv_pip_menu_fade_out_animation.xml)3
-rw-r--r--libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient.xml25
-rw-r--r--libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient_transition.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_expand.xml (renamed from packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml)17
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml25
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_fullscreen_white.xml25
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml (renamed from packages/SystemUI/res/drawable/ic_pause_white.xml)5
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml (renamed from packages/SystemUI/res/drawable/ic_play_arrow_white.xml)5
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml28
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_icon.xml (renamed from packages/SystemUI/res/drawable/pip_icon.xml)4
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_resize_handle.xml (renamed from packages/SystemUI/res/drawable/pip_resize_handle.xml)4
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml18
-rw-r--r--libs/WindowManager/Shell/res/layout/pip_menu_action.xml23
-rw-r--r--libs/WindowManager/Shell/res/layout/pip_menu_activity.xml (renamed from packages/SystemUI/res/layout/pip_menu_activity.xml)63
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml (renamed from packages/SystemUI/res/layout/tv_pip_control_button.xml)29
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_controls.xml (renamed from packages/SystemUI/res/layout/tv_pip_controls.xml)35
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml21
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu.xml31
-rw-r--r--libs/WindowManager/Shell/res/values-tvdpi/dimen.xml22
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml42
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml59
-rw-r--r--libs/WindowManager/Shell/res/values/ids.xml19
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml56
-rw-r--r--libs/WindowManager/Shell/res/values/strings_tv.xml34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java (renamed from packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java)2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java (renamed from packages/SystemUI/src/com/android/systemui/wm/DisplayController.java)16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java (renamed from packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java)169
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java (renamed from packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java)25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java (renamed from packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java)32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java (renamed from packages/SystemUI/src/com/android/systemui/TransactionPool.java)9
-rw-r--r--libs/WindowManager/Shell/tests/Android.bp3
-rw-r--r--libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java (renamed from libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java)6
-rw-r--r--libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java)5
-rw-r--r--libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp46
-rw-r--r--libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arscbin0 -> 724 bytes
-rw-r--r--libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp39
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp3
-rw-r--r--libs/hwui/JankTracker.cpp3
-rw-r--r--libs/hwui/JankTracker.h2
-rw-r--r--libs/hwui/SkiaCanvas.cpp5
-rw-r--r--libs/hwui/SkiaCanvas.h2
-rw-r--r--libs/hwui/hwui/Bitmap.cpp7
-rw-r--r--libs/hwui/hwui/Canvas.h4
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp16
-rw-r--r--libs/hwui/jni/FontFamily.cpp12
-rw-r--r--libs/hwui/jni/android_graphics_DisplayListCanvas.cpp38
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp7
-rw-r--r--libs/hwui/jni/fonts/Font.cpp12
-rw-r--r--libs/hwui/pipeline/skia/FunctorDrawable.h31
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.cpp42
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.h2
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp17
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h3
-rw-r--r--libs/hwui/pipeline/skia/VkFunctorDrawable.cpp7
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp15
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h2
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp4
-rw-r--r--libs/hwui/renderthread/EglManager.cpp9
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp14
-rw-r--r--libs/hwui/renderthread/RenderProxy.h1
-rw-r--r--libs/hwui/tests/common/TestUtils.h24
-rwxr-xr-xlibs/hwui/tests/scripts/prep_generic.sh209
-rw-r--r--libs/hwui/tests/unit/CanvasContextTests.cpp11
-rw-r--r--libs/hwui/tests/unit/RenderNodeTests.cpp50
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp17
-rw-r--r--libs/hwui/utils/MathUtils.h4
-rw-r--r--libs/input/PointerController.cpp120
-rw-r--r--libs/input/PointerController.h72
-rw-r--r--libs/input/tests/PointerController_test.cpp16
-rw-r--r--location/java/android/location/GpsStatus.java10
-rw-r--r--location/java/android/location/ILocationCallback.aidl28
-rw-r--r--location/java/android/location/ILocationListener.aidl43
-rw-r--r--location/java/android/location/ILocationManager.aidl12
-rw-r--r--location/java/android/location/LocationListener.java15
-rw-r--r--location/java/android/location/LocationManager.java305
-rw-r--r--location/java/android/location/package.html2
-rw-r--r--location/java/android/location/util/LocationListenerTransportManager.java176
-rw-r--r--location/java/com/android/internal/location/ILocationProviderManager.aidl9
-rw-r--r--location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java30
-rw-r--r--location/lib/java/com/android/location/provider/LocationProviderBase.java11
-rw-r--r--media/java/android/media/AudioAttributes.java6
-rw-r--r--media/java/android/media/AudioDeviceInfo.java15
-rw-r--r--media/java/android/media/ExifInterface.java4
-rw-r--r--media/java/android/media/ImageReader.java14
-rw-r--r--media/java/android/media/MediaCas.java9
-rw-r--r--media/java/android/media/MediaCodecInfo.java38
-rw-r--r--media/java/android/media/MediaFrameworkInitializer.java74
-rw-r--r--media/java/android/media/MediaMetrics.java5
-rw-r--r--media/java/android/media/MediaRoute2Info.java8
-rw-r--r--media/java/android/media/MediaRouter2.java7
-rw-r--r--media/java/android/media/MediaServiceManager.java68
-rw-r--r--media/java/android/media/MediaTranscodeManager.java61
-rw-r--r--media/java/android/media/audiofx/AudioEffect.java20
-rw-r--r--media/java/android/media/audiofx/HapticGenerator.java128
-rw-r--r--media/java/android/media/browse/MediaBrowser.java13
-rw-r--r--media/java/android/media/session/MediaSessionManager.java9
-rw-r--r--media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl4
-rw-r--r--media/java/android/service/media/MediaBrowserService.java2
-rw-r--r--media/jni/android_media_ImageWriter.cpp43
-rw-r--r--media/jni/android_media_tv_Tuner.cpp76
-rw-r--r--media/jni/audioeffect/Visualizer.cpp1
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java28
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java53
-rw-r--r--media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh11
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java66
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java332
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java1
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java480
-rw-r--r--non-updatable-api/current.txt17
-rw-r--r--non-updatable-api/module-lib-current.txt16
-rw-r--r--non-updatable-api/removed.txt4
-rw-r--r--non-updatable-api/system-current.txt13
-rw-r--r--non-updatable-api/system-removed.txt5
-rw-r--r--packages/CarSystemUI/Android.bp10
-rw-r--r--packages/CarSystemUI/TEST_MAPPING10
-rw-r--r--packages/CarSystemUI/res/layout/car_user_switching_dialog.xml1
-rw-r--r--packages/CarSystemUI/res/layout/headsup_container_bottom.xml7
-rw-r--r--packages/CarSystemUI/res/layout/notification_center_activity.xml16
-rw-r--r--packages/CarSystemUI/res/layout/super_notification_shade.xml80
-rw-r--r--packages/CarSystemUI/res/layout/super_status_bar.xml46
-rw-r--r--packages/CarSystemUI/res/layout/system_icons.xml6
-rw-r--r--packages/CarSystemUI/res/layout/sysui_overlay_window.xml4
-rw-r--r--packages/CarSystemUI/res/values/dimens.xml15
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java20
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java42
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java9
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java32
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java9
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java22
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java7
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java12
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java519
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java137
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java283
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarStatusBarHeader.java4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java36
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java107
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java19
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java48
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java8
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java250
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java175
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java173
-rw-r--r--packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java29
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java2
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java258
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java195
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java109
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java11
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java4
-rw-r--r--packages/DynamicSystemInstallationService/tests/res/values/strings.xml2
-rw-r--r--packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java46
-rw-r--r--packages/EasterEgg/Android.bp18
-rw-r--r--packages/EasterEgg/AndroidManifest.xml99
-rw-r--r--packages/EasterEgg/build.gradle82
-rw-r--r--packages/EasterEgg/gradle.properties23
-rw-r--r--packages/EasterEgg/res/drawable/android_11_dial.xml63
-rw-r--r--packages/EasterEgg/res/drawable/back.xml22
-rw-r--r--packages/EasterEgg/res/drawable/belly.xml22
-rw-r--r--packages/EasterEgg/res/drawable/body.xml22
-rw-r--r--packages/EasterEgg/res/drawable/bowtie.xml22
-rw-r--r--packages/EasterEgg/res/drawable/cap.xml22
-rw-r--r--packages/EasterEgg/res/drawable/collar.xml22
-rw-r--r--packages/EasterEgg/res/drawable/face_spot.xml22
-rw-r--r--packages/EasterEgg/res/drawable/food_bits.xml33
-rw-r--r--packages/EasterEgg/res/drawable/food_chicken.xml39
-rw-r--r--packages/EasterEgg/res/drawable/food_cookie.xml35
-rw-r--r--packages/EasterEgg/res/drawable/food_dish.xml24
-rw-r--r--packages/EasterEgg/res/drawable/food_donut.xml24
-rw-r--r--packages/EasterEgg/res/drawable/food_sysuituna.xml24
-rw-r--r--packages/EasterEgg/res/drawable/foot1.xml22
-rw-r--r--packages/EasterEgg/res/drawable/foot2.xml22
-rw-r--r--packages/EasterEgg/res/drawable/foot3.xml22
-rw-r--r--packages/EasterEgg/res/drawable/foot4.xml22
-rw-r--r--packages/EasterEgg/res/drawable/head.xml22
-rw-r--r--packages/EasterEgg/res/drawable/ic_bowl.xml34
-rw-r--r--packages/EasterEgg/res/drawable/ic_close.xml24
-rw-r--r--packages/EasterEgg/res/drawable/ic_foodbowl_filled.xml44
-rw-r--r--packages/EasterEgg/res/drawable/ic_fullcat_icon.xml108
-rw-r--r--packages/EasterEgg/res/drawable/ic_share.xml24
-rw-r--r--packages/EasterEgg/res/drawable/ic_toy_ball.xml29
-rw-r--r--packages/EasterEgg/res/drawable/ic_toy_fish.xml41
-rw-r--r--packages/EasterEgg/res/drawable/ic_toy_laser.xml42
-rw-r--r--packages/EasterEgg/res/drawable/ic_toy_mouse.xml45
-rw-r--r--packages/EasterEgg/res/drawable/ic_water.xml27
-rw-r--r--packages/EasterEgg/res/drawable/ic_water_filled.xml24
-rw-r--r--packages/EasterEgg/res/drawable/ic_waterbowl_filled.xml33
-rw-r--r--packages/EasterEgg/res/drawable/icon.xml19
-rw-r--r--packages/EasterEgg/res/drawable/icon_bg.xml8
-rw-r--r--packages/EasterEgg/res/drawable/left_ear.xml22
-rw-r--r--packages/EasterEgg/res/drawable/left_ear_inside.xml22
-rw-r--r--packages/EasterEgg/res/drawable/left_eye.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg1.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg2.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg2_shadow.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg3.xml22
-rw-r--r--packages/EasterEgg/res/drawable/leg4.xml22
-rw-r--r--packages/EasterEgg/res/drawable/mouth.xml27
-rw-r--r--packages/EasterEgg/res/drawable/nose.xml22
-rw-r--r--packages/EasterEgg/res/drawable/octo_bg.xml8
-rw-r--r--packages/EasterEgg/res/drawable/right_ear.xml22
-rw-r--r--packages/EasterEgg/res/drawable/right_ear_inside.xml23
-rw-r--r--packages/EasterEgg/res/drawable/right_eye.xml22
-rw-r--r--packages/EasterEgg/res/drawable/stat_icon.xml30
-rw-r--r--packages/EasterEgg/res/drawable/tail.xml26
-rw-r--r--packages/EasterEgg/res/drawable/tail_cap.xml22
-rw-r--r--packages/EasterEgg/res/drawable/tail_shadow.xml22
-rw-r--r--packages/EasterEgg/res/layout/activity_paint.xml4
-rw-r--r--packages/EasterEgg/res/layout/cat_view.xml82
-rw-r--r--packages/EasterEgg/res/layout/edit_text.xml30
-rw-r--r--packages/EasterEgg/res/layout/food_layout.xml31
-rw-r--r--packages/EasterEgg/res/layout/neko_activity.xml25
-rw-r--r--packages/EasterEgg/res/values/cat_strings.xml71
-rw-r--r--packages/EasterEgg/res/values/dimens.xml19
-rw-r--r--packages/EasterEgg/res/values/strings.xml4
-rw-r--r--packages/EasterEgg/res/xml/filepaths.xml (renamed from packages/SystemUI/res/drawable/pip_expand.xml)16
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/Cat.java524
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/Food.java61
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java66
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoControlsService.kt323
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java109
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoLand.java340
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java48
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoService.java193
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/NekoTile.java116
-rw-r--r--packages/EasterEgg/src/com/android/egg/neko/PrefState.java104
-rw-r--r--packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java2
-rw-r--r--packages/InputDevices/res/raw/keyboard_layout_georgian.kcm383
-rw-r--r--packages/InputDevices/res/values-af/strings.xml1
-rw-r--r--packages/InputDevices/res/values-am/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ar/strings.xml1
-rw-r--r--packages/InputDevices/res/values-as/strings.xml1
-rw-r--r--packages/InputDevices/res/values-az/strings.xml1
-rw-r--r--packages/InputDevices/res/values-b+sr+Latn/strings.xml1
-rw-r--r--packages/InputDevices/res/values-be/strings.xml1
-rw-r--r--packages/InputDevices/res/values-bg/strings.xml1
-rw-r--r--packages/InputDevices/res/values-bn/strings.xml1
-rw-r--r--packages/InputDevices/res/values-bs/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ca/strings.xml1
-rw-r--r--packages/InputDevices/res/values-cs/strings.xml1
-rw-r--r--packages/InputDevices/res/values-da/strings.xml1
-rw-r--r--packages/InputDevices/res/values-de/strings.xml1
-rw-r--r--packages/InputDevices/res/values-el/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rAU/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rCA/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rGB/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rIN/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rXC/strings.xml1
-rw-r--r--packages/InputDevices/res/values-es-rUS/strings.xml1
-rw-r--r--packages/InputDevices/res/values-es/strings.xml1
-rw-r--r--packages/InputDevices/res/values-et/strings.xml1
-rw-r--r--packages/InputDevices/res/values-eu/strings.xml1
-rw-r--r--packages/InputDevices/res/values-fa/strings.xml1
-rw-r--r--packages/InputDevices/res/values-fi/strings.xml1
-rw-r--r--packages/InputDevices/res/values-fr-rCA/strings.xml1
-rw-r--r--packages/InputDevices/res/values-fr/strings.xml1
-rw-r--r--packages/InputDevices/res/values-gl/strings.xml1
-rw-r--r--packages/InputDevices/res/values-gu/strings.xml1
-rw-r--r--packages/InputDevices/res/values-hi/strings.xml1
-rw-r--r--packages/InputDevices/res/values-hr/strings.xml1
-rw-r--r--packages/InputDevices/res/values-hu/strings.xml1
-rw-r--r--packages/InputDevices/res/values-hy/strings.xml1
-rw-r--r--packages/InputDevices/res/values-in/strings.xml1
-rw-r--r--packages/InputDevices/res/values-is/strings.xml1
-rw-r--r--packages/InputDevices/res/values-it/strings.xml1
-rw-r--r--packages/InputDevices/res/values-iw/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ja/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ka/strings.xml1
-rw-r--r--packages/InputDevices/res/values-kk/strings.xml1
-rw-r--r--packages/InputDevices/res/values-km/strings.xml1
-rw-r--r--packages/InputDevices/res/values-kn/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ko/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ky/strings.xml1
-rw-r--r--packages/InputDevices/res/values-lo/strings.xml1
-rw-r--r--packages/InputDevices/res/values-lt/strings.xml1
-rw-r--r--packages/InputDevices/res/values-lv/strings.xml1
-rw-r--r--packages/InputDevices/res/values-mk/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ml/strings.xml1
-rw-r--r--packages/InputDevices/res/values-mn/strings.xml1
-rw-r--r--packages/InputDevices/res/values-mr/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ms/strings.xml1
-rw-r--r--packages/InputDevices/res/values-my/strings.xml1
-rw-r--r--packages/InputDevices/res/values-nb/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ne/strings.xml1
-rw-r--r--packages/InputDevices/res/values-nl/strings.xml1
-rw-r--r--packages/InputDevices/res/values-or/strings.xml1
-rw-r--r--packages/InputDevices/res/values-pa/strings.xml1
-rw-r--r--packages/InputDevices/res/values-pl/strings.xml1
-rw-r--r--packages/InputDevices/res/values-pt-rBR/strings.xml1
-rw-r--r--packages/InputDevices/res/values-pt-rPT/strings.xml1
-rw-r--r--packages/InputDevices/res/values-pt/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ro/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ru/strings.xml1
-rw-r--r--packages/InputDevices/res/values-si/strings.xml1
-rw-r--r--packages/InputDevices/res/values-sk/strings.xml1
-rw-r--r--packages/InputDevices/res/values-sl/strings.xml1
-rw-r--r--packages/InputDevices/res/values-sq/strings.xml1
-rw-r--r--packages/InputDevices/res/values-sr/strings.xml1
-rw-r--r--packages/InputDevices/res/values-sv/strings.xml1
-rw-r--r--packages/InputDevices/res/values-sw/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ta/strings.xml1
-rw-r--r--packages/InputDevices/res/values-te/strings.xml1
-rw-r--r--packages/InputDevices/res/values-th/strings.xml1
-rw-r--r--packages/InputDevices/res/values-tl/strings.xml1
-rw-r--r--packages/InputDevices/res/values-tr/strings.xml1
-rw-r--r--packages/InputDevices/res/values-uk/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ur/strings.xml1
-rw-r--r--packages/InputDevices/res/values-uz/strings.xml1
-rw-r--r--packages/InputDevices/res/values-vi/strings.xml1
-rw-r--r--packages/InputDevices/res/values-zh-rCN/strings.xml1
-rw-r--r--packages/InputDevices/res/values-zh-rHK/strings.xml1
-rw-r--r--packages/InputDevices/res/values-zh-rTW/strings.xml1
-rw-r--r--packages/InputDevices/res/values-zu/strings.xml1
-rw-r--r--packages/InputDevices/res/values/strings.xml3
-rw-r--r--packages/InputDevices/res/xml/keyboard_layouts.xml4
-rw-r--r--packages/PackageInstaller/Android.bp1
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java8
-rw-r--r--packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java5
-rw-r--r--packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/ServiceListener.java151
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java8
-rw-r--r--packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java22
-rw-r--r--packages/SettingsLib/HelpUtils/res/values-en-rAU/strings.xml2
-rw-r--r--packages/SettingsLib/HelpUtils/res/values-en-rCA/strings.xml2
-rw-r--r--packages/SettingsLib/HelpUtils/res/values-en-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml2
-rw-r--r--packages/SettingsLib/SearchWidget/res/values-in/strings.xml2
-rw-r--r--packages/SettingsLib/Utils/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java2
-rw-r--r--packages/SettingsLib/res/drawable/ic_media_display_device.xml16
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ja/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml14
-rw-r--r--packages/SettingsLib/res/values-ne/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java26
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java21
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java6
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java11
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java29
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java9
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java14
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java43
-rw-r--r--packages/Shell/AndroidManifest.xml11
-rw-r--r--packages/Shell/TEST_MAPPING31
-rw-r--r--packages/Shell/res/values-bn/strings.xml2
-rw-r--r--packages/SoundPicker/res/values-af/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-am/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ar/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-as/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-az/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-b+sr+Latn/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-be/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-bg/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-bn/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-bs/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ca/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-cs/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-da/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-de/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-el/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-en-rAU/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-en-rCA/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-en-rGB/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-en-rIN/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-en-rXC/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-es-rUS/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-es/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-et/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-eu/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-fa/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-fi/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-fr-rCA/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-fr/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-gl/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-gu/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-hi/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-hr/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-hu/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-hy/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-in/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-is/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-it/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-iw/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ja/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ka/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-kk/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-km/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-kn/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ko/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ky/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-lo/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-lt/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-lv/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-mk/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ml/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-mn/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-mr/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ms/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-my/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-nb/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ne/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-nl/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-or/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-pa/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-pl/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-pt-rBR/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-pt-rPT/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-pt/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ro/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ru/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-si/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-sk/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-sl/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-sq/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-sr/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-sv/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-sw/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ta/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-te/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-th/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-tl/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-tr/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-uk/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-ur/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-uz/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-vi/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-zh-rCN/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-zh-rHK/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-zh-rTW/strings.xml3
-rw-r--r--packages/SoundPicker/res/values-zu/strings.xml3
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java3
-rw-r--r--packages/SystemUI/Android.bp13
-rw-r--r--packages/SystemUI/AndroidManifest.xml12
-rw-r--r--packages/SystemUI/docs/dagger.md109
-rw-r--r--packages/SystemUI/proguard.flags4
-rw-r--r--packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml32
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml9
-rw-r--r--packages/SystemUI/res-keyguard/values-af/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-am/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ar/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-as/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-az/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-be/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-bg/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-bn/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-bs/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ca/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-cs/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-da/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-de/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-el/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rAU/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rCA/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rGB/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rIN/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rXC/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-es-rUS/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-es/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-et/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-eu/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-fa/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-fi/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-fr/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-gl/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-gu/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-hi/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-hr/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-hu/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-hy/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-in/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-is/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-it/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-iw/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ja/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ka/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-kk/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-km/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-kn/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ko/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ky/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-lo/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-lt/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-lv/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-mk/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ml/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-mn/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-mr/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ms/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-my/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-nb/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ne/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-nl/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-or/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-pa/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-pl/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-pt/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ro/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ru/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-si/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-sk/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-sl/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-sq/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-sr/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-sv/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-sw/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ta/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-te/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-th/strings.xml5
-rw-r--r--packages/SystemUI/res-keyguard/values-tl/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-tr/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-uk/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ur/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-uz/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-vi/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-zu/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values/strings.xml9
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml8
-rw-r--r--packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_0.xml (renamed from packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml)40
-rw-r--r--packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml187
-rw-r--r--packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml187
-rw-r--r--packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml187
-rw-r--r--packages/SystemUI/res/drawable/privacy_chip_bg.xml (renamed from packages/SystemUI/res/drawable/tv_pip_button_focused.xml)11
-rw-r--r--packages/SystemUI/res/layout/bubbles_manage_button_education.xml4
-rw-r--r--packages/SystemUI/res/layout/global_actions_power_item.xml2
-rw-r--r--packages/SystemUI/res/layout/media_carousel.xml1
-rw-r--r--packages/SystemUI/res/layout/media_view.xml43
-rw-r--r--packages/SystemUI/res/layout/ongoing_privacy_chip.xml40
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml2
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml1
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml29
-rw-r--r--packages/SystemUI/res/layout/tv_pip_menu.xml34
-rw-r--r--packages/SystemUI/res/layout/udfps_view.xml10
-rw-r--r--packages/SystemUI/res/values-af/strings.xml98
-rw-r--r--packages/SystemUI/res/values-af/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-am/strings.xml98
-rw-r--r--packages/SystemUI/res/values-am/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml104
-rw-r--r--packages/SystemUI/res/values-ar/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-as/strings.xml98
-rw-r--r--packages/SystemUI/res/values-as/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-az/strings.xml98
-rw-r--r--packages/SystemUI/res/values-az/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml98
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-be/strings.xml98
-rw-r--r--packages/SystemUI/res/values-be/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml98
-rw-r--r--packages/SystemUI/res/values-bg/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml100
-rw-r--r--packages/SystemUI/res/values-bn/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml104
-rw-r--r--packages/SystemUI/res/values-bs/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml102
-rw-r--r--packages/SystemUI/res/values-ca/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml98
-rw-r--r--packages/SystemUI/res/values-cs/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-da/strings.xml100
-rw-r--r--packages/SystemUI/res/values-da/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-de/strings.xml104
-rw-r--r--packages/SystemUI/res/values-de/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-el/strings.xml98
-rw-r--r--packages/SystemUI/res/values-el/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml98
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml98
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml98
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml98
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml98
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml104
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-es/strings.xml98
-rw-r--r--packages/SystemUI/res/values-es/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-et/strings.xml98
-rw-r--r--packages/SystemUI/res/values-et/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml98
-rw-r--r--packages/SystemUI/res/values-eu/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml128
-rw-r--r--packages/SystemUI/res/values-fa/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml98
-rw-r--r--packages/SystemUI/res/values-fi/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml98
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml110
-rw-r--r--packages/SystemUI/res/values-fr/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml98
-rw-r--r--packages/SystemUI/res/values-gl/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml104
-rw-r--r--packages/SystemUI/res/values-gu/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml102
-rw-r--r--packages/SystemUI/res/values-hi/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml98
-rw-r--r--packages/SystemUI/res/values-hr/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml98
-rw-r--r--packages/SystemUI/res/values-hu/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml98
-rw-r--r--packages/SystemUI/res/values-hy/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-in/strings.xml98
-rw-r--r--packages/SystemUI/res/values-in/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-is/strings.xml98
-rw-r--r--packages/SystemUI/res/values-is/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-it/strings.xml98
-rw-r--r--packages/SystemUI/res/values-it/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml98
-rw-r--r--packages/SystemUI/res/values-iw/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml106
-rw-r--r--packages/SystemUI/res/values-ja/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml98
-rw-r--r--packages/SystemUI/res/values-ka/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml104
-rw-r--r--packages/SystemUI/res/values-kk/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-km/strings.xml98
-rw-r--r--packages/SystemUI/res/values-km/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml98
-rw-r--r--packages/SystemUI/res/values-kn/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml100
-rw-r--r--packages/SystemUI/res/values-ko/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml98
-rw-r--r--packages/SystemUI/res/values-ky/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml98
-rw-r--r--packages/SystemUI/res/values-lo/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml98
-rw-r--r--packages/SystemUI/res/values-lt/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml102
-rw-r--r--packages/SystemUI/res/values-lv/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml98
-rw-r--r--packages/SystemUI/res/values-mk/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml98
-rw-r--r--packages/SystemUI/res/values-ml/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml98
-rw-r--r--packages/SystemUI/res/values-mn/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml98
-rw-r--r--packages/SystemUI/res/values-mr/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml98
-rw-r--r--packages/SystemUI/res/values-ms/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-my/strings.xml98
-rw-r--r--packages/SystemUI/res/values-my/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml98
-rw-r--r--packages/SystemUI/res/values-nb/strings_tv.xml6
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml104
-rw-r--r--packages/SystemUI/res/values-ne/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml104
-rw-r--r--packages/SystemUI/res/values-nl/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-or/strings.xml98
-rw-r--r--packages/SystemUI/res/values-or/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml98
-rw-r--r--packages/SystemUI/res/values-pa/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml100
-rw-r--r--packages/SystemUI/res/values-pl/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml100
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml106
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml100
-rw-r--r--packages/SystemUI/res/values-pt/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml98
-rw-r--r--packages/SystemUI/res/values-ro/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml114
-rw-r--r--packages/SystemUI/res/values-ru/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-si/strings.xml98
-rw-r--r--packages/SystemUI/res/values-si/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml98
-rw-r--r--packages/SystemUI/res/values-sk/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml98
-rw-r--r--packages/SystemUI/res/values-sl/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml98
-rw-r--r--packages/SystemUI/res/values-sq/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml98
-rw-r--r--packages/SystemUI/res/values-sr/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml98
-rw-r--r--packages/SystemUI/res/values-sv/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml100
-rw-r--r--packages/SystemUI/res/values-sw/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml98
-rw-r--r--packages/SystemUI/res/values-ta/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-te/strings.xml102
-rw-r--r--packages/SystemUI/res/values-te/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-th/strings.xml100
-rw-r--r--packages/SystemUI/res/values-th/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml98
-rw-r--r--packages/SystemUI/res/values-tl/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml98
-rw-r--r--packages/SystemUI/res/values-tr/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-tvdpi/dimens.xml4
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml98
-rw-r--r--packages/SystemUI/res/values-uk/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml98
-rw-r--r--packages/SystemUI/res/values-ur/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml100
-rw-r--r--packages/SystemUI/res/values-uz/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml100
-rw-r--r--packages/SystemUI/res/values-vi/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml100
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml102
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml122
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml98
-rw-r--r--packages/SystemUI/res/values-zu/strings_tv.xml4
-rw-r--r--packages/SystemUI/res/values/arrays_tv.xml17
-rw-r--r--packages/SystemUI/res/values/attrs.xml12
-rw-r--r--packages/SystemUI/res/values/config.xml14
-rw-r--r--packages/SystemUI/res/values/config_tv.xml13
-rw-r--r--packages/SystemUI/res/values/dimens.xml73
-rw-r--r--packages/SystemUI/res/values/ids.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml88
-rw-r--r--packages/SystemUI/res/values/strings_tv.xml15
-rw-r--r--packages/SystemUI/res/values/styles.xml25
-rw-r--r--packages/SystemUI/res/xml/media_collapsed.xml58
-rw-r--r--packages/SystemUI/res/xml/media_expanded.xml31
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java34
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java136
-rw-r--r--packages/SystemUI/src/com/android/systemui/BatteryMeterView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java188
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java154
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java148
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java185
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt163
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java297
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/NightDisplayListenerModule.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java157
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozePauser.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeService.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java (renamed from libs/hwui/GlFunctorLifecycleListener.h)24
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java99
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogLevel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt116
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaData.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt155
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt131
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHost.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaScrollView.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt187
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java226
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java285
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java200
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuIconsAlgorithm.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java93
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt296
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSHost.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java114
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java595
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java91
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Assert.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt170
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java (renamed from core/java/android/app/IRequestFinishCallback.aidl)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java412
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java67
-rw-r--r--packages/SystemUI/tests/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java131
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java70
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt216
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java169
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java118
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt290
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java153
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java76
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java116
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java85
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java130
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java11
-rw-r--r--packages/Tethering/common/TetheringLib/api/module-lib-current.txt88
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java3
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java1
-rw-r--r--packages/Tethering/jarjar-rules.txt2
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java10
-rw-r--r--packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java25
-rw-r--r--packages/Tethering/tests/privileged/Android.bp30
-rw-r--r--packages/Tethering/tests/privileged/AndroidManifest.xml32
-rw-r--r--packages/Tethering/tests/unit/jarjar-rules.txt2
-rw-r--r--packages/WAPPushManager/AndroidManifest.xml2
-rw-r--r--packages/overlays/IconPackKaiAndroidOverlay/res/drawable/ic_screenshot.xml20
-rw-r--r--packages/overlays/IconPackKaiLauncherOverlay/res/drawable/ic_screenshot.xml30
-rw-r--r--packages/overlays/IconPackKaiLauncherOverlay/res/drawable/ic_select.xml15
-rw-r--r--packages/overlays/IconPackKaiSystemUIOverlay/res/drawable/ic_screenshot.xml30
-rw-r--r--packages/overlays/IconPackSamAndroidOverlay/res/drawable/ic_screenshot.xml20
-rw-r--r--packages/overlays/IconPackSamLauncherOverlay/res/drawable/ic_screenshot.xml30
-rw-r--r--packages/overlays/IconPackSamLauncherOverlay/res/drawable/ic_select.xml15
-rw-r--r--packages/overlays/IconPackSamSystemUIOverlay/res/drawable/ic_screenshot.xml30
-rw-r--r--packages/overlays/IconPackVictorAndroidOverlay/res/drawable/ic_screenshot.xml20
-rw-r--r--packages/overlays/IconPackVictorLauncherOverlay/res/drawable/ic_screenshot.xml30
-rw-r--r--packages/overlays/IconPackVictorLauncherOverlay/res/drawable/ic_select.xml15
-rw-r--r--packages/overlays/IconPackVictorSystemUIOverlay/res/drawable/ic_screenshot.xml30
-rw-r--r--packages/services/PacProcessor/AndroidManifest.xml4
-rw-r--r--packages/services/PacProcessor/src/com/android/net/IProxyService.aidl3
-rw-r--r--packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java10
-rw-r--r--rs/jni/Android.mk2
-rw-r--r--services/Android.bp8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java36
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java132
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java262
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java26
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java117
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java (renamed from services/accessibility/java/com/android/server/accessibility/MagnificationController.java)69
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java (renamed from services/accessibility/java/com/android/server/accessibility/FullScreenMagnificationGestureHandler.java)62
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java73
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java29
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java12
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java35
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java27
-rw-r--r--services/backup/OWNERS13
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java16
-rw-r--r--services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java16
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java112
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java16
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java18
-rw-r--r--services/backup/java/com/android/server/backup/internal/BackupHandler.java16
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java32
-rw-r--r--services/backup/java/com/android/server/backup/params/AdbBackupParams.java7
-rw-r--r--services/backup/java/com/android/server/backup/params/BackupParams.java5
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java10
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java (renamed from services/backup/java/com/android/server/backup/utils/AppBackupUtils.java)93
-rw-r--r--services/backup/java/com/android/server/backup/utils/RestoreUtils.java4
-rw-r--r--services/backup/java/com/android/server/backup/utils/TarBackupReader.java5
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java20
-rw-r--r--services/core/java/com/android/server/DropBoxManagerService.java22
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java105
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java25
-rw-r--r--services/core/java/com/android/server/RuntimeService.java15
-rw-r--r--services/core/java/com/android/server/ServiceWatcher.java27
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java149
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java28
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java33
-rw-r--r--services/core/java/com/android/server/VibratorService.java154
-rw-r--r--services/core/java/com/android/server/Watchdog.java157
-rw-r--r--services/core/java/com/android/server/adb/AdbDebuggingManager.java3
-rw-r--r--services/core/java/com/android/server/adb/AdbService.java9
-rw-r--r--services/core/java/com/android/server/adb/AdbShellCommand.java68
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java24
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java104
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java301
-rw-r--r--services/core/java/com/android/server/am/AnrHelper.java26
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java47
-rw-r--r--services/core/java/com/android/server/am/AppExitInfoTracker.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastConstants.java3
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java34
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java4
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java75
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java51
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java17
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java141
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java103
-rw-r--r--services/core/java/com/android/server/appop/TEST_MAPPING10
-rwxr-xr-xservices/core/java/com/android/server/audio/AudioService.java112
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java64
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java17
-rw-r--r--services/core/java/com/android/server/audio/RecordingActivityMonitor.java19
-rw-r--r--services/core/java/com/android/server/audio/SoundEffectsHelper.java14
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java25
-rw-r--r--services/core/java/com/android/server/biometrics/PreAuthInfo.java39
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java125
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java107
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java196
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationConsumer.java29
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java473
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java1022
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java166
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java31
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java108
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnumerateClient.java98
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnumerateConsumer.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java62
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java126
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java56
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/Interruptable.java46
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java123
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java28
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RemovalClient.java89
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java45
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/Face10.java657
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java72
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java111
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java53
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java80
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java68
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java58
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java57
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java74
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java50
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java922
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java91
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java94
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java664
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java95
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java123
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java108
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java53
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java71
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java58
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java60
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java52
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java799
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java125
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityDispatcher.java88
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java27
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java86
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java92
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java56
-rw-r--r--services/core/java/com/android/server/connectivity/PacManager.java13
-rw-r--r--services/core/java/com/android/server/connectivity/PermissionMonitor.java112
-rw-r--r--services/core/java/com/android/server/connectivity/ProxyTracker.java24
-rw-r--r--services/core/java/com/android/server/content/SyncAdapterStateFetcher.java11
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java5
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java20
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java2
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java16
-rw-r--r--services/core/java/com/android/server/input/InputShellCommand.java409
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java9
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java74
-rw-r--r--services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java11
-rw-r--r--services/core/java/com/android/server/location/ConcurrentLinkedEvictingDeque.java43
-rw-r--r--services/core/java/com/android/server/location/ContextHubClientManager.java23
-rw-r--r--services/core/java/com/android/server/location/ContextHubService.java25
-rw-r--r--services/core/java/com/android/server/location/ContextHubServiceTransaction.java30
-rw-r--r--services/core/java/com/android/server/location/ContextHubTransactionManager.java109
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java292
-rw-r--r--services/core/java/com/android/server/location/LocationProviderProxy.java35
-rw-r--r--services/core/java/com/android/server/location/MockProvider.java4
-rw-r--r--services/core/java/com/android/server/location/PassiveProvider.java12
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceManager.java87
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java76
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java9
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssManagerService.java12
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java8
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java8
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssStatusProvider.java11
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java9
-rw-r--r--services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java45
-rw-r--r--services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java136
-rw-r--r--services/core/java/com/android/server/location/listeners/ListenerRegistration.java71
-rw-r--r--services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java41
-rw-r--r--services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java55
-rw-r--r--services/core/java/com/android/server/location/util/AppOpsHelper.java84
-rw-r--r--services/core/java/com/android/server/location/util/Injector.java17
-rw-r--r--services/core/java/com/android/server/location/util/LocationAttributionHelper.java24
-rw-r--r--services/core/java/com/android/server/location/util/LocationPermissionsHelper.java107
-rw-r--r--services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java71
-rw-r--r--services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java67
-rw-r--r--services/core/java/com/android/server/location/util/SystemAppOpsHelper.java40
-rw-r--r--services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java65
-rw-r--r--services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java87
-rw-r--r--services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java85
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java78
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java2
-rw-r--r--services/core/java/com/android/server/media/AudioPlayerStateMonitor.java8
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteProvider.java47
-rw-r--r--services/core/java/com/android/server/media/MediaResourceMonitorService.java18
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java15
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java49
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java134
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java8
-rw-r--r--services/core/java/com/android/server/media/OWNERS1
-rw-r--r--services/core/java/com/android/server/net/IpConfigStore.java3
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java40
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java15
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java23
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java27
-rw-r--r--services/core/java/com/android/server/notification/ShortcutHelper.java13
-rw-r--r--services/core/java/com/android/server/os/TEST_MAPPING36
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java177
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java38
-rw-r--r--services/core/java/com/android/server/pm/OWNERS4
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelper.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelperImpl.java31
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java72
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java412
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java398
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java50
-rw-r--r--services/core/java/com/android/server/pm/Settings.java27
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java40
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING10
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java43
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java13
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java272
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java16
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java4
-rw-r--r--services/core/java/com/android/server/power/ShutdownCheckPoints.java399
-rw-r--r--services/core/java/com/android/server/power/ShutdownThread.java26
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverController.java8
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java145
-rw-r--r--services/core/java/com/android/server/rollback/AppDataRollbackHelper.java10
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java10
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java8
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/README.md19
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java10
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java174
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java123
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java113
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java887
-rw-r--r--services/core/java/com/android/server/storage/DeviceStorageMonitorService.java25
-rw-r--r--services/core/java/com/android/server/storage/StorageSessionController.java6
-rw-r--r--services/core/java/com/android/server/storage/StorageUserConnection.java12
-rw-r--r--services/core/java/com/android/server/textclassifier/IconsContentProvider.java66
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java5
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java13
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java7
-rw-r--r--services/core/java/com/android/server/timezone/RulesManagerService.java8
-rw-r--r--services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java2
-rw-r--r--services/core/java/com/android/server/timezonedetector/Dumpable.java41
-rw-r--r--services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java173
-rw-r--r--services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java3
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java35
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java64
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java130
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java62
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java26
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java31
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java2
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java11
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java21
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java201
-rw-r--r--services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java3367
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java95
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java15
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java96
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java24
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java145
-rw-r--r--services/core/java/com/android/server/wm/BarController.java17
-rw-r--r--services/core/java/com/android/server/wm/BlackFrame.java1
-rw-r--r--services/core/java/com/android/server/wm/CompatModePackages.java30
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/Dimmer.java1
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java177
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java3
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicy.java94
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java537
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java409
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java332
-rw-r--r--services/core/java/com/android/server/wm/DragResizeMode.java2
-rw-r--r--services/core/java/com/android/server/wm/DragState.java4
-rw-r--r--services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java1
-rw-r--r--services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java17
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java39
-rw-r--r--services/core/java/com/android/server/wm/InputConsumerImpl.java7
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java74
-rw-r--r--services/core/java/com/android/server/wm/InsetsControlTarget.java9
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java57
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java29
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java19
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java57
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsController.java6
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java32
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java12
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java49
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java24
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java4
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java22
-rw-r--r--services/core/java/com/android/server/wm/ResetTargetTaskHelper.java8
-rw-r--r--services/core/java/com/android/server/wm/RootDisplayArea.java76
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java718
-rw-r--r--services/core/java/com/android/server/wm/RunningTasks.java4
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java3
-rw-r--r--services/core/java/com/android/server/wm/ShellRoot.java5
-rw-r--r--services/core/java/com/android/server/wm/StrictModeFlash.java1
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java3
-rw-r--r--services/core/java/com/android/server/wm/SurfaceFreezer.java1
-rw-r--r--services/core/java/com/android/server/wm/Task.java3352
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java505
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java18
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioningController.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java1
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java19
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java6
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java10
-rw-r--r--services/core/java/com/android/server/wm/Watermark.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java190
-rw-r--r--services/core/java/com/android/server/wm/WindowContainerThumbnail.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowFrames.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java121
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java76
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java138
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java17
-rw-r--r--services/core/jni/Android.bp19
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp144
-rw-r--r--services/core/jni/com_android_server_power_PowerManagerService.cpp4
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java61
-rw-r--r--services/incremental/IncrementalService.cpp15
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/robotests/Android.bp1
-rw-r--r--services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java23
-rw-r--r--services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java16
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java68
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowBackupEligibilityRules.java (renamed from services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java)16
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowCloseGuard.java5
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java4
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java4
-rw-r--r--services/tests/PackageManagerServiceTests/OWNERS3
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt17
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt15
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt31
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java61
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java71
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java140
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java30
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java101
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java95
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java42
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java56
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java49
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java42
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java56
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java94
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java170
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java38
-rw-r--r--services/tests/servicestests/Android.bp1
-rw-r--r--services/tests/servicestests/AndroidManifest.xml3
-rw-r--r--services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorServiceTest.java607
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java79
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java)236
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/FullScreenMagnificationGestureHandlerTest.java)23
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java67
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java (renamed from services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java)240
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java84
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java144
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt25
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt50
-rw-r--r--services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java356
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java101
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java152
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java81
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java214
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java194
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java4
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java90
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java58
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java111
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java251
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java76
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java396
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java206
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java312
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java253
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java109
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java58
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java136
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java51
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java57
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java134
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java31
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java219
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java35
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java3
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java3
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java105
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java13
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl5
-rw-r--r--telephony/api/system-current.txt1
-rw-r--r--telephony/api/system-removed.txt5
-rw-r--r--telephony/common/android/telephony/LocationAccessPolicy.java7
-rw-r--r--telephony/common/com/android/internal/telephony/TelephonyPermissions.java4
-rw-r--r--telephony/java/android/service/euicc/EuiccProfileInfo.java5
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java51
-rw-r--r--telephony/java/android/telephony/CellLocation.java31
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthNr.java14
-rw-r--r--telephony/java/android/telephony/DataSpecificRegistrationInfo.java48
-rw-r--r--telephony/java/android/telephony/NetworkRegistrationInfo.java47
-rw-r--r--telephony/java/android/telephony/PreciseDataConnectionState.java221
-rw-r--r--telephony/java/android/telephony/ServiceState.java25
-rw-r--r--telephony/java/android/telephony/SmsCbEtwsInfo.java32
-rw-r--r--telephony/java/android/telephony/SmsManager.java4
-rw-r--r--telephony/java/android/telephony/SmsMessage.java22
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java153
-rw-r--r--telephony/java/android/telephony/TelephonyScanManager.java84
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java19
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl23
-rw-r--r--telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java22
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyIntents.java4
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java49
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java152
-rw-r--r--test-base/Android.bp1
-rw-r--r--test-base/src/android/test/InstrumentationTestCase.java4
-rw-r--r--test-mock/Android.bp1
-rw-r--r--test-mock/src/android/test/mock/MockContentProvider.java3
-rw-r--r--test-runner/Android.bp1
-rw-r--r--test-runner/api/current.txt1
-rw-r--r--test-runner/src/android/test/InstrumentationTestRunner.java1
-rw-r--r--tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java16
-rw-r--r--tests/DynamicCodeLoggerIntegrationTests/Android.mk3
-rw-r--r--tests/FlickerTests/Android.bp3
-rw-r--r--tests/FlickerTests/AndroidManifest.xml2
-rw-r--r--tests/FlickerTests/AndroidTest.xml4
-rw-r--r--tests/FlickerTests/README.md190
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java174
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java76
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java75
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java79
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java98
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java399
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt446
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java193
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt190
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java146
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt137
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java80
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt68
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java84
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java86
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java84
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java73
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/PipTestBase.java75
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java66
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java64
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java200
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt126
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java175
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java115
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java)18
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java51
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt40
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java47
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt36
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt67
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt67
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt86
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt104
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt78
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt87
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt87
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt81
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt63
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt60
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt85
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt144
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt78
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt198
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt111
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml4
-rw-r--r--tests/Internal/src/android/app/WallpaperColorsTest.java2
-rw-r--r--tests/RollbackTest/Android.bp12
-rw-r--r--tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java37
-rw-r--r--tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java4
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java33
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java5
-rw-r--r--tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java30
-rw-r--r--tests/StagedInstallTest/Android.bp11
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java92
-rw-r--r--tests/WindowInsetsTests/AndroidManifest.xml24
-rw-r--r--tests/WindowInsetsTests/res/layout/chat_activity.xml (renamed from tests/WindowInsetsTests/res/layout/window_inset_activity.xml)0
-rw-r--r--tests/WindowInsetsTests/res/layout/controller_activity.xml106
-rw-r--r--tests/WindowInsetsTests/res/layout/main_activity.xml37
-rw-r--r--tests/WindowInsetsTests/res/values/strings.xml11
-rw-r--r--tests/WindowInsetsTests/res/values/styles.xml8
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java (renamed from tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java)9
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java202
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java36
-rw-r--r--tests/benchmarks/internal/Android.bp26
-rw-r--r--tests/benchmarks/internal/AndroidManifest.xml (renamed from packages/SystemUI/res/drawable/floating_dismiss_gradient.xml)22
-rw-r--r--tests/benchmarks/internal/AndroidTest.xml (renamed from packages/SystemUI/res/layout/pip_menu_action.xml)22
-rw-r--r--tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java454
-rw-r--r--tests/net/java/android/net/NetworkTemplateTest.kt7
-rw-r--r--tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java49
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java55
-rw-r--r--tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java3
-rw-r--r--tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java62
-rw-r--r--tools/aapt2/cmd/Compile.cpp20
-rw-r--r--tools/aapt2/cmd/Compile.h4
-rw-r--r--tools/aapt2/cmd/Compile_test.cpp87
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp25
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp1
-rw-r--r--tools/processors/intdef_mappings/Android.bp33
-rw-r--r--tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt190
-rw-r--r--tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt162
-rw-r--r--tools/stats_log_api_gen/Collation.cpp53
-rw-r--r--tools/stats_log_api_gen/Collation.h10
-rw-r--r--tools/stats_log_api_gen/java_writer.cpp326
-rw-r--r--tools/stats_log_api_gen/test.proto41
-rw-r--r--tools/stats_log_api_gen/test_collation.cpp64
-rw-r--r--tools/validatekeymaps/Android.bp1
-rw-r--r--wifi/TEST_MAPPING12
-rw-r--r--wifi/api/system-current.txt1
-rw-r--r--wifi/java/android/net/wifi/SoftApCapability.java107
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java12
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java32
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java20
2293 files changed, 68183 insertions, 46725 deletions
diff --git a/Android.bp b/Android.bp
index aae78a02e32e..e19fe640d32b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -568,6 +568,7 @@ java_library {
"libcore-platform-compat-config",
"services-platform-compat-config",
"documents-ui-compat-config",
+ "calendar-provider-compat-config",
],
libs: ["framework-updatable-stubs-module_libs_api"],
static_libs: [
@@ -626,6 +627,9 @@ java_library {
// Additional dependencies needed to build the ike API classes.
"ike-internals",
],
+ plugins: [
+ "intdef-annotation-processor",
+ ],
libs: ["icing-java-proto-lite"],
apex_available: ["//apex_available:platform"],
visibility: [
@@ -886,6 +890,7 @@ java_library {
exclude_srcs: [
"core/proto/android/privacy.proto",
"core/proto/android/section.proto",
+ "core/proto/android/typedef.proto",
],
sdk_version: "9",
srcs: [
@@ -911,6 +916,7 @@ java_library {
exclude_srcs: [
"core/proto/android/privacy.proto",
"core/proto/android/section.proto",
+ "core/proto/android/typedef.proto",
],
sdk_version: "core_current",
// Protos have lots of MissingOverride and similar.
diff --git a/Android.mk b/Android.mk
index d8532489a786..46529eb64657 100644
--- a/Android.mk
+++ b/Android.mk
@@ -32,10 +32,6 @@ ifneq ($(ANDROID_BUILD_EMBEDDED),true)
# ============================================================
include $(CLEAR_VARS)
-# This is used by ide.mk as the list of source files that are
-# always included.
-INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
-
# sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
# So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
# $(OUT_DOCS)/offline-sdk.
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 56c568832ea9..0fe34fb650eb 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -155,6 +155,9 @@ priv_apps = " " +
module_libs = " " +
" --show-annotation android.annotation.SystemApi\\(" +
"client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+ "\\)" +
+ " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
"\\) "
droidstubs {
@@ -232,16 +235,10 @@ droidstubs {
}
/////////////////////////////////////////////////////////////////////
-// Following droidstubs modules are for extra APIs for modules.
-// The framework currently have two more API surfaces for modules:
-// @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES)
+// Following droidstubs modules are for extra APIs for modules,
+// namely @SystemApi(client=MODULE_LIBRARIES) APIs.
/////////////////////////////////////////////////////////////////////
-// TODO(b/146727827) remove the *-api module when we can teach metalava
-// about the relationship among the API surfaces. Currently, these modules are only to generate
-// the API signature files and ensure that the APIs evolve in a backwards compatible manner.
-// They however are NOT used for building the API stub.
-
droidstubs {
name: "module-lib-api",
defaults: ["metalava-full-api-stubs-default"],
@@ -278,7 +275,7 @@ droidstubs {
name: "module-lib-api-stubs-docs-non-updatable",
defaults: ["metalava-non-updatable-api-stubs-default"],
arg_files: ["core/res/AndroidManifest.xml"],
- args: metalava_framework_docs_args + module_libs,
+ args: metalava_framework_docs_args + priv_apps + module_libs,
check_api: {
current: {
api_file: "non-updatable-api/module-lib-current.txt",
@@ -291,17 +288,6 @@ droidstubs {
},
}
-// The following droidstub module generates source files for the API stub library for
-// modules. Note that it not only includes its own APIs but also other APIs that have
-// narrower scope (all @SystemApis, not just the ones with 'client=MODULE_LIBRARIES').
-
-droidstubs {
- name: "module-lib-api-stubs-docs",
- defaults: ["metalava-non-updatable-api-stubs-default"],
- arg_files: ["core/res/AndroidManifest.xml"],
- args: metalava_framework_docs_args + priv_apps + module_libs,
-}
-
/////////////////////////////////////////////////////////////////////
// android_*_stubs_current modules are the stubs libraries compiled
// from *-api-stubs-docs
@@ -325,6 +311,15 @@ java_defaults {
compile_dex: true,
}
+java_defaults {
+ name: "android_stubs_dists_default",
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android.jar",
+ },
+}
+
java_library_static {
name: "android_monolith_stubs_current",
srcs: [ ":api-stubs-docs" ],
@@ -360,7 +355,21 @@ java_library_static {
name: "android_system_monolith_stubs_current",
srcs: [ ":system-api-stubs-docs" ],
static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/system",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android_system.jar",
+ },
+ ],
}
java_library_static {
@@ -392,14 +401,34 @@ java_library_static {
name: "android_test_stubs_current",
srcs: [ ":test-api-stubs-docs" ],
static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/test",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android_test.jar",
+ },
+ ],
}
java_library_static {
name: "android_module_lib_stubs_current",
- srcs: [ ":module-lib-api-stubs-docs" ],
- defaults: ["android_defaults_stubs_current"],
+ srcs: [ ":module-lib-api-stubs-docs-non-updatable" ],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
libs: ["sdk_system_29_android"],
+ dist: {
+ dir: "apistubs/android/module-lib",
+ },
}
java_library_static {
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp
new file mode 100644
index 000000000000..66d7348008ab
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "ContentCapturePerfTests",
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.annotation_annotation",
+ "apct-perftests-utils",
+ "collector-device-lib-platform",
+ "compatibility-device-util-axt",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
diff --git a/apct-tests/perftests/contentcapture/AndroidManifest.xml b/apct-tests/perftests/contentcapture/AndroidManifest.xml
new file mode 100644
index 000000000000..ee5577f265fa
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.contentcapture">
+
+ <uses-sdk android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.view.contentcapture.CustomTestActivity"
+ android:exported="true">
+ </activity>
+
+ <service
+ android:name="android.view.contentcapture.MyContentCaptureService"
+ android:label="PERF ContentCaptureService"
+ android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.contentcapture.ContentCaptureService" />
+ </intent-filter>
+ </service>
+
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.perftests.contentcapture" />
+</manifest>
diff --git a/apct-tests/perftests/contentcapture/AndroidTest.xml b/apct-tests/perftests/contentcapture/AndroidTest.xml
new file mode 100644
index 000000000000..d2386bb1efea
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs ContentCapturePerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="ContentCapturePerfTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.contentcapture" />
+ </test>
+</configuration>
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
new file mode 100644
index 000000000000..ca1a11a25e62
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
new file mode 100644
index 000000000000..9bab32ca2264
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/username_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Username" />
+
+ <EditText
+ android:id="@+id/username"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/password_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Password" />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
new file mode 100644
index 000000000000..f02c96d74ac9
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_CUSTOM_VIEWS;
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_LAYOUT_ID;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import android.app.Application;
+import android.content.ContentCaptureOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.UserHandle;
+import android.perftests.utils.PerfStatusReporter;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher;
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+/**
+ * Base class for all content capture tests.
+ */
+@LargeTest
+public abstract class AbstractContentCapturePerfTestCase {
+
+ private static final String TAG = AbstractContentCapturePerfTestCase.class.getSimpleName();
+ private static final long GENERIC_TIMEOUT_MS = 10_000;
+
+ private static int sOriginalStayOnWhilePluggedIn;
+ private static Context sContext = getInstrumentation().getTargetContext();
+
+ protected ActivitiesWatcher mActivitiesWatcher;
+
+ private MyContentCaptureService.ServiceWatcher mServiceWatcher;
+
+ @Rule
+ public ActivityTestRule<CustomTestActivity> mActivityRule =
+ new ActivityTestRule<>(CustomTestActivity.class, false, false);
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Rule
+ public TestRule mServiceDisablerRule = (base, description) -> {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ base.evaluate();
+ } finally {
+ Log.v(TAG, "@mServiceDisablerRule: safelyDisableService()");
+ safelyDisableService();
+ }
+ }
+ };
+ };
+
+ private void safelyDisableService() {
+ try {
+ resetService();
+ MyContentCaptureService.resetStaticState();
+
+ if (mServiceWatcher != null) {
+ mServiceWatcher.waitOnDestroy();
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "error disabling service", t);
+ }
+ }
+
+ /**
+ * Sets the content capture service.
+ */
+ private static void setService(@NonNull String service) {
+ final int userId = getCurrentUserId();
+ Log.d(TAG, "Setting service for user " + userId + " to " + service);
+ // TODO(b/123540602): use @TestingAPI to get max duration constant
+ runShellCommand("cmd content_capture set temporary-service %d %s 119000", userId, service);
+ }
+
+ /**
+ * Resets the content capture service.
+ */
+ private static void resetService() {
+ final int userId = getCurrentUserId();
+ Log.d(TAG, "Resetting back user " + userId + " to default service");
+ runShellCommand("cmd content_capture set temporary-service %d", userId);
+ }
+
+ private static int getCurrentUserId() {
+ return UserHandle.myUserId();
+ }
+
+ @BeforeClass
+ public static void setStayAwake() {
+ Log.v(TAG, "@BeforeClass: setStayAwake()");
+ // Some test cases will restart the activity, and stay awake is necessary to ensure that
+ // the test will not screen off during the test.
+ // Keeping the activity screen on is not enough, screen off may occur between the activity
+ // finished and the next start
+ final int stayOnWhilePluggedIn = Settings.Global.getInt(sContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+ sOriginalStayOnWhilePluggedIn = -1;
+ if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) {
+ sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn;
+ // Keep the device awake during testing.
+ setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY);
+ }
+ }
+
+ @AfterClass
+ public static void resetStayAwake() {
+ Log.v(TAG, "@AfterClass: resetStayAwake()");
+ if (sOriginalStayOnWhilePluggedIn != -1) {
+ setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
+ }
+ }
+
+ private static void setStayOnWhilePluggedIn(int value) {
+ runShellCommand(String.format("settings put global %s %d",
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
+ }
+
+ @BeforeClass
+ public static void setAllowSelf() {
+ final ContentCaptureOptions options = new ContentCaptureOptions(null);
+ Log.v(TAG, "@BeforeClass: setAllowSelf(): options=" + options);
+ sContext.getApplicationContext().setContentCaptureOptions(options);
+ }
+
+ @AfterClass
+ public static void unsetAllowSelf() {
+ Log.v(TAG, "@AfterClass: unsetAllowSelf()");
+ clearOptions();
+ }
+
+ protected static void clearOptions() {
+ sContext.getApplicationContext().setContentCaptureOptions(null);
+ }
+
+ @BeforeClass
+ public static void disableDefaultService() {
+ Log.v(TAG, "@BeforeClass: disableDefaultService()");
+ setDefaultServiceEnabled(false);
+ }
+
+ @AfterClass
+ public static void enableDefaultService() {
+ Log.v(TAG, "@AfterClass: enableDefaultService()");
+ setDefaultServiceEnabled(true);
+ }
+
+ /**
+ * Enables / disables the default service.
+ */
+ private static void setDefaultServiceEnabled(boolean enabled) {
+ final int userId = getCurrentUserId();
+ Log.d(TAG, "setDefaultServiceEnabled(user=" + userId + ", enabled= " + enabled + ")");
+ runShellCommand("cmd content_capture set default-service-enabled %d %s", userId,
+ Boolean.toString(enabled));
+ }
+
+ @Before
+ public void prepareDevice() throws Exception {
+ Log.v(TAG, "@Before: prepareDevice()");
+
+ // Unlock screen.
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+ // Dismiss keyguard, in case it's set as "Swipe to unlock".
+ runShellCommand("wm dismiss-keyguard");
+
+ // Collapse notifications.
+ runShellCommand("cmd statusbar collapse");
+ }
+
+ @Before
+ public void registerLifecycleCallback() {
+ Log.v(TAG, "@Before: Registering lifecycle callback");
+ final Application app = (Application) sContext.getApplicationContext();
+ mActivitiesWatcher = new ActivitiesWatcher(GENERIC_TIMEOUT_MS);
+ app.registerActivityLifecycleCallbacks(mActivitiesWatcher);
+ }
+
+ @After
+ public void unregisterLifecycleCallback() {
+ Log.d(TAG, "@After: Unregistering lifecycle callback: " + mActivitiesWatcher);
+ if (mActivitiesWatcher != null) {
+ final Application app = (Application) sContext.getApplicationContext();
+ app.unregisterActivityLifecycleCallbacks(mActivitiesWatcher);
+ }
+ }
+
+ /**
+ * Sets {@link MyContentCaptureService} as the service for the current user and waits until
+ * its created, then add the perf test package into allow list.
+ */
+ public MyContentCaptureService enableService() throws InterruptedException {
+ if (mServiceWatcher != null) {
+ throw new IllegalStateException("There Can Be Only One!");
+ }
+
+ mServiceWatcher = MyContentCaptureService.setServiceWatcher();
+ setService(MyContentCaptureService.SERVICE_NAME);
+ mServiceWatcher.setAllowSelf();
+ return mServiceWatcher.waitOnCreate();
+ }
+
+ @NonNull
+ protected ActivityWatcher startWatcher() {
+ return mActivitiesWatcher.watch(CustomTestActivity.class);
+ }
+
+ /**
+ * Launch test activity with default login layout
+ */
+ protected CustomTestActivity launchActivity() {
+ return launchActivity(R.layout.test_login_activity, 0);
+ }
+
+ /**
+ * Launch test activity with give layout and parameter
+ */
+ protected CustomTestActivity launchActivity(int layoutId, int numViews) {
+ final Intent intent = new Intent(sContext, CustomTestActivity.class);
+ intent.putExtra(INTENT_EXTRA_LAYOUT_ID, layoutId);
+ intent.putExtra(INTENT_EXTRA_CUSTOM_VIEWS, numViews);
+ return mActivityRule.launchActivity(intent);
+ }
+
+ protected void finishActivity() {
+ try {
+ mActivityRule.finishActivity();
+ } catch (IllegalStateException e) {
+ // no op
+ }
+ }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
new file mode 100644
index 000000000000..e509837f441a
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.perftests.contentcapture.R;
+
+/**
+ * A simple activity used for testing, e.g. performance of activity switching, or as a base
+ * container of testing view.
+ */
+public class CustomTestActivity extends Activity {
+ public static final String INTENT_EXTRA_LAYOUT_ID = "layout_id";
+ public static final String INTENT_EXTRA_CUSTOM_VIEWS = "custom_view_number";
+ public static final int MAX_VIEWS = 500;
+ private static final int CUSTOM_CONTAINER_LAYOUT_ID = R.layout.test_container_activity;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().hasExtra(INTENT_EXTRA_LAYOUT_ID)) {
+ final int layoutId = getIntent().getIntExtra(INTENT_EXTRA_LAYOUT_ID,
+ /* defaultValue= */0);
+ setContentView(layoutId);
+ if (layoutId == CUSTOM_CONTAINER_LAYOUT_ID) {
+ createCustomViews(findViewById(R.id.root_view),
+ getIntent().getIntExtra(INTENT_EXTRA_CUSTOM_VIEWS, MAX_VIEWS));
+ }
+ }
+ }
+
+ private void createCustomViews(LinearLayout root, int number) {
+ LinearLayout horizontalLayout = null;
+ for (int i = 0; i < number; i++) {
+ final int j = i % 8;
+ if (horizontalLayout != null && j == 0) {
+ root.addView(horizontalLayout);
+ horizontalLayout = null;
+ }
+ if (horizontalLayout == null) {
+ horizontalLayout = createHorizontalLayout();
+ }
+ horizontalLayout.addView(createItem(null, i));
+ }
+ if (horizontalLayout != null) {
+ root.addView(horizontalLayout);
+ }
+ }
+
+ private LinearLayout createHorizontalLayout() {
+ final LinearLayout layout = new LinearLayout(getApplicationContext());
+ layout.setOrientation(LinearLayout.HORIZONTAL);
+ return layout;
+ }
+
+ private LinearLayout createItem(Drawable drawable, int index) {
+ final LinearLayout group = new LinearLayout(getApplicationContext());
+ group.setOrientation(LinearLayout.VERTICAL);
+ group.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT, /* weight= */ 1.0f));
+
+ final TextView text = new TextView(this);
+ text.setText("i = " + index);
+ group.addView(text);
+
+ return group;
+ }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
new file mode 100644
index 000000000000..750d3b407a12
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.CREATED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+
+import android.perftests.utils.BenchmarkState;
+import android.view.View;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.Test;
+
+public class LoginTest extends AbstractContentCapturePerfTestCase {
+
+ @Test
+ public void testLaunchActivity() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_login_activity, 0);
+ }
+
+ @Test
+ public void testLaunchActivity_contain100Views() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_container_activity, 100);
+ }
+
+ @Test
+ public void testLaunchActivity_contain300Views() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_container_activity, 300);
+ }
+
+ @Test
+ public void testLaunchActivity_contain500Views() throws Throwable {
+ enableService();
+
+ testActivityLaunchTime(R.layout.test_container_activity, 500);
+ }
+
+ @Test
+ public void testLaunchActivity_noService() throws Throwable {
+ testActivityLaunchTime(R.layout.test_login_activity, 0);
+ }
+
+ @Test
+ public void testLaunchActivity_noService_contain100Views() throws Throwable {
+ testActivityLaunchTime(R.layout.test_container_activity, 100);
+ }
+
+ @Test
+ public void testLaunchActivity_noService_contain300Views() throws Throwable {
+ testActivityLaunchTime(R.layout.test_container_activity, 300);
+ }
+
+ @Test
+ public void testLaunchActivity_noService_contain500Views() throws Throwable {
+ testActivityLaunchTime(R.layout.test_container_activity, 500);
+ }
+
+ private void testActivityLaunchTime(int layoutId, int numViews) throws Throwable {
+ final ActivityWatcher watcher = startWatcher();
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ launchActivity(layoutId, numViews);
+
+ // Ignore the time to finish the activity
+ state.pauseTiming();
+ watcher.waitFor(CREATED);
+ finishActivity();
+ watcher.waitFor(DESTROYED);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged() throws Throwable {
+ enableService();
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged_noService() throws Throwable {
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged_noOptions() throws Throwable {
+ enableService();
+ clearOptions();
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ @Test
+ public void testOnVisibilityAggregated_visibleChanged_notImportant() throws Throwable {
+ enableService();
+ final CustomTestActivity activity = launchActivity();
+ final View root = activity.getWindow().getDecorView();
+ final View username = root.findViewById(R.id.username);
+ username.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_NO);
+
+ testOnVisibilityAggregated(username);
+ }
+
+ private void testOnVisibilityAggregated(View view) throws Throwable {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ // Only count the time of onVisibilityAggregated()
+ state.pauseTiming();
+ mActivityRule.runOnUiThread(() -> {
+ state.resumeTiming();
+ view.onVisibilityAggregated(false);
+ state.pauseTiming();
+ });
+ mActivityRule.runOnUiThread(() -> {
+ state.resumeTiming();
+ view.onVisibilityAggregated(true);
+ state.pauseTiming();
+ });
+ state.resumeTiming();
+ }
+ }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
new file mode 100644
index 000000000000..b1dbb28c9501
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.content.ComponentName;
+import android.service.contentcapture.ActivityEvent;
+import android.service.contentcapture.ContentCaptureService;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class MyContentCaptureService extends ContentCaptureService {
+
+ private static final String TAG = MyContentCaptureService.class.getSimpleName();
+ private static final String MY_PACKAGE = "com.android.perftests.contentcapture";
+ public static final String SERVICE_NAME = MY_PACKAGE + "/"
+ + MyContentCaptureService.class.getName();
+
+ private static ServiceWatcher sServiceWatcher;
+
+ @NonNull
+ public static ServiceWatcher setServiceWatcher() {
+ if (sServiceWatcher != null) {
+ throw new IllegalStateException("There Can Be Only One!");
+ }
+ sServiceWatcher = new ServiceWatcher();
+ return sServiceWatcher;
+ }
+
+ public static void resetStaticState() {
+ sServiceWatcher = null;
+ }
+
+ public static void clearServiceWatcher() {
+ if (sServiceWatcher != null) {
+ if (sServiceWatcher.mReadyToClear) {
+ sServiceWatcher.mService = null;
+ sServiceWatcher = null;
+ } else {
+ sServiceWatcher.mReadyToClear = true;
+ }
+ }
+ }
+
+ @Override
+ public void onConnected() {
+ Log.i(TAG, "onConnected: sServiceWatcher=" + sServiceWatcher);
+
+ if (sServiceWatcher == null) {
+ Log.e(TAG, "onConnected() without a watcher");
+ return;
+ }
+
+ if (!sServiceWatcher.mReadyToClear && sServiceWatcher.mService != null) {
+ Log.e(TAG, "onConnected(): already created: " + sServiceWatcher);
+ return;
+ }
+
+ sServiceWatcher.mService = this;
+ sServiceWatcher.mCreated.countDown();
+ sServiceWatcher.mReadyToClear = false;
+ }
+
+ @Override
+ public void onDisconnected() {
+ Log.i(TAG, "onDisconnected: sServiceWatcher=" + sServiceWatcher);
+
+ if (sServiceWatcher == null) {
+ Log.e(TAG, "onDisconnected() without a watcher");
+ return;
+ }
+ if (sServiceWatcher.mService == null) {
+ Log.e(TAG, "onDisconnected(): no service on " + sServiceWatcher);
+ return;
+ }
+
+ sServiceWatcher.mDestroyed.countDown();
+ clearServiceWatcher();
+ }
+
+ @Override
+ public void onCreateContentCaptureSession(ContentCaptureContext context,
+ ContentCaptureSessionId sessionId) {
+ Log.i(TAG, "onCreateContentCaptureSession(ctx=" + context + ", session=" + sessionId);
+ }
+
+ @Override
+ public void onDestroyContentCaptureSession(ContentCaptureSessionId sessionId) {
+ Log.i(TAG, "onDestroyContentCaptureSession(session=" + sessionId + ")");
+ }
+
+ @Override
+ public void onContentCaptureEvent(ContentCaptureSessionId sessionId,
+ ContentCaptureEvent event) {
+ Log.i(TAG, "onContentCaptureEventsRequest(session=" + sessionId + "): " + event);
+ }
+
+ @Override
+ public void onActivityEvent(ActivityEvent event) {
+ Log.i(TAG, "onActivityEvent(): " + event);
+ }
+
+ public static final class ServiceWatcher {
+
+ private static final long GENERIC_TIMEOUT_MS = 10_000;
+ private final CountDownLatch mCreated = new CountDownLatch(1);
+ private final CountDownLatch mDestroyed = new CountDownLatch(1);
+ private boolean mReadyToClear = true;
+ private Pair<Set<String>, Set<ComponentName>> mAllowList;
+
+ private MyContentCaptureService mService;
+
+ @NonNull
+ public MyContentCaptureService waitOnCreate() throws InterruptedException {
+ await(mCreated, "not created");
+
+ if (mService == null) {
+ throw new IllegalStateException("not created");
+ }
+
+ if (mAllowList != null) {
+ Log.d(TAG, "Allow after created: " + mAllowList);
+ mService.setContentCaptureWhitelist(mAllowList.first, mAllowList.second);
+ }
+
+ return mService;
+ }
+
+ public void waitOnDestroy() throws InterruptedException {
+ await(mDestroyed, "not destroyed");
+ }
+
+ /**
+ * Allow just this package.
+ */
+ public void setAllowSelf() {
+ final ArraySet<String> pkgs = new ArraySet<>(1);
+ pkgs.add(MY_PACKAGE);
+ mAllowList = new Pair<>(pkgs, null);
+ }
+
+ @Override
+ public String toString() {
+ return "mService: " + mService + " created: " + (mCreated.getCount() == 0)
+ + " destroyed: " + (mDestroyed.getCount() == 0);
+ }
+
+ /**
+ * Awaits for a latch to be counted down.
+ */
+ private static void await(@NonNull CountDownLatch latch, @NonNull String fmt,
+ @Nullable Object... args)
+ throws InterruptedException {
+ final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (!called) {
+ throw new IllegalStateException(String.format(fmt, args)
+ + " in " + GENERIC_TIMEOUT_MS + "ms");
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index 984893a25605..03ab5b6f2c8c 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -12,6 +12,8 @@ android_test {
"androidx.appcompat_appcompat",
"androidx.test.rules",
"androidx.annotation_annotation",
+ "androidx.benchmark_benchmark-common",
+ "androidx.benchmark_benchmark-junit4",
"apct-perftests-overlay-apps",
"apct-perftests-resources-manager-apps",
"apct-perftests-utils",
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index e9f4747b4545..833ae63e1125 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -34,7 +34,7 @@
</application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.benchmark.junit4.AndroidBenchmarkRunner"
android:targetPackage="com.android.perftests.core"/>
</manifest>
diff --git a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
index 14282bfbd24e..860c134ce3e2 100644
--- a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
+++ b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
@@ -16,24 +16,20 @@
package android.view;
-import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
import android.text.TextUtils;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.PathParser;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,6 +37,10 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class CutoutSpecificationBenchmark {
+ private static final int DISPLAY_WIDTH = 1080;
+ private static final int DISPLAY_HEIGHT = 1920;
+ private static final float DISPLAY_DENSITY = 3.5f;
+
private static final String TAG = "CutoutSpecificationBenchmark";
private static final String BOTTOM_MARKER = "@bottom";
@@ -67,22 +67,7 @@ public class CutoutSpecificationBenchmark {
+ "Z\n"
+ "@dp";
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
- private Context mContext;
- private DisplayMetrics mDisplayMetrics;
-
- /**
- * Setup the necessary member field used by test methods.
- */
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- mDisplayMetrics = new DisplayMetrics();
- mContext.getDisplay().getRealMetrics(mDisplayMetrics);
- }
-
+ public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
final RectF rectF = new RectF();
@@ -168,19 +153,18 @@ public class CutoutSpecificationBenchmark {
@Test
public void parseByOldMethodForDoubleCutout() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, mDisplayMetrics.widthPixels,
- mDisplayMetrics.heightPixels, mDisplayMetrics.density);
+ oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_DENSITY);
}
}
@Test
public void parseByNewMethodForDoubleCutout() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- new CutoutSpecification.Parser(mDisplayMetrics.density,
- mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)
+ new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
.parse(DOUBLE_CUTOUT_SPEC);
}
}
@@ -209,10 +193,10 @@ public class CutoutSpecificationBenchmark {
+ "@right\n"
+ "@dp";
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- new CutoutSpecification.Parser(mDisplayMetrics.density,
- mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+ new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+ .parse(spec);
}
}
@@ -231,10 +215,10 @@ public class CutoutSpecificationBenchmark {
+ "Z\n"
+ "@dp";
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
- new CutoutSpecification.Parser(mDisplayMetrics.density,
- mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+ new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+ .parse(spec);
}
}
}
diff --git a/apct-tests/perftests/core/src/android/view/ViewPerfTest.java b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
index 72f02c488dbd..a2aeb313cbde 100644
--- a/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
@@ -19,11 +19,11 @@ package android.view;
import static junit.framework.Assert.assertTrue;
import android.content.Context;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
import android.widget.FrameLayout;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -37,7 +37,7 @@ import org.junit.Test;
@LargeTest
public class ViewPerfTest {
@Rule
- public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ public final BenchmarkRule mBenchmarkRule = new BenchmarkRule();
@Rule
public final ActivityTestRule<PerfTestActivity> mActivityRule =
@@ -52,7 +52,7 @@ public class ViewPerfTest {
@Test
public void testSimpleViewInflate() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
LayoutInflater inflater = LayoutInflater.from(mContext);
FrameLayout root = new FrameLayout(mContext);
while (state.keepRunning()) {
@@ -62,7 +62,7 @@ public class ViewPerfTest {
@Test
public void testTwelveKeyInflate() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
LayoutInflater inflater = LayoutInflater.from(mContext);
FrameLayout root = new FrameLayout(mContext);
while (state.keepRunning()) {
@@ -72,7 +72,7 @@ public class ViewPerfTest {
@Test
public void testPerformHapticFeedback() throws Throwable {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
mActivityRule.runOnUiThread(() -> {
state.pauseTiming();
View view = new View(mContext);
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index b0edb117a00b..a69d3ffa46fa 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -21,14 +21,14 @@ import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
import android.view.View.MeasureSpec;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -50,7 +50,7 @@ public class ViewShowHidePerfTest {
new ActivityTestRule<>(PerfTestActivity.class);
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ public final BenchmarkRule mBenchmarkRule = new BenchmarkRule();
public Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -143,7 +143,7 @@ public class ViewShowHidePerfTest {
private void testParentWithChild(TestCallback callback) throws Throwable {
mActivityRule.runOnUiThread(() -> {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
FrameLayout parent = new FrameLayout(getContext());
mActivityRule.getActivity().setContentView(parent);
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
index f02cbcfc4daf..9e95a104af81 100644
--- a/apct-tests/perftests/windowmanager/Android.bp
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -19,6 +19,7 @@ android_test {
"androidx.test.rules",
"androidx.annotation_annotation",
"apct-perftests-utils",
+ "platform-test-annotations",
],
test_suites: ["device-tests"],
platform_apis: true,
diff --git a/apct-tests/perftests/windowmanager/AndroidTest.xml b/apct-tests/perftests/windowmanager/AndroidTest.xml
index 69d187f9419a..0a80cf96fba3 100644
--- a/apct-tests/perftests/windowmanager/AndroidTest.xml
+++ b/apct-tests/perftests/windowmanager/AndroidTest.xml
@@ -21,9 +21,17 @@
<option name="test-file-name" value="WmPerfTests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="cmd window dismiss-keyguard" />
+ <option name="run-command" value="cmd package compile -m speed com.android.perftests.wm" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.perftests.wm" />
<option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.wm.WmPerfRunListener" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/apct-tests/perftests/windowmanager/README.md b/apct-tests/perftests/windowmanager/README.md
new file mode 100644
index 000000000000..05fa6279a949
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/README.md
@@ -0,0 +1,27 @@
+## Window manager performance tests
+
+### Precondition
+To reduce the variance of the test, if `perf-setup.sh` (platform_testing/scripts/perf-setup)
+is available, it is better to use the following instructions to lock CPU and GPU frequencies.
+```
+m perf-setup.sh
+PERF_SETUP_PATH=/data/local/tmp/perf-setup.sh
+adb push $OUT/$PERF_SETUP_PATH $PERF_SETUP_PATH
+adb shell chmod +x $PERF_SETUP_PATH
+adb shell $PERF_SETUP_PATH
+```
+
+### Example to run
+Use `atest`
+```
+atest WmPerfTests:RelayoutPerfTest -- \
+ --module-arg WmPerfTests:instrumentation-arg:kill-bg:=true
+```
+Use `am instrument`
+```
+adb shell am instrument -w -r -e class android.wm.RelayoutPerfTest \
+ -e listener android.wm.WmPerfRunListener \
+ -e kill-bg true \
+ com.android.perftests.wm/androidx.test.runner.AndroidJUnitRunner
+```
+* `kill-bg` is optional.
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index f04e55567520..cff5663e9d9e 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -26,6 +26,7 @@ import android.os.RemoteException;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
+import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.IWindow;
@@ -52,6 +53,7 @@ import java.util.function.IntSupplier;
@RunWith(Parameterized.class)
@LargeTest
+@Presubmit
public class RelayoutPerfTest extends WindowManagerPerfTestBase {
private int mIteration;
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
index 655d2f7f8aa7..dc6245bf2c09 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
@@ -23,18 +23,15 @@ import android.app.KeyguardManager;
import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
-import android.os.BatteryManager;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.perftests.utils.PerfTestActivity;
-import android.provider.Settings;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
-import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -43,7 +40,9 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
public class WindowManagerPerfTestBase {
static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
@@ -56,21 +55,11 @@ public class WindowManagerPerfTestBase {
* is in /data because while enabling method profling of system server, it cannot write the
* trace to external storage.
*/
- static final File BASE_OUT_PATH = new File("/data/local/CorePerfTests");
-
- private static int sOriginalStayOnWhilePluggedIn;
+ static final File BASE_OUT_PATH = new File("/data/local/WmPerfTests");
@BeforeClass
public static void setUpOnce() {
final Context context = getInstrumentation().getContext();
- final int stayOnWhilePluggedIn = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
- sOriginalStayOnWhilePluggedIn = -1;
- if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) {
- sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn;
- // Keep the device awake during testing.
- setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY);
- }
if (!BASE_OUT_PATH.exists()) {
executeShellCommand("mkdir -p " + BASE_OUT_PATH);
@@ -84,18 +73,6 @@ public class WindowManagerPerfTestBase {
.addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
- @AfterClass
- public static void tearDownOnce() {
- if (sOriginalStayOnWhilePluggedIn != -1) {
- setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
- }
- }
-
- private static void setStayOnWhilePluggedIn(int value) {
- executeShellCommand(String.format("settings put global %s %d",
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
- }
-
/**
* Executes shell command with reading the output. It may also used to block until the current
* command is completed.
@@ -124,6 +101,42 @@ public class WindowManagerPerfTestBase {
executeShellCommand("am profile stop system");
}
+ static void runWithShellPermissionIdentity(Runnable runnable) {
+ sUiAutomation.adoptShellPermissionIdentity();
+ try {
+ runnable.run();
+ } finally {
+ sUiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ static class SettingsSession<T> implements AutoCloseable {
+ private final Consumer<T> mSetter;
+ private final T mOriginalValue;
+ private boolean mChanged;
+
+ SettingsSession(T originalValue, Consumer<T> setter) {
+ mOriginalValue = originalValue;
+ mSetter = setter;
+ }
+
+ void set(T value) {
+ if (Objects.equals(value, mOriginalValue)) {
+ mChanged = false;
+ return;
+ }
+ mSetter.accept(value);
+ mChanged = true;
+ }
+
+ @Override
+ public void close() {
+ if (mChanged) {
+ mSetter.accept(mOriginalValue);
+ }
+ }
+ }
+
/**
* Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
*/
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
new file mode 100644
index 000000000000..6eb85aacb4e8
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.wm.WindowManagerPerfTestBase.executeShellCommand;
+import static android.wm.WindowManagerPerfTestBase.runWithShellPermissionIdentity;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.view.WindowManagerPolicyConstants;
+import android.wm.WindowManagerPerfTestBase.SettingsSession;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.policy.PhoneWindow;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.RunListener;
+
+import java.util.List;
+
+/** Prepare the preconditions before running performance test. */
+public class WmPerfRunListener extends RunListener {
+
+ private static final String OPTION_KILL_BACKGROUND = "kill-bg";
+ private static final long KILL_BACKGROUND_WAIT_MS = 3000;
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ private long mWaitPreconditionDoneMs = 500;
+
+ private final SettingsSession<Integer> mStayOnWhilePluggedInSetting = new SettingsSession<>(
+ Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0),
+ value -> executeShellCommand(String.format("settings put global %s %d",
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value)));
+
+ private final SettingsSession<Integer> mNavigationModeSetting = new SettingsSession<>(
+ mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode),
+ value -> {
+ final String navOverlay;
+ switch (value) {
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
+ break;
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
+ break;
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL:
+ default:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
+ break;
+ }
+ executeShellCommand("cmd overlay enable-exclusive " + navOverlay);
+ });
+
+ /** It only executes once before all tests. */
+ @Override
+ public void testRunStarted(Description description) {
+ final Bundle arguments = InstrumentationRegistry.getArguments();
+
+ // Use gesture navigation for consistency.
+ mNavigationModeSetting.set(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+ // Keep the device awake during testing.
+ mStayOnWhilePluggedInSetting.set(BatteryManager.BATTERY_PLUGGED_ANY);
+
+ runWithShellPermissionIdentity(() -> {
+ final ActivityTaskManager atm = mContext.getSystemService(ActivityTaskManager.class);
+ atm.removeAllVisibleRecentTasks();
+ atm.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
+ ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED });
+ });
+ PhoneWindow.sendCloseSystemWindows(mContext, "WmPerfTests");
+
+ if (Boolean.parseBoolean(arguments.getString(OPTION_KILL_BACKGROUND))) {
+ runWithShellPermissionIdentity(this::killBackgroundProcesses);
+ mWaitPreconditionDoneMs = KILL_BACKGROUND_WAIT_MS;
+ }
+ // Wait a while for the precondition setup to complete.
+ SystemClock.sleep(mWaitPreconditionDoneMs);
+ }
+
+ private void killBackgroundProcesses() {
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final List<RunningAppProcessInfo> processes = am.getRunningAppProcesses();
+ if (processes == null) {
+ return;
+ }
+ for (RunningAppProcessInfo processInfo : processes) {
+ if (processInfo.importanceReasonCode == RunningAppProcessInfo.REASON_UNKNOWN
+ && processInfo.importance > RunningAppProcessInfo.IMPORTANCE_SERVICE) {
+ for (String pkg : processInfo.pkgList) {
+ am.forceStopPackage(pkg);
+ }
+ }
+ }
+ }
+
+ /** It only executes once after all tests. */
+ @Override
+ public void testRunFinished(Result result) {
+ mNavigationModeSetting.close();
+ mStayOnWhilePluggedInSetting.close();
+ }
+}
diff --git a/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java
new file mode 100644
index 000000000000..d018287986f2
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.test.filters;
+
+import android.wm.RelayoutPerfTest;
+
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+/**
+ * A static filter to have the same signature as the one in frameworks/base/tests/utils/testutils/.
+ * This doesn't share the existing library because it doesn't support parameterized test.
+ */
+public class FrameworksTestsFilter extends Filter {
+
+ private boolean mShouldRun;
+
+ @Override
+ public boolean shouldRun(Description description) {
+ final Class<?> testClass = description.getTestClass();
+ // Parameterized test methods don't have the original information. So keep the last status
+ // that matches the target class.
+ mShouldRun = (mShouldRun && testClass == null) || testClass == RelayoutPerfTest.class;
+ return mShouldRun;
+ }
+
+ @Override
+ public String describe() {
+ return "Default filter";
+ }
+}
diff --git a/apex/Android.bp b/apex/Android.bp
index c1b572a89eea..410e21141f86 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -53,6 +53,9 @@ priv_apps = " " +
module_libs = " " +
" --show-annotation android.annotation.SystemApi\\(" +
"client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+ "\\)" +
+ " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
"\\) "
mainline_service_stubs_args =
@@ -98,10 +101,8 @@ java_defaults {
annotations_enabled: true,
- // The stub libraries must be visible to frameworks/base so they can be combined
- // into API specific libraries.
stubs_library_visibility: [
- "//frameworks/base", // Framework
+ "//visibility:public",
],
// Set the visibility of the modules creating the stubs source.
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index ecc78ce7cf34..113f8fe9e248 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -51,6 +51,7 @@ public final class BlobHandle implements Parcelable {
};
private static final int LIMIT_BLOB_TAG_LENGTH = 128; // characters
+ private static final int LIMIT_BLOB_LABEL_LENGTH = 100; // characters
/**
* Cyrptographically secure hash algorithm used to generate hash of the blob this handle is
@@ -128,6 +129,9 @@ public final class BlobHandle implements Parcelable {
*
* @param digest the SHA-256 hash of the blob this is representing.
* @param label a label indicating what the blob is, that can be surfaced to the user.
+ * The length of the label cannot be more than 100 characters. It is recommended
+ * to keep this brief. This may be truncated and ellipsized if it is too long
+ * to be displayed to the user.
* @param expiryTimeMillis the time in secs after which the blob should be invalidated and not
* allowed to be accessed by any other app,
* in {@link System#currentTimeMillis()} timebase or {@code 0} to
@@ -205,9 +209,9 @@ public final class BlobHandle implements Parcelable {
final BlobHandle other = (BlobHandle) obj;
return this.algorithm.equals(other.algorithm)
&& Arrays.equals(this.digest, other.digest)
- && this.label.equals(other.label)
+ && this.label.toString().equals(other.label.toString())
&& this.expiryTimeMillis == other.expiryTimeMillis
- && this.tag.equals(tag);
+ && this.tag.equals(other.tag);
}
@Override
@@ -233,6 +237,7 @@ public final class BlobHandle implements Parcelable {
Preconditions.checkArgumentIsSupported(SUPPORTED_ALGOS, algorithm);
Preconditions.checkByteArrayNotEmpty(digest, "digest");
Preconditions.checkStringNotEmpty(label, "label must not be null");
+ Preconditions.checkArgument(label.length() <= LIMIT_BLOB_LABEL_LENGTH, "label too long");
Preconditions.checkArgumentNonnegative(expiryTimeMillis,
"expiryTimeMillis must not be negative");
Preconditions.checkStringNotEmpty(tag, "tag must not be null");
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 483d2cc2ec57..39f7526560a9 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -184,9 +184,8 @@ public class BlobStoreManager {
* @throws SecurityException when the caller is not allowed to create a session, such
* as when called from an Instant app.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
- * @throws IllegalStateException when a new session could not be created, such as when the
- * caller is trying to create too many sessions or when the
- * device is running low on space.
+ * @throws LimitExceededException when a new session could not be created, such as when the
+ * caller is trying to create too many sessions.
*/
public @IntRange(from = 1) long createSession(@NonNull BlobHandle blobHandle)
throws IOException {
@@ -194,6 +193,7 @@ public class BlobStoreManager {
return mService.createSession(blobHandle, mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
+ e.maybeRethrow(LimitExceededException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -302,8 +302,9 @@ public class BlobStoreManager {
* if the {@code leaseExpiryTimeMillis} is greater than the
* {@link BlobHandle#getExpiryTimeMillis()}.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -346,7 +347,9 @@ public class BlobStoreManager {
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
* acquire a lease for.
* @param description a short description string that can be surfaced
- * to the user explaining what the blob is used for.
+ * to the user explaining what the blob is used for. It is recommended to
+ * keep this description brief. This may be truncated and ellipsized
+ * if it is too long to be displayed to the user.
* @param leaseExpiryTimeMillis the time in milliseconds after which the lease can be
* automatically released, in {@link System#currentTimeMillis()}
* timebase. If its value is {@code 0}, then the behavior of this
@@ -362,8 +365,9 @@ public class BlobStoreManager {
* if the {@code leaseExpiryTimeMillis} is greater than the
* {@link BlobHandle#getExpiryTimeMillis()}.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -415,8 +419,9 @@ public class BlobStoreManager {
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -455,15 +460,18 @@ public class BlobStoreManager {
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
* acquire a lease for.
* @param description a short description string that can be surfaced
- * to the user explaining what the blob is used for.
+ * to the user explaining what the blob is used for. It is recommended to
+ * keep this description brief. This may be truncated and
+ * ellipsized if it is too long to be displayed to the user.
*
* @throws IOException when there is an I/O error while acquiring a lease to the blob.
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -757,6 +765,8 @@ public class BlobStoreManager {
* @throws SecurityException when the caller is not the owner of the session.
* @throws IllegalStateException when the caller tries to change access for a blob which is
* already committed.
+ * @throws LimitExceededException when the caller tries to explicitly allow too
+ * many packages using this API.
*/
public void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate)
throws IOException {
@@ -764,6 +774,7 @@ public class BlobStoreManager {
mSession.allowPackageAccess(packageName, certificate);
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
+ e.maybeRethrow(LimitExceededException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 4d29045fa631..9850b5d254d8 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -61,6 +61,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
@@ -154,10 +155,10 @@ class BlobMetadata {
}
}
- void removeInvalidCommitters(SparseArray<String> packages) {
+ void removeCommittersFromUnknownPkgs(SparseArray<String> knownPackages) {
synchronized (mMetadataLock) {
mCommitters.removeIf(committer ->
- !committer.packageName.equals(packages.get(committer.uid)));
+ !committer.packageName.equals(knownPackages.get(committer.uid)));
}
}
@@ -200,16 +201,27 @@ class BlobMetadata {
}
}
- void removeInvalidLeasees(SparseArray<String> packages) {
+ void removeLeaseesFromUnknownPkgs(SparseArray<String> knownPackages) {
synchronized (mMetadataLock) {
mLeasees.removeIf(leasee ->
- !leasee.packageName.equals(packages.get(leasee.uid)));
+ !leasee.packageName.equals(knownPackages.get(leasee.uid)));
}
}
- boolean hasLeases() {
+ void removeExpiredLeases() {
synchronized (mMetadataLock) {
- return !mLeasees.isEmpty();
+ mLeasees.removeIf(leasee -> !leasee.isStillValid());
+ }
+ }
+
+ boolean hasValidLeases() {
+ synchronized (mMetadataLock) {
+ for (int i = 0, size = mLeasees.size(); i < size; ++i) {
+ if (mLeasees.valueAt(i).isStillValid()) {
+ return true;
+ }
+ }
+ return false;
}
}
@@ -226,8 +238,7 @@ class BlobMetadata {
// Check if packageName already holds a lease on the blob.
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
final Leasee leasee = mLeasees.valueAt(i);
- if (leasee.equals(callingPackage, callingUid)
- && leasee.isStillValid()) {
+ if (leasee.isStillValid() && leasee.equals(callingPackage, callingUid)) {
return true;
}
}
@@ -259,25 +270,32 @@ class BlobMetadata {
boolean isALeasee(@Nullable String packageName, int uid) {
synchronized (mMetadataLock) {
- return isAnAccessor(mLeasees, packageName, uid);
+ final Leasee leasee = getAccessor(mLeasees, packageName, uid);
+ return leasee != null && leasee.isStillValid();
}
}
private static <T extends Accessor> boolean isAnAccessor(@NonNull ArraySet<T> accessors,
@Nullable String packageName, int uid) {
// Check if the package is an accessor of the data blob.
+ return getAccessor(accessors, packageName, uid) != null;
+ }
+
+ private static <T extends Accessor> T getAccessor(@NonNull ArraySet<T> accessors,
+ @Nullable String packageName, int uid) {
+ // Check if the package is an accessor of the data blob.
for (int i = 0, size = accessors.size(); i < size; ++i) {
final Accessor accessor = accessors.valueAt(i);
if (packageName != null && uid != INVALID_UID
&& accessor.equals(packageName, uid)) {
- return true;
+ return (T) accessor;
} else if (packageName != null && accessor.packageName.equals(packageName)) {
- return true;
+ return (T) accessor;
} else if (uid != INVALID_UID && accessor.uid == uid) {
- return true;
+ return (T) accessor;
}
}
- return false;
+ return null;
}
boolean isALeasee(@NonNull String packageName) {
@@ -298,11 +316,11 @@ class BlobMetadata {
private boolean hasOtherLeasees(@Nullable String packageName, int uid) {
synchronized (mMetadataLock) {
- if (mCommitters.size() > 1 || mLeasees.size() > 1) {
- return true;
- }
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
final Leasee leasee = mLeasees.valueAt(i);
+ if (!leasee.isStillValid()) {
+ continue;
+ }
// TODO: Also exclude packages which are signed with same cert?
if (packageName != null && uid != INVALID_UID
&& !leasee.equals(packageName, uid)) {
@@ -322,6 +340,9 @@ class BlobMetadata {
synchronized (mMetadataLock) {
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
final Leasee leasee = mLeasees.valueAt(i);
+ if (!leasee.isStillValid()) {
+ continue;
+ }
if (leasee.uid == uid && leasee.packageName.equals(packageName)) {
final int descriptionResId = leasee.descriptionResEntryName == null
? Resources.ID_NULL
@@ -398,6 +419,26 @@ class BlobMetadata {
return revocableFd.getRevocableFileDescriptor();
}
+ void destroy() {
+ revokeAllFds();
+ getBlobFile().delete();
+ }
+
+ private void revokeAllFds() {
+ synchronized (mRevocableFds) {
+ for (int i = 0, pkgCount = mRevocableFds.size(); i < pkgCount; ++i) {
+ final ArraySet<RevocableFileDescriptor> packageFds =
+ mRevocableFds.valueAt(i);
+ if (packageFds == null) {
+ continue;
+ }
+ for (int j = 0, fdCount = packageFds.size(); j < fdCount; ++j) {
+ packageFds.valueAt(j).revoke();
+ }
+ }
+ }
+ }
+
boolean shouldBeDeleted(boolean respectLeaseWaitTime) {
// Expired data blobs
if (getBlobHandle().isExpired()) {
@@ -406,7 +447,7 @@ class BlobMetadata {
// Blobs with no active leases
if ((!respectLeaseWaitTime || hasLeaseWaitTimeElapsedForAll())
- && !hasLeases()) {
+ && !hasValidLeases()) {
return true;
}
@@ -456,14 +497,8 @@ class BlobMetadata {
final byte[] leaseesBytes = proto.getBytes();
// Construct the StatsEvent to represent this Blob
- return StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(mBlobId)
- .writeLong(getSize())
- .writeLong(mBlobHandle.getExpiryTimeMillis())
- .writeByteArray(committersBytes)
- .writeByteArray(leaseesBytes)
- .build();
+ return FrameworkStatsLog.buildStatsEvent(atomTag, mBlobId, getSize(),
+ mBlobHandle.getExpiryTimeMillis(), committersBytes, leaseesBytes);
}
}
@@ -695,7 +730,7 @@ class BlobMetadata {
}
boolean isStillValid() {
- return expiryTimeMillis == 0 || expiryTimeMillis <= System.currentTimeMillis();
+ return expiryTimeMillis == 0 || expiryTimeMillis >= System.currentTimeMillis();
}
void dump(@NonNull Context context, @NonNull IndentingPrintWriter fout) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index 265479f7c533..bb9f13f1712c 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.os.Environment;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
+import android.text.TextUtils;
import android.util.DataUnit;
import android.util.Log;
import android.util.Slog;
@@ -141,6 +142,43 @@ class BlobStoreConfig {
public static long DELETE_ON_LAST_LEASE_DELAY_MS =
DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS;
+ /**
+ * Denotes the maximum number of active sessions per app at any time.
+ */
+ public static final String KEY_MAX_ACTIVE_SESSIONS = "max_active_sessions";
+ public static int DEFAULT_MAX_ACTIVE_SESSIONS = 250;
+ public static int MAX_ACTIVE_SESSIONS = DEFAULT_MAX_ACTIVE_SESSIONS;
+
+ /**
+ * Denotes the maximum number of committed blobs per app at any time.
+ */
+ public static final String KEY_MAX_COMMITTED_BLOBS = "max_committed_blobs";
+ public static int DEFAULT_MAX_COMMITTED_BLOBS = 1000;
+ public static int MAX_COMMITTED_BLOBS = DEFAULT_MAX_COMMITTED_BLOBS;
+
+ /**
+ * Denotes the maximum number of leased blobs per app at any time.
+ */
+ public static final String KEY_MAX_LEASED_BLOBS = "max_leased_blobs";
+ public static int DEFAULT_MAX_LEASED_BLOBS = 500;
+ public static int MAX_LEASED_BLOBS = DEFAULT_MAX_LEASED_BLOBS;
+
+ /**
+ * Denotes the maximum number of packages explicitly permitted to access a blob
+ * (permitted as part of creating a {@link BlobAccessMode}).
+ */
+ public static final String KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = "max_permitted_pks";
+ public static int DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = 300;
+ public static int MAX_BLOB_ACCESS_PERMITTED_PACKAGES =
+ DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
+
+ /**
+ * Denotes the maximum number of characters that a lease description can have.
+ */
+ public static final String KEY_LEASE_DESC_CHAR_LIMIT = "lease_desc_char_limit";
+ public static int DEFAULT_LEASE_DESC_CHAR_LIMIT = 300;
+ public static int LEASE_DESC_CHAR_LIMIT = DEFAULT_LEASE_DESC_CHAR_LIMIT;
+
static void refresh(Properties properties) {
if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
return;
@@ -178,6 +216,23 @@ class BlobStoreConfig {
DELETE_ON_LAST_LEASE_DELAY_MS = properties.getLong(key,
DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS);
break;
+ case KEY_MAX_ACTIVE_SESSIONS:
+ MAX_ACTIVE_SESSIONS = properties.getInt(key, DEFAULT_MAX_ACTIVE_SESSIONS);
+ break;
+ case KEY_MAX_COMMITTED_BLOBS:
+ MAX_COMMITTED_BLOBS = properties.getInt(key, DEFAULT_MAX_COMMITTED_BLOBS);
+ break;
+ case KEY_MAX_LEASED_BLOBS:
+ MAX_LEASED_BLOBS = properties.getInt(key, DEFAULT_MAX_LEASED_BLOBS);
+ break;
+ case KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES:
+ MAX_BLOB_ACCESS_PERMITTED_PACKAGES = properties.getInt(key,
+ DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES);
+ break;
+ case KEY_LEASE_DESC_CHAR_LIMIT:
+ LEASE_DESC_CHAR_LIMIT = properties.getInt(key,
+ DEFAULT_LEASE_DESC_CHAR_LIMIT);
+ break;
default:
Slog.wtf(TAG, "Unknown key in device config properties: " + key);
}
@@ -210,6 +265,17 @@ class BlobStoreConfig {
fout.println(String.format(dumpFormat, KEY_DELETE_ON_LAST_LEASE_DELAY_MS,
TimeUtils.formatDuration(DELETE_ON_LAST_LEASE_DELAY_MS),
TimeUtils.formatDuration(DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS)));
+ fout.println(String.format(dumpFormat, KEY_MAX_ACTIVE_SESSIONS,
+ MAX_ACTIVE_SESSIONS, DEFAULT_MAX_ACTIVE_SESSIONS));
+ fout.println(String.format(dumpFormat, KEY_MAX_COMMITTED_BLOBS,
+ MAX_COMMITTED_BLOBS, DEFAULT_MAX_COMMITTED_BLOBS));
+ fout.println(String.format(dumpFormat, KEY_MAX_LEASED_BLOBS,
+ MAX_LEASED_BLOBS, DEFAULT_MAX_LEASED_BLOBS));
+ fout.println(String.format(dumpFormat, KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
+ MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
+ DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES));
+ fout.println(String.format(dumpFormat, KEY_LEASE_DESC_CHAR_LIMIT,
+ LEASE_DESC_CHAR_LIMIT, DEFAULT_LEASE_DESC_CHAR_LIMIT));
}
}
@@ -288,6 +354,46 @@ class BlobStoreConfig {
return DeviceConfigProperties.DELETE_ON_LAST_LEASE_DELAY_MS;
}
+ /**
+ * Returns the maximum number of active sessions per app.
+ */
+ public static int getMaxActiveSessions() {
+ return DeviceConfigProperties.MAX_ACTIVE_SESSIONS;
+ }
+
+ /**
+ * Returns the maximum number of committed blobs per app.
+ */
+ public static int getMaxCommittedBlobs() {
+ return DeviceConfigProperties.MAX_COMMITTED_BLOBS;
+ }
+
+ /**
+ * Returns the maximum number of leased blobs per app.
+ */
+ public static int getMaxLeasedBlobs() {
+ return DeviceConfigProperties.MAX_LEASED_BLOBS;
+ }
+
+ /**
+ * Returns the maximum number of packages explicitly permitted to access a blob.
+ */
+ public static int getMaxPermittedPackages() {
+ return DeviceConfigProperties.MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
+ }
+
+ /**
+ * Returns the lease description truncated to
+ * {@link DeviceConfigProperties#LEASE_DESC_CHAR_LIMIT} characters.
+ */
+ public static CharSequence getTruncatedLeaseDescription(CharSequence description) {
+ if (TextUtils.isEmpty(description)) {
+ return description;
+ }
+ return TextUtils.trimToLengthWithEllipsis(description,
+ DeviceConfigProperties.LEASE_DESC_CHAR_LIMIT);
+ }
+
@Nullable
public static File prepareBlobFile(long sessionId) {
final File blobsDir = prepareBlobsDir();
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index a90536fee904..d37dfdeaa583 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -35,6 +35,9 @@ import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
import static com.android.server.blob.BlobStoreConfig.getAdjustedCommitTimeMs;
import static com.android.server.blob.BlobStoreConfig.getDeletionOnLastLeaseDelayMs;
+import static com.android.server.blob.BlobStoreConfig.getMaxActiveSessions;
+import static com.android.server.blob.BlobStoreConfig.getMaxCommittedBlobs;
+import static com.android.server.blob.BlobStoreConfig.getMaxLeasedBlobs;
import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
@@ -124,6 +127,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -332,9 +336,26 @@ public class BlobStoreManagerService extends SystemService {
mKnownBlobIds.add(id);
}
+ @GuardedBy("mBlobsLock")
+ private int getSessionsCountLocked(int uid, String packageName) {
+ // TODO: Maintain a counter instead of traversing all the sessions
+ final AtomicInteger sessionsCount = new AtomicInteger(0);
+ forEachSessionInUser(session -> {
+ if (session.getOwnerUid() == uid && session.getOwnerPackageName().equals(packageName)) {
+ sessionsCount.getAndIncrement();
+ }
+ }, UserHandle.getUserId(uid));
+ return sessionsCount.get();
+ }
+
private long createSessionInternal(BlobHandle blobHandle,
int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
+ final int sessionsCount = getSessionsCountLocked(callingUid, callingPackage);
+ if (sessionsCount >= getMaxActiveSessions()) {
+ throw new LimitExceededException("Too many active sessions for the caller: "
+ + sessionsCount);
+ }
// TODO: throw if there is already an active session associated with blobHandle.
final long sessionId = generateNextSessionIdLocked();
final BlobStoreSession session = new BlobStoreSession(mContext,
@@ -408,10 +429,42 @@ public class BlobStoreManagerService extends SystemService {
}
}
+ @GuardedBy("mBlobsLock")
+ private int getCommittedBlobsCountLocked(int uid, String packageName) {
+ // TODO: Maintain a counter instead of traversing all the blobs
+ final AtomicInteger blobsCount = new AtomicInteger(0);
+ forEachBlobInUser((blobMetadata) -> {
+ if (blobMetadata.isACommitter(packageName, uid)) {
+ blobsCount.getAndIncrement();
+ }
+ }, UserHandle.getUserId(uid));
+ return blobsCount.get();
+ }
+
+ @GuardedBy("mBlobsLock")
+ private int getLeasedBlobsCountLocked(int uid, String packageName) {
+ // TODO: Maintain a counter instead of traversing all the blobs
+ final AtomicInteger blobsCount = new AtomicInteger(0);
+ forEachBlobInUser((blobMetadata) -> {
+ if (blobMetadata.isALeasee(packageName, uid)) {
+ blobsCount.getAndIncrement();
+ }
+ }, UserHandle.getUserId(uid));
+ return blobsCount.get();
+ }
+
private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
CharSequence description, long leaseExpiryTimeMillis,
int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
+ final int leasesCount = getLeasedBlobsCountLocked(callingUid, callingPackage);
+ if (leasesCount >= getMaxLeasedBlobs()) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
+ INVALID_BLOB_ID, INVALID_BLOB_SIZE,
+ FrameworkStatsLog.BLOB_LEASED__RESULT__COUNT_LIMIT_EXCEEDED);
+ throw new LimitExceededException("Too many leased blobs for the caller: "
+ + leasesCount);
+ }
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
@@ -489,7 +542,7 @@ public class BlobStoreManagerService extends SystemService {
Slog.v(TAG, "Released lease on " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
- if (!blobMetadata.hasLeases()) {
+ if (!blobMetadata.hasValidLeases()) {
mHandler.postDelayed(() -> {
synchronized (mBlobsLock) {
// Check if blobMetadata object is still valid. If it is not, then
@@ -533,6 +586,9 @@ public class BlobStoreManagerService extends SystemService {
getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
final ArrayList<LeaseInfo> leaseInfos = new ArrayList<>();
blobMetadata.forEachLeasee(leasee -> {
+ if (!leasee.isStillValid()) {
+ return;
+ }
final int descriptionResId = leasee.descriptionResEntryName == null
? Resources.ID_NULL
: getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
@@ -556,7 +612,11 @@ public class BlobStoreManagerService extends SystemService {
UserHandle.getUserId(callingUid));
userBlobs.entrySet().removeIf(entry -> {
final BlobMetadata blobMetadata = entry.getValue();
- return blobMetadata.getBlobId() == blobId;
+ if (blobMetadata.getBlobId() == blobId) {
+ deleteBlobLocked(blobMetadata);
+ return true;
+ }
+ return false;
});
writeBlobsInfoAsync();
}
@@ -607,11 +667,10 @@ public class BlobStoreManagerService extends SystemService {
switch (session.getState()) {
case STATE_ABANDONED:
case STATE_VERIFIED_INVALID:
- session.getSessionFile().delete();
synchronized (mBlobsLock) {
+ deleteSessionLocked(session);
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
.remove(session.getSessionId());
- mActiveBlobIds.remove(session.getSessionId());
if (LOGV) {
Slog.v(TAG, "Session is invalid; deleted " + session);
}
@@ -626,6 +685,20 @@ public class BlobStoreManagerService extends SystemService {
break;
case STATE_VERIFIED_VALID:
synchronized (mBlobsLock) {
+ final int committedBlobsCount = getCommittedBlobsCountLocked(
+ session.getOwnerUid(), session.getOwnerPackageName());
+ if (committedBlobsCount >= getMaxCommittedBlobs()) {
+ Slog.d(TAG, "Failed to commit: too many committed blobs. count: "
+ + committedBlobsCount + "; blob: " + session);
+ session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
+ deleteSessionLocked(session);
+ getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
+ .remove(session.getSessionId());
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
+ session.getOwnerUid(), session.getSessionId(), session.getSize(),
+ FrameworkStatsLog.BLOB_COMMITTED__RESULT__COUNT_LIMIT_EXCEEDED);
+ break;
+ }
final int userId = UserHandle.getUserId(session.getOwnerUid());
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
userId);
@@ -656,9 +729,9 @@ public class BlobStoreManagerService extends SystemService {
} else {
blob.addOrReplaceCommitter(existingCommitter);
}
- Slog.d(TAG, "Error committing the blob", e);
+ Slog.d(TAG, "Error committing the blob: " + session, e);
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
- session.getOwnerUid(), blob.getBlobId(), blob.getSize(),
+ session.getOwnerUid(), session.getSessionId(), blob.getSize(),
FrameworkStatsLog.BLOB_COMMITTED__RESULT__ERROR_DURING_COMMIT);
session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
// If the commit fails and this blob data didn't exist before, delete it.
@@ -670,8 +743,7 @@ public class BlobStoreManagerService extends SystemService {
}
// Delete redundant data from recommits.
if (session.getSessionId() != blob.getBlobId()) {
- session.getSessionFile().delete();
- mActiveBlobIds.remove(session.getSessionId());
+ deleteSessionLocked(session);
}
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
.remove(session.getSessionId());
@@ -859,8 +931,8 @@ public class BlobStoreManagerService extends SystemService {
blobMetadata.getBlobFile().delete();
} else {
addBlobForUserLocked(blobMetadata, blobMetadata.getUserId());
- blobMetadata.removeInvalidCommitters(userPackages);
- blobMetadata.removeInvalidLeasees(userPackages);
+ blobMetadata.removeCommittersFromUnknownPkgs(userPackages);
+ blobMetadata.removeLeaseesFromUnknownPkgs(userPackages);
}
mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId());
}
@@ -957,8 +1029,7 @@ public class BlobStoreManagerService extends SystemService {
userSessions.removeIf((sessionId, blobStoreSession) -> {
if (blobStoreSession.getOwnerUid() == uid
&& blobStoreSession.getOwnerPackageName().equals(packageName)) {
- blobStoreSession.getSessionFile().delete();
- mActiveBlobIds.remove(blobStoreSession.getSessionId());
+ deleteSessionLocked(blobStoreSession);
return true;
}
return false;
@@ -999,8 +1070,7 @@ public class BlobStoreManagerService extends SystemService {
if (userSessions != null) {
for (int i = 0, count = userSessions.size(); i < count; ++i) {
final BlobStoreSession session = userSessions.valueAt(i);
- session.getSessionFile().delete();
- mActiveBlobIds.remove(session.getSessionId());
+ deleteSessionLocked(session);
}
}
@@ -1049,6 +1119,9 @@ public class BlobStoreManagerService extends SystemService {
userBlobs.entrySet().removeIf(entry -> {
final BlobMetadata blobMetadata = entry.getValue();
+ // Remove expired leases
+ blobMetadata.removeExpiredLeases();
+
if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
deleteBlobLocked(blobMetadata);
deletedBlobIds.add(blobMetadata.getBlobId());
@@ -1076,8 +1149,7 @@ public class BlobStoreManagerService extends SystemService {
}
if (shouldRemove) {
- blobStoreSession.getSessionFile().delete();
- mActiveBlobIds.remove(blobStoreSession.getSessionId());
+ deleteSessionLocked(blobStoreSession);
deletedBlobIds.add(blobStoreSession.getSessionId());
}
return shouldRemove;
@@ -1089,13 +1161,29 @@ public class BlobStoreManagerService extends SystemService {
}
@GuardedBy("mBlobsLock")
+ private void deleteSessionLocked(BlobStoreSession blobStoreSession) {
+ blobStoreSession.destroy();
+ mActiveBlobIds.remove(blobStoreSession.getSessionId());
+ }
+
+ @GuardedBy("mBlobsLock")
private void deleteBlobLocked(BlobMetadata blobMetadata) {
- blobMetadata.getBlobFile().delete();
+ blobMetadata.destroy();
mActiveBlobIds.remove(blobMetadata.getBlobId());
}
void runClearAllSessions(@UserIdInt int userId) {
synchronized (mBlobsLock) {
+ for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
+ final int sessionUserId = mSessions.keyAt(i);
+ if (userId != UserHandle.USER_ALL && userId != sessionUserId) {
+ continue;
+ }
+ final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
+ for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
+ mActiveBlobIds.remove(userSessions.valueAt(j).getSessionId());
+ }
+ }
if (userId == UserHandle.USER_ALL) {
mSessions.clear();
} else {
@@ -1107,6 +1195,16 @@ public class BlobStoreManagerService extends SystemService {
void runClearAllBlobs(@UserIdInt int userId) {
synchronized (mBlobsLock) {
+ for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+ final int blobUserId = mBlobsMap.keyAt(i);
+ if (userId != UserHandle.USER_ALL && userId != blobUserId) {
+ continue;
+ }
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
+ for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
+ mActiveBlobIds.remove(userBlobs.valueAt(j).getBlobId());
+ }
+ }
if (userId == UserHandle.USER_ALL) {
mBlobsMap.clear();
} else {
@@ -1331,8 +1429,11 @@ public class BlobStoreManagerService extends SystemService {
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
- // TODO: Verify caller request is within limits (no. of calls/blob sessions/blobs)
- return createSessionInternal(blobHandle, callingUid, packageName);
+ try {
+ return createSessionInternal(blobHandle, callingUid, packageName);
+ } catch (LimitExceededException e) {
+ throw new ParcelableException(e);
+ }
}
@Override
@@ -1399,6 +1500,8 @@ public class BlobStoreManagerService extends SystemService {
"leaseExpiryTimeMillis must not be negative");
Objects.requireNonNull(packageName, "packageName must not be null");
+ description = BlobStoreConfig.getTruncatedLeaseDescription(description);
+
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index baafff53d072..2f83be1e0370 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -32,6 +32,7 @@ import static android.text.format.Formatter.formatFileSize;
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_SESSION_CREATION_TIME;
+import static com.android.server.blob.BlobStoreConfig.getMaxPermittedPackages;
import static com.android.server.blob.BlobStoreConfig.hasSessionExpired;
import android.annotation.BytesLong;
@@ -43,7 +44,9 @@ import android.app.blob.IBlobStoreSession;
import android.content.Context;
import android.os.Binder;
import android.os.FileUtils;
+import android.os.LimitExceededException;
import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
import android.os.Trace;
@@ -76,7 +79,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
-/** TODO: add doc */
+/**
+ * Class to represent the state corresponding to an ongoing
+ * {@link android.app.blob.BlobStoreManager.Session}
+ */
@VisibleForTesting
class BlobStoreSession extends IBlobStoreSession.Stub {
@@ -326,6 +332,11 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
throw new IllegalStateException("Not allowed to change access type in state: "
+ stateToString(mState));
}
+ if (mBlobAccessMode.getNumWhitelistedPackages() >= getMaxPermittedPackages()) {
+ throw new ParcelableException(new LimitExceededException(
+ "Too many packages permitted to access the blob: "
+ + mBlobAccessMode.getNumWhitelistedPackages()));
+ }
mBlobAccessMode.allowPackageAccess(packageName, certificate);
}
}
@@ -468,6 +479,11 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
}
}
+ void destroy() {
+ revokeAllFds();
+ getSessionFile().delete();
+ }
+
private void revokeAllFds() {
synchronized (mRevocableFds) {
for (int i = mRevocableFds.size() - 1; i >= 0; --i) {
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index b3137d84c3f1..12ec9eb4eb73 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -34,7 +34,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
-import libcore.timezone.ZoneInfoDb;
+import com.android.i18n.timezone.ZoneInfoDb;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 231263579088..42725c51fd87 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -59,9 +59,9 @@ import java.util.List;
*
* <p class="caution"><strong>Note:</strong> Beginning with API 30
* ({@link android.os.Build.VERSION_CODES#R}), JobScheduler will throttle runaway applications.
- * Calling {@link #schedule(JobInfo)} and other such methods with very high frequency is indicative
- * of an app bug and so, to make sure the system doesn't get overwhelmed, JobScheduler will begin
- * to throttle apps that show buggy behavior, regardless of target SDK version.
+ * Calling {@link #schedule(JobInfo)} and other such methods with very high frequency can have a
+ * high cost and so, to make sure the system doesn't get overwhelmed, JobScheduler will begin
+ * to throttle apps, regardless of target SDK version.
*/
@SystemService(Context.JOB_SCHEDULER_SERVICE)
public abstract class JobScheduler {
@@ -74,9 +74,16 @@ public abstract class JobScheduler {
public @interface Result {}
/**
- * Returned from {@link #schedule(JobInfo)} when an invalid parameter was supplied. This can occur
- * if the run-time for your job is too short, or perhaps the system can't resolve the
- * requisite {@link JobService} in your package.
+ * Returned from {@link #schedule(JobInfo)} if a job wasn't scheduled successfully. Scheduling
+ * can fail for a variety of reasons, including, but not limited to:
+ * <ul>
+ * <li>an invalid parameter was supplied (eg. the run-time for your job is too short, or the
+ * system can't resolve the requisite {@link JobService} in your package)</li>
+ * <li>the app has too many jobs scheduled</li>
+ * <li>the app has tried to schedule too many jobs in a short amount of time</li>
+ * </ul>
+ * Attempting to schedule the job again immediately after receiving this result will not
+ * guarantee a successful schedule.
*/
public static final int RESULT_FAILURE = 0;
/**
@@ -89,6 +96,11 @@ public abstract class JobScheduler {
* ID with the new information in the {@link JobInfo}. If a job with the given ID is currently
* running, it will be stopped.
*
+ * <p class="caution"><strong>Note:</strong> Scheduling a job can have a high cost, even if it's
+ * rescheduling the same job and the job didn't execute, especially on platform versions before
+ * version {@link android.os.Build.VERSION_CODES#Q}. As such, the system may throttle calls to
+ * this API if calls are made too frequently in a short amount of time.
+ *
* @param job The job you wish scheduled. See
* {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
* you can schedule.
diff --git a/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java b/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java
new file mode 100644
index 000000000000..b0b9abccd229
--- /dev/null
+++ b/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+
+/**
+ * Tracks the forced-app-standby state for apps.
+ */
+public interface AppStateTracker {
+ String TAG = "AppStateTracker";
+
+ /**
+ * Register a {@link ServiceStateListener} to listen for forced-app-standby changes that should
+ * affect services.
+ */
+ void addServiceStateListener(@NonNull ServiceStateListener listener);
+
+ /**
+ * A listener to listen to forced-app-standby changes that should affect services.
+ */
+ interface ServiceStateListener {
+ /**
+ * Called when an app goes into forced app standby and its foreground
+ * services need to be removed from that state.
+ */
+ void stopForegroundServicesForUidPackage(int uid, String packageName);
+ }
+}
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 6475f5706a6d..18643ed91276 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -37,7 +37,7 @@ public interface DeviceIdleInternal {
String reason);
// duration in milliseconds
- long getNotificationWhitelistDuration();
+ long getNotificationAllowlistDuration();
void setJobsActive(boolean active);
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 2a4081ae16f8..398ccb69fbe8 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -7,7 +7,6 @@ import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.app.usage.UsageStatsManager.SystemForcedReasons;
import android.content.Context;
-import android.os.Looper;
import android.util.IndentingPrintWriter;
import java.io.PrintWriter;
@@ -21,13 +20,12 @@ public interface AppStandbyInternal {
* TODO AppStandbyController should probably be a binder service, and then we shouldn't need
* this method.
*/
- static AppStandbyInternal newAppStandbyController(ClassLoader loader, Context context,
- Looper looper) {
+ static AppStandbyInternal newAppStandbyController(ClassLoader loader, Context context) {
try {
final Class<?> clazz = Class.forName("com.android.server.usage.AppStandbyController",
true, loader);
- final Constructor<?> ctor = clazz.getConstructor(Context.class, Looper.class);
- return (AppStandbyInternal) ctor.newInstance(context, looper);
+ final Constructor<?> ctor = clazz.getConstructor(Context.class);
+ return (AppStandbyInternal) ctor.newInstance(context);
} catch (NoSuchMethodException | InstantiationException
| IllegalAccessException | InvocationTargetException | ClassNotFoundException e) {
throw new RuntimeException("Unable to instantiate AppStandbyController!", e);
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 47267dfc90aa..8aa88c233f07 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -19,4 +19,8 @@ java_library {
// Rename classes shared with the framework
jarjar_rules: "jarjar-rules.txt",
+
+ required: [
+ "libalarm_jni",
+ ],
}
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 7343352e0c71..6d2363549604 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -73,8 +73,7 @@ import java.util.Objects;
*
* Test: atest com.android.server.AppStateTrackerTest
*/
-public class AppStateTracker {
- private static final String TAG = "AppStateTracker";
+public class AppStateTrackerImpl implements AppStateTracker {
private static final boolean DEBUG = false;
private final Object mLock = new Object();
@@ -164,6 +163,16 @@ public class AppStateTracker {
@GuardedBy("mLock")
boolean mForcedAppStandbyEnabled;
+ @Override
+ public void addServiceStateListener(@NonNull ServiceStateListener listener) {
+ addListener(new Listener() {
+ @Override
+ public void stopForegroundServicesForUidPackage(int uid, String packageName) {
+ listener.stopForegroundServicesForUidPackage(uid, packageName);
+ }
+ });
+ }
+
interface Stats {
int UID_FG_STATE_CHANGED = 0;
int UID_ACTIVE_STATE_CHANGED = 1;
@@ -228,7 +237,7 @@ public class AppStateTracker {
}
mForcedAppStandbyEnabled = enabled;
if (DEBUG) {
- Slog.d(TAG,"Forced app standby feature flag changed: "
+ Slog.d(TAG, "Forced app standby feature flag changed: "
+ mForcedAppStandbyEnabled);
}
}
@@ -253,17 +262,20 @@ public class AppStateTracker {
}
}
- public static abstract class Listener {
+ /**
+ * Listener for any state changes that affect any app's eligibility to run.
+ */
+ public abstract static class Listener {
/**
* This is called when the OP_RUN_ANY_IN_BACKGROUND appops changed for a package.
*/
- private void onRunAnyAppOpsChanged(AppStateTracker sender,
+ private void onRunAnyAppOpsChanged(AppStateTrackerImpl sender,
int uid, @NonNull String packageName) {
updateJobsForUidPackage(uid, packageName, sender.isUidActive(uid));
if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ false)) {
unblockAlarmsForUidPackage(uid, packageName);
- } else if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ true)){
+ } else if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ true)) {
// we need to deliver the allow-while-idle alarms for this uid, package
unblockAllUnrestrictedAlarms();
}
@@ -278,14 +290,14 @@ public class AppStateTracker {
/**
* This is called when the foreground state changed for a UID.
*/
- private void onUidForegroundStateChanged(AppStateTracker sender, int uid) {
+ private void onUidForegroundStateChanged(AppStateTrackerImpl sender, int uid) {
onUidForeground(uid, sender.isUidInForeground(uid));
}
/**
* This is called when the active/idle state changed for a UID.
*/
- private void onUidActiveStateChanged(AppStateTracker sender, int uid) {
+ private void onUidActiveStateChanged(AppStateTrackerImpl sender, int uid) {
final boolean isActive = sender.isUidActive(uid);
updateJobsForUid(uid, isActive);
@@ -298,7 +310,7 @@ public class AppStateTracker {
/**
* This is called when an app-id(s) is removed from the power save whitelist.
*/
- private void onPowerSaveUnwhitelisted(AppStateTracker sender) {
+ private void onPowerSaveUnwhitelisted(AppStateTrackerImpl sender) {
updateAllJobs();
unblockAllUnrestrictedAlarms();
}
@@ -307,14 +319,14 @@ public class AppStateTracker {
* This is called when the power save whitelist changes, excluding the
* {@link #onPowerSaveUnwhitelisted} case.
*/
- private void onPowerSaveWhitelistedChanged(AppStateTracker sender) {
+ private void onPowerSaveWhitelistedChanged(AppStateTrackerImpl sender) {
updateAllJobs();
}
/**
* This is called when the temp whitelist changes.
*/
- private void onTempPowerSaveWhitelistChanged(AppStateTracker sender) {
+ private void onTempPowerSaveWhitelistChanged(AppStateTrackerImpl sender) {
// TODO This case happens rather frequently; consider optimizing and update jobs
// only for affected app-ids.
@@ -327,7 +339,7 @@ public class AppStateTracker {
/**
* This is called when the EXEMPT bucket is updated.
*/
- private void onExemptChanged(AppStateTracker sender) {
+ private void onExemptChanged(AppStateTrackerImpl sender) {
// This doesn't happen very often, so just re-evaluate all jobs / alarms.
updateAllJobs();
unblockAllUnrestrictedAlarms();
@@ -336,7 +348,7 @@ public class AppStateTracker {
/**
* This is called when the global "force all apps standby" flag changes.
*/
- private void onForceAllAppsStandbyChanged(AppStateTracker sender) {
+ private void onForceAllAppsStandbyChanged(AppStateTrackerImpl sender) {
updateAllJobs();
if (!sender.isForceAllAppsStandbyEnabled()) {
@@ -401,14 +413,12 @@ public class AppStateTracker {
/**
* Called when an ephemeral uid goes to the background, so its alarms need to be removed.
- *
- * @param uid
*/
public void removeAlarmsForUid(int uid) {
}
}
- public AppStateTracker(Context context, Looper looper) {
+ public AppStateTrackerImpl(Context context, Looper looper) {
mContext = context;
mHandler = new MyHandler(looper);
}
@@ -711,7 +721,7 @@ public class AppStateTracker {
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
int bucket, int reason) {
if (DEBUG) {
- Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId
+ Slog.d(TAG, "onAppIdleStateChanged: " + packageName + " u" + userId
+ (idle ? " idle" : " active") + " " + bucket);
}
synchronized (mLock) {
@@ -751,7 +761,7 @@ public class AppStateTracker {
private static final int MSG_ON_UID_GONE = 13;
private static final int MSG_ON_UID_IDLE = 14;
- public MyHandler(Looper looper) {
+ MyHandler(Looper looper) {
super(looper);
}
@@ -831,7 +841,7 @@ public class AppStateTracker {
return;
}
}
- final AppStateTracker sender = AppStateTracker.this;
+ final AppStateTrackerImpl sender = AppStateTrackerImpl.this;
long start = mStatLogger.getTime();
switch (msg.what) {
@@ -1077,7 +1087,7 @@ public class AppStateTracker {
// Public interface.
/**
- * Register a new listener.
+ * Register a listener to get callbacks when any state changes.
*/
public void addListener(@NonNull Listener listener) {
synchronized (mLock) {
@@ -1127,8 +1137,7 @@ public class AppStateTracker {
if (ArrayUtils.contains(mPowerWhitelistedAllAppIds, appId)) {
return false;
}
- if (useTempWhitelistToo &&
- ArrayUtils.contains(mTempWhitelistedAppIds, appId)) {
+ if (useTempWhitelistToo && ArrayUtils.contains(mTempWhitelistedAppIds, appId)) {
return false;
}
if (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)) {
@@ -1197,7 +1206,6 @@ public class AppStateTracker {
/**
* @return whether force all apps standby is enabled or not.
- *
*/
public boolean isForceAllAppsStandbyEnabled() {
synchronized (mLock) {
@@ -1248,11 +1256,18 @@ public class AppStateTracker {
}
}
+ /**
+ * @deprecated use {@link #dump(IndentingPrintWriter)} instead.
+ */
@Deprecated
public void dump(PrintWriter pw, String prefix) {
dump(new IndentingPrintWriter(pw, " ").setIndent(prefix));
}
+ /**
+ * Dump the internal state to the given PrintWriter. Can be included in the dump
+ * of a binder service to be output on the shell command "dumpsys".
+ */
public void dump(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled);
@@ -1329,6 +1344,9 @@ public class AppStateTracker {
pw.println("]");
}
+ /**
+ * Proto version of {@link #dump(IndentingPrintWriter)}
+ */
public void dumpProto(ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
final long token = proto.start(fieldId);
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 7b681fb8d1f8..b1bafeea3c98 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -286,7 +286,7 @@ public class DeviceIdleController extends SystemService
private Intent mIdleIntent;
private Intent mLightIdleIntent;
private AnyMotionDetector mAnyMotionDetector;
- private final AppStateTracker mAppStateTracker;
+ private final AppStateTrackerImpl mAppStateTracker;
private boolean mLightEnabled;
private boolean mDeepEnabled;
private boolean mQuickDozeActivated;
@@ -890,7 +890,8 @@ public class DeviceIdleController extends SystemService
"mms_temp_app_whitelist_duration";
private static final String KEY_SMS_TEMP_APP_WHITELIST_DURATION =
"sms_temp_app_whitelist_duration";
- private static final String KEY_NOTIFICATION_WHITELIST_DURATION =
+ // TODO(b/124466289): update value to match the name
+ private static final String KEY_NOTIFICATION_ALLOWLIST_DURATION =
"notification_whitelist_duration";
/**
* Whether to wait for the user to unlock the device before causing screen-on to
@@ -1124,9 +1125,9 @@ public class DeviceIdleController extends SystemService
* Amount of time we would like to whitelist an app that is handling a
* {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
- * @see #KEY_NOTIFICATION_WHITELIST_DURATION
+ * @see #KEY_NOTIFICATION_ALLOWLIST_DURATION
*/
- public long NOTIFICATION_WHITELIST_DURATION;
+ public long NOTIFICATION_ALLOWLIST_DURATION;
/**
* Pre idle time factor use to make idle delay longer
@@ -1230,8 +1231,8 @@ public class DeviceIdleController extends SystemService
KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L);
SMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);
- NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis(
- KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
+ NOTIFICATION_ALLOWLIST_DURATION = mParser.getDurationMillis(
+ KEY_NOTIFICATION_ALLOWLIST_DURATION, 30 * 1000L);
WAIT_FOR_UNLOCK = mParser.getBoolean(KEY_WAIT_FOR_UNLOCK, true);
PRE_IDLE_FACTOR_LONG = mParser.getFloat(KEY_PRE_IDLE_FACTOR_LONG, 1.67f);
PRE_IDLE_FACTOR_SHORT = mParser.getFloat(KEY_PRE_IDLE_FACTOR_SHORT, 0.33f);
@@ -1343,8 +1344,8 @@ public class DeviceIdleController extends SystemService
TimeUtils.formatDuration(SMS_TEMP_APP_WHITELIST_DURATION, pw);
pw.println();
- pw.print(" "); pw.print(KEY_NOTIFICATION_WHITELIST_DURATION); pw.print("=");
- TimeUtils.formatDuration(NOTIFICATION_WHITELIST_DURATION, pw);
+ pw.print(" "); pw.print(KEY_NOTIFICATION_ALLOWLIST_DURATION); pw.print("=");
+ TimeUtils.formatDuration(NOTIFICATION_ALLOWLIST_DURATION, pw);
pw.println();
pw.print(" "); pw.print(KEY_WAIT_FOR_UNLOCK); pw.print("=");
@@ -1792,8 +1793,8 @@ public class DeviceIdleController extends SystemService
// duration in milliseconds
@Override
- public long getNotificationWhitelistDuration() {
- return mConstants.NOTIFICATION_WHITELIST_DURATION;
+ public long getNotificationAllowlistDuration() {
+ return mConstants.NOTIFICATION_ALLOWLIST_DURATION;
}
@Override
@@ -1859,8 +1860,8 @@ public class DeviceIdleController extends SystemService
return new AnyMotionDetector(getPowerManager(), handler, sm, callback, angleThreshold);
}
- AppStateTracker getAppStateTracker(Context ctx, Looper looper) {
- return new AppStateTracker(ctx, looper);
+ AppStateTrackerImpl getAppStateTracker(Context ctx, Looper looper) {
+ return new AppStateTrackerImpl(ctx, looper);
}
ConnectivityManager getConnectivityManager() {
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index d99830dc47c9..b8fef63fa008 100644
--- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -10,6 +10,15 @@
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
+ },
+ {
+ "name": "FrameworksMockingServicesTests",
+ "file_patterns": ["AppStateTrackerImpl\\.java"],
+ "options": [
+ {"include-filter": "com.android.server.AppStateTrackerTest"},
+ {"include-annotation": "android.platform.test.annotations.Presubmit"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
}
],
"postsubmit": [
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 3e43dcb8f763..6529503808b3 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -99,7 +99,8 @@ import com.android.internal.util.LocalLog;
import com.android.internal.util.StatLogger;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppStateTracker;
-import com.android.server.AppStateTracker.Listener;
+import com.android.server.AppStateTrackerImpl;
+import com.android.server.AppStateTrackerImpl.Listener;
import com.android.server.DeviceIdleInternal;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -286,7 +287,7 @@ public class AlarmManagerService extends SystemService {
private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
new SparseArray<>();
- private AppStateTracker mAppStateTracker;
+ private AppStateTrackerImpl mAppStateTracker;
private boolean mAppStandbyParole;
/**
@@ -1593,7 +1594,8 @@ public class AlarmManagerService extends SystemService {
LocalServices.getService(AppStandbyInternal.class);
appStandbyInternal.addListener(new AppStandbyTracker());
- mAppStateTracker = LocalServices.getService(AppStateTracker.class);
+ mAppStateTracker =
+ (AppStateTrackerImpl) LocalServices.getService(AppStateTracker.class);
mAppStateTracker.addListener(mForceAppStandbyListener);
mClockReceiver.scheduleTimeTickEvent();
@@ -3831,6 +3833,7 @@ public class AlarmManagerService extends SystemService {
}
void init() {
+ System.loadLibrary("alarm_jni");
mNativeData = AlarmManagerService.init();
}
@@ -4392,8 +4395,7 @@ public class AlarmManagerService extends SystemService {
/**
* Tracking of app assignments to standby buckets
*/
- private final class AppStandbyTracker extends
- AppIdleStateChangeListener {
+ private final class AppStandbyTracker extends AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
boolean idle, int bucket, int reason) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index ec9bdadcfae2..06c469a5cc82 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -86,6 +86,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.AppStateTracker;
+import com.android.server.AppStateTrackerImpl;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
@@ -254,6 +255,18 @@ public class JobSchedulerService extends com.android.server.SystemService
private final CountQuotaTracker mQuotaTracker;
private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
+ private static final String QUOTA_TRACKER_SCHEDULE_LOGGED =
+ ".schedulePersisted out-of-quota logged";
+ private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED = new Category(
+ ".schedulePersisted()");
+ private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED = new Category(
+ ".schedulePersisted out-of-quota logged");
+ private static final Categorizer QUOTA_CATEGORIZER = (userId, packageName, tag) -> {
+ if (QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG.equals(tag)) {
+ return QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED;
+ }
+ return QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED;
+ };
/**
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list
@@ -270,7 +283,8 @@ public class JobSchedulerService extends com.android.server.SystemService
ActivityManagerInternal mActivityManagerInternal;
IBatteryStats mBatteryStats;
DeviceIdleInternal mLocalDeviceIdleController;
- AppStateTracker mAppStateTracker;
+ @VisibleForTesting
+ AppStateTrackerImpl mAppStateTracker;
final UsageStatsManagerInternal mUsageStats;
private final AppStandbyInternal mAppStandbyInternal;
@@ -342,10 +356,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final StateController sc = mControllers.get(controller);
sc.onConstantsUpdatedLocked();
}
- mQuotaTracker.setEnabled(mConstants.ENABLE_API_QUOTAS);
- mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
- mConstants.API_QUOTA_SCHEDULE_COUNT,
- mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
+ updateQuotaTracker();
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
@@ -355,6 +366,14 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
+ @VisibleForTesting
+ void updateQuotaTracker() {
+ mQuotaTracker.setEnabled(mConstants.ENABLE_API_QUOTAS);
+ mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED,
+ mConstants.API_QUOTA_SCHEDULE_COUNT,
+ mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
+ }
+
static class MaxJobCounts {
private final KeyValueListParser.IntValue mTotal;
private final KeyValueListParser.IntValue mMaxBg;
@@ -507,6 +526,8 @@ public class JobSchedulerService extends com.android.server.SystemService
private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms";
private static final String KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION =
"aq_schedule_throw_exception";
+ private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
+ "aq_schedule_return_failure";
private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
@@ -520,6 +541,7 @@ public class JobSchedulerService extends com.android.server.SystemService
private static final int DEFAULT_API_QUOTA_SCHEDULE_COUNT = 250;
private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS;
private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
+ private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
/**
* Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -623,6 +645,11 @@ public class JobSchedulerService extends com.android.server.SystemService
*/
public boolean API_QUOTA_SCHEDULE_THROW_EXCEPTION =
DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION;
+ /**
+ * Whether or not to return a failure result when an app hits its schedule quota limit.
+ */
+ public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
+ DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -678,6 +705,9 @@ public class JobSchedulerService extends com.android.server.SystemService
API_QUOTA_SCHEDULE_THROW_EXCEPTION = mParser.getBoolean(
KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION);
+ API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = mParser.getBoolean(
+ KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
+ DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT);
}
void dump(IndentingPrintWriter pw) {
@@ -712,6 +742,8 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(KEY_API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS).println();
pw.print(KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
API_QUOTA_SCHEDULE_THROW_EXCEPTION).println();
+ pw.print(KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
+ API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT).println();
pw.decreaseIndent();
}
@@ -740,6 +772,8 @@ public class JobSchedulerService extends com.android.server.SystemService
proto.write(ConstantsProto.API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS);
proto.write(ConstantsProto.API_QUOTA_SCHEDULE_THROW_EXCEPTION,
API_QUOTA_SCHEDULE_THROW_EXCEPTION);
+ proto.write(ConstantsProto.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
+ API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT);
}
}
@@ -973,12 +1007,17 @@ public class JobSchedulerService extends com.android.server.SystemService
public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
int userId, String tag) {
- if (job.isPersisted()) {
- // Only limit schedule calls for persisted jobs.
+ final String servicePkg = job.getService().getPackageName();
+ if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) {
+ // Only limit schedule calls for persisted jobs scheduled by the app itself.
final String pkg =
packageName == null ? job.getService().getPackageName() : packageName;
if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
- Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
+ if (mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED)) {
+ // Don't log too frequently
+ Slog.wtf(TAG, userId + "-" + pkg + " has called schedule() too many times");
+ mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED);
+ }
mAppStandbyInternal.restrictApp(
pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
@@ -1004,13 +1043,17 @@ public class JobSchedulerService extends com.android.server.SystemService
// Only throw the exception for debuggable apps.
throw new LimitExceededException(
"schedule()/enqueue() called more than "
- + mQuotaTracker.getLimit(Category.SINGLE_CATEGORY)
+ + mQuotaTracker.getLimit(
+ QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED)
+ " times in the past "
- + mQuotaTracker.getWindowSizeMs(Category.SINGLE_CATEGORY)
- + "ms");
+ + mQuotaTracker.getWindowSizeMs(
+ QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED)
+ + "ms. See the documentation for more information.");
}
}
- return JobScheduler.RESULT_FAILURE;
+ if (mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT) {
+ return JobScheduler.RESULT_FAILURE;
+ }
}
mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG);
}
@@ -1371,10 +1414,12 @@ public class JobSchedulerService extends com.android.server.SystemService
// Set up the app standby bucketing tracker
mStandbyTracker = new StandbyTracker();
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
- mQuotaTracker = new CountQuotaTracker(context, Categorizer.SINGLE_CATEGORIZER);
- mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
+ mQuotaTracker = new CountQuotaTracker(context, QUOTA_CATEGORIZER);
+ mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED,
mConstants.API_QUOTA_SCHEDULE_COUNT,
mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
+ // Log at most once per minute.
+ mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED, 1, 60_000);
mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
mAppStandbyInternal.addListener(mStandbyTracker);
@@ -1480,7 +1525,7 @@ public class JobSchedulerService extends com.android.server.SystemService
controller.onSystemServicesReady();
}
- mAppStateTracker = Objects.requireNonNull(
+ mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull(
LocalServices.getService(AppStateTracker.class));
// Register br for package removals and user removals.
@@ -2243,7 +2288,8 @@ public class JobSchedulerService extends com.android.server.SystemService
}
// Everything else checked out so far, so this is the final yes/no check
- final boolean appIsBad = mActivityManagerInternal.isAppBad(service.applicationInfo);
+ final boolean appIsBad = mActivityManagerInternal.isAppBad(
+ service.processName, service.applicationInfo.uid);
if (DEBUG && appIsBad) {
Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable");
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index f2a55805d70a..7bd51b77a119 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -335,7 +335,7 @@ public final class JobStore {
Slog.v(TAG, "Scheduling persist of jobs to disk.");
}
mIoHandler.postDelayed(mWriteRunnable, JOB_PERSIST_DELAY);
- mWriteScheduled = mWriteInProgress = true;
+ mWriteScheduled = true;
}
}
}
@@ -353,7 +353,7 @@ public final class JobStore {
throw new IllegalStateException("An asynchronous write is already scheduled.");
}
- mWriteScheduled = mWriteInProgress = true;
+ mWriteScheduled = true;
mWriteRunnable.run();
}
}
@@ -369,7 +369,7 @@ public final class JobStore {
final long start = SystemClock.uptimeMillis();
final long end = start + maxWaitMillis;
synchronized (mWriteScheduleLock) {
- while (mWriteInProgress) {
+ while (mWriteScheduled || mWriteInProgress) {
final long now = SystemClock.uptimeMillis();
if (now >= end) {
// still not done and we've hit the end; failure
@@ -404,6 +404,12 @@ public final class JobStore {
// a bit of lock contention.
synchronized (mWriteScheduleLock) {
mWriteScheduled = false;
+ if (mWriteInProgress) {
+ // Another runnable is currently writing. Postpone this new write task.
+ maybeWriteStatusToDiskAsync();
+ return;
+ }
+ mWriteInProgress = true;
}
synchronized (mLock) {
// Clone the jobs so we can release the lock before writing.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index fd26b72ef9d9..b6324358ec33 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -26,7 +26,8 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.server.AppStateTracker;
-import com.android.server.AppStateTracker.Listener;
+import com.android.server.AppStateTrackerImpl;
+import com.android.server.AppStateTrackerImpl.Listener;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
@@ -56,12 +57,12 @@ public final class BackgroundJobsController extends StateController {
static final int KNOWN_ACTIVE = 1;
static final int KNOWN_INACTIVE = 2;
- private final AppStateTracker mAppStateTracker;
+ private final AppStateTrackerImpl mAppStateTracker;
public BackgroundJobsController(JobSchedulerService service) {
super(service);
- mAppStateTracker = Objects.requireNonNull(
+ mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull(
LocalServices.getService(AppStateTracker.class));
mAppStateTracker.addListener(mForceAppStandbyListener);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index 361ebe55ccd8..227b8276abe1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -18,28 +18,20 @@ package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
-import android.content.ContentResolver;
import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.provider.Settings;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.job.ConstantsProto;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -63,9 +55,6 @@ public final class TimeController extends StateController {
/** Delay alarm tag for logging purposes */
private final String DELAY_TAG = "*job.delay*";
- private final Handler mHandler;
- private final TcConstants mTcConstants;
-
private long mNextJobExpiredElapsedMillis;
private long mNextDelayExpiredElapsedMillis;
@@ -81,14 +70,6 @@ public final class TimeController extends StateController {
mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
-
- mHandler = new Handler(mContext.getMainLooper());
- mTcConstants = new TcConstants(mHandler);
- }
-
- @Override
- public void onSystemServicesReady() {
- mTcConstants.start(mContext.getContentResolver());
}
/**
@@ -372,8 +353,7 @@ public final class TimeController extends StateController {
/**
* Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's
* delay will expire.
- * This alarm <b>will not</b> wake up the phone if
- * {@link TcConstants#USE_NON_WAKEUP_ALARM_FOR_DELAY} is true.
+ * This alarm <b>will not</b> wake up the phone.
*/
private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
@@ -381,10 +361,7 @@ public final class TimeController extends StateController {
return;
}
mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
- final int alarmType =
- mTcConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY
- ? AlarmManager.ELAPSED_REALTIME : AlarmManager.ELAPSED_REALTIME_WAKEUP;
- updateAlarmWithListenerLocked(DELAY_TAG, alarmType,
+ updateAlarmWithListenerLocked(DELAY_TAG, AlarmManager.ELAPSED_REALTIME,
mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis, ws);
}
@@ -443,80 +420,6 @@ public final class TimeController extends StateController {
}
};
- @VisibleForTesting
- class TcConstants extends ContentObserver {
- private ContentResolver mResolver;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
-
- private static final String KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY =
- "use_non_wakeup_delay_alarm";
-
- private static final boolean DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
-
- /**
- * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
- * ready now.
- */
- public boolean USE_NON_WAKEUP_ALARM_FOR_DELAY = DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY;
-
- /**
- * Creates a content observer.
- *
- * @param handler The handler to run {@link #onChange} on, or null if none.
- */
- TcConstants(Handler handler) {
- super(handler);
- }
-
- private void start(ContentResolver resolver) {
- mResolver = resolver;
- mResolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS), false, this);
- onChange(true, null);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- final String constants = Settings.Global.getString(
- mResolver, Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);
-
- try {
- mParser.setString(constants);
- } catch (Exception e) {
- // Failed to parse the settings string, log this and move on with defaults.
- Slog.e(TAG, "Bad jobscheduler time controller settings", e);
- }
-
- USE_NON_WAKEUP_ALARM_FOR_DELAY = mParser.getBoolean(
- KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY, DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY);
- // Intentionally not calling checkExpiredDelaysAndResetAlarm() here. There's no need to
- // iterate through the entire list again for this constant change. The next delay alarm
- // that is set will make use of the new constant value.
- }
-
- private void dump(IndentingPrintWriter pw) {
- pw.println();
- pw.println("TimeController:");
- pw.increaseIndent();
- pw.print(KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY,
- USE_NON_WAKEUP_ALARM_FOR_DELAY).println();
- pw.decreaseIndent();
- }
-
- private void dump(ProtoOutputStream proto) {
- final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
- proto.write(ConstantsProto.TimeController.USE_NON_WAKEUP_ALARM_FOR_DELAY,
- USE_NON_WAKEUP_ALARM_FOR_DELAY);
- proto.end(tcToken);
- }
- }
-
- @VisibleForTesting
- @NonNull
- TcConstants getTcConstants() {
- return mTcConstants;
- }
-
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
@@ -591,14 +494,4 @@ public final class TimeController extends StateController {
proto.end(mToken);
proto.end(token);
}
-
- @Override
- public void dumpConstants(IndentingPrintWriter pw) {
- mTcConstants.dump(pw);
- }
-
- @Override
- public void dumpConstants(ProtoOutputStream proto) {
- mTcConstants.dump(proto);
- }
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index ae95b49bd113..2f993dad51c7 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -110,6 +110,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
+import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.usage.AppIdleHistory.AppUsageHistory;
@@ -418,8 +419,8 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
- public AppStandbyController(Context context, Looper looper) {
- this(new Injector(context, looper));
+ public AppStandbyController(Context context) {
+ this(new Injector(context, JobSchedulerBackgroundThread.get().getLooper()));
}
AppStandbyController(Injector injector) {
@@ -486,17 +487,11 @@ public class AppStandbyController implements AppStandbyInternal {
mSystemServicesReady = true;
- // Offload to handler thread to avoid boot time impact.
- mHandler.post(mInjector::updatePowerWhitelistCache);
-
boolean userFileExists;
synchronized (mAppIdleLock) {
userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM);
}
- // Offload to handler thread to avoid boottime impact.
- mHandler.post(this::loadHeadlessSystemAppCache);
-
if (mPendingInitializeDefaults || !userFileExists) {
initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
}
@@ -506,6 +501,12 @@ public class AppStandbyController implements AppStandbyInternal {
}
} else if (phase == PHASE_BOOT_COMPLETED) {
setChargingState(mInjector.isCharging());
+
+ // Offload to handler thread after boot completed to avoid boot time impact. This means
+ // that app standby buckets may be slightly out of date and headless system apps may be
+ // put in a lower bucket until boot has completed.
+ mHandler.post(AppStandbyController.this::updatePowerWhitelistCache);
+ mHandler.post(this::loadHeadlessSystemAppCache);
}
}
@@ -1714,6 +1715,14 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
+ private void updatePowerWhitelistCache() {
+ if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
+ return;
+ }
+ mInjector.updatePowerWhitelistCache();
+ postCheckIdleStates(UserHandle.USER_ALL);
+ }
+
private class PackageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -2027,10 +2036,7 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
- private void updatePowerWhitelistCache() {
- if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) {
- return;
- }
+ void updatePowerWhitelistCache() {
try {
// Don't call out to DeviceIdleController with the lock held.
final String[] whitelistedPkgs =
@@ -2230,7 +2236,7 @@ public class AppStandbyController implements AppStandbyInternal {
break;
case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
if (mSystemServicesReady) {
- mHandler.post(mInjector::updatePowerWhitelistCache);
+ mHandler.post(AppStandbyController.this::updatePowerWhitelistCache);
}
break;
}
diff --git a/apex/jobscheduler/service/jni/Android.bp b/apex/jobscheduler/service/jni/Android.bp
new file mode 100644
index 000000000000..c502867dc6f1
--- /dev/null
+++ b/apex/jobscheduler/service/jni/Android.bp
@@ -0,0 +1,41 @@
+cc_library_shared {
+ name: "libalarm_jni",
+
+ cpp_std: "c++2a",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+ ],
+
+ srcs: [
+ "com_android_server_alarm_AlarmManagerService.cpp",
+ "onload.cpp",
+ ],
+
+ shared_libs: [
+ "libnativehelper",
+ "liblog",
+ "libbase",
+ ],
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "com_android_server_alarm_AlarmManagerService.cpp",
+ ],
+ srcs: [
+ ":arctimersrcs",
+ ],
+ }
+ }
+
+}
+
+filegroup {
+ name: "lib_alarmManagerService_native",
+ srcs: [
+ "com_android_server_alarm_AlarmManagerService.cpp",
+ ],
+}
diff --git a/services/core/jni/com_android_server_alarm_AlarmManagerService.cpp b/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp
index dfe8aa8a1507..8f9e187a7a93 100644
--- a/services/core/jni/com_android_server_alarm_AlarmManagerService.cpp
+++ b/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp
@@ -1,5 +1,4 @@
-/* //device/libs/android_runtime/android_server_AlarmManagerService.cpp
-**
+/*
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,11 +16,13 @@
#define LOG_TAG "AlarmManagerService"
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
#include <nativehelper/JNIHelp.h>
-#include "jni.h"
#include <utils/Log.h>
-#include <utils/misc.h>
#include <utils/String8.h>
+#include <utils/misc.h>
+#include "jni.h"
#include <dirent.h>
#include <fcntl.h>
@@ -75,8 +76,8 @@ typedef std::array<int, N_ANDROID_TIMERFDS> TimerFds;
class AlarmImpl
{
public:
- AlarmImpl(const TimerFds &fds, int epollfd, int rtc_id) :
- fds{fds}, epollfd{epollfd}, rtc_id{rtc_id} { }
+ AlarmImpl(const TimerFds &fds, int epollfd, const std::string &rtc_dev)
+ : fds{fds}, epollfd{epollfd}, rtc_dev{rtc_dev} {}
~AlarmImpl();
int set(int type, struct timespec *ts);
@@ -87,7 +88,7 @@ public:
private:
const TimerFds fds;
const int epollfd;
- const int rtc_id;
+ std::string rtc_dev;
};
AlarmImpl::~AlarmImpl()
@@ -132,38 +133,24 @@ int AlarmImpl::getTime(int type, struct itimerspec *spec)
int AlarmImpl::setTime(struct timeval *tv)
{
- struct rtc_time rtc;
- struct tm tm, *gmtime_res;
- int fd;
- int res;
-
- res = settimeofday(tv, NULL);
- if (res < 0) {
- ALOGV("settimeofday() failed: %s\n", strerror(errno));
+ if (settimeofday(tv, NULL) == -1) {
+ ALOGV("settimeofday() failed: %s", strerror(errno));
return -1;
}
- if (rtc_id < 0) {
- ALOGV("Not setting RTC because wall clock RTC was not found");
- errno = ENODEV;
+ android::base::unique_fd fd{open(rtc_dev.c_str(), O_RDWR)};
+ if (!fd.ok()) {
+ ALOGE("Unable to open %s: %s", rtc_dev.c_str(), strerror(errno));
return -1;
}
- android::String8 rtc_dev = String8::format("/dev/rtc%d", rtc_id);
- fd = open(rtc_dev.string(), O_RDWR);
- if (fd < 0) {
- ALOGV("Unable to open %s: %s\n", rtc_dev.string(), strerror(errno));
- return res;
- }
-
- gmtime_res = gmtime_r(&tv->tv_sec, &tm);
- if (!gmtime_res) {
- ALOGV("gmtime_r() failed: %s\n", strerror(errno));
- res = -1;
- goto done;
+ struct tm tm;
+ if (!gmtime_r(&tv->tv_sec, &tm)) {
+ ALOGV("gmtime_r() failed: %s", strerror(errno));
+ return -1;
}
- memset(&rtc, 0, sizeof(rtc));
+ struct rtc_time rtc = {};
rtc.tm_sec = tm.tm_sec;
rtc.tm_min = tm.tm_min;
rtc.tm_hour = tm.tm_hour;
@@ -173,12 +160,12 @@ int AlarmImpl::setTime(struct timeval *tv)
rtc.tm_wday = tm.tm_wday;
rtc.tm_yday = tm.tm_yday;
rtc.tm_isdst = tm.tm_isdst;
- res = ioctl(fd, RTC_SET_TIME, &rtc);
- if (res < 0)
- ALOGV("RTC_SET_TIME ioctl failed: %s\n", strerror(errno));
-done:
- close(fd);
- return res;
+ if (ioctl(fd, RTC_SET_TIME, &rtc) == -1) {
+ ALOGV("RTC_SET_TIME ioctl failed: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
}
int AlarmImpl::waitForAlarm()
@@ -251,65 +238,6 @@ static jint android_server_alarm_AlarmManagerService_setKernelTimezone(JNIEnv*,
return 0;
}
-static const char rtc_sysfs[] = "/sys/class/rtc";
-
-static bool rtc_is_hctosys(unsigned int rtc_id)
-{
- android::String8 hctosys_path = String8::format("%s/rtc%u/hctosys",
- rtc_sysfs, rtc_id);
- FILE *file = fopen(hctosys_path.string(), "re");
- if (!file) {
- ALOGE("failed to open %s: %s", hctosys_path.string(), strerror(errno));
- return false;
- }
-
- unsigned int hctosys;
- bool ret = false;
- int err = fscanf(file, "%u", &hctosys);
- if (err == EOF)
- ALOGE("failed to read from %s: %s", hctosys_path.string(),
- strerror(errno));
- else if (err == 0)
- ALOGE("%s did not have expected contents", hctosys_path.string());
- else
- ret = hctosys;
-
- fclose(file);
- return ret;
-}
-
-static int wall_clock_rtc()
-{
- std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(rtc_sysfs), closedir);
- if (!dir.get()) {
- ALOGE("failed to open %s: %s", rtc_sysfs, strerror(errno));
- return -1;
- }
-
- struct dirent *dirent;
- while (errno = 0, dirent = readdir(dir.get())) {
- unsigned int rtc_id;
- int matched = sscanf(dirent->d_name, "rtc%u", &rtc_id);
-
- if (matched < 0)
- break;
- else if (matched != 1)
- continue;
-
- if (rtc_is_hctosys(rtc_id)) {
- ALOGV("found wall clock RTC %u", rtc_id);
- return rtc_id;
- }
- }
-
- if (errno == 0)
- ALOGW("no wall clock RTC found");
- else
- ALOGE("failed to enumerate RTCs: %s", strerror(errno));
-
- return -1;
-}
-
static void log_timerfd_create_error(clockid_t id)
{
if (errno == EINVAL) {
@@ -343,8 +271,7 @@ static jlong android_server_alarm_AlarmManagerService_init(JNIEnv*, jobject)
epollfd = epoll_create(fds.size());
if (epollfd < 0) {
- ALOGE("epoll_create(%zu) failed: %s", fds.size(),
- strerror(errno));
+ ALOGE("epoll_create(%zu) failed: %s", fds.size(), strerror(errno));
return 0;
}
@@ -360,7 +287,19 @@ static jlong android_server_alarm_AlarmManagerService_init(JNIEnv*, jobject)
}
}
- AlarmImpl *ret = new AlarmImpl(fds, epollfd, wall_clock_rtc());
+ // Find the wall clock RTC. We expect this always to be /dev/rtc0, but
+ // check the /dev/rtc symlink first so that legacy devices that don't use
+ // rtc0 can add a symlink rather than need to carry a local patch to this
+ // code.
+ //
+ // TODO: if you're reading this in a world where all devices are using the
+ // GKI, you can remove the readlink and just assume /dev/rtc0.
+ std::string dev_rtc;
+ if (!android::base::Readlink("/dev/rtc", &dev_rtc)) {
+ dev_rtc = "/dev/rtc0";
+ }
+
+ std::unique_ptr<AlarmImpl> alarm{new AlarmImpl(fds, epollfd, dev_rtc)};
for (size_t i = 0; i < fds.size(); i++) {
epoll_event event;
@@ -370,13 +309,11 @@ static jlong android_server_alarm_AlarmManagerService_init(JNIEnv*, jobject)
int err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], &event);
if (err < 0) {
ALOGE("epoll_ctl(EPOLL_CTL_ADD) failed: %s", strerror(errno));
- delete ret;
return 0;
}
}
- struct itimerspec spec;
- memset(&spec, 0, sizeof(spec));
+ struct itimerspec spec = {};
/* 0 = disarmed; the timerfd doesn't need to be armed to get
RTC change notifications, just set up as cancelable */
@@ -384,11 +321,10 @@ static jlong android_server_alarm_AlarmManagerService_init(JNIEnv*, jobject)
TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, NULL);
if (err < 0) {
ALOGE("timerfd_settime() failed: %s", strerror(errno));
- delete ret;
return 0;
}
- return reinterpret_cast<jlong>(ret);
+ return reinterpret_cast<jlong>(alarm.release());
}
static jlong android_server_alarm_AlarmManagerService_getNextAlarm(JNIEnv*, jobject, jlong nativeData, jint type)
diff --git a/apex/jobscheduler/service/jni/onload.cpp b/apex/jobscheduler/service/jni/onload.cpp
new file mode 100644
index 000000000000..f40413fe91b4
--- /dev/null
+++ b/apex/jobscheduler/service/jni/onload.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+namespace android {
+int register_android_server_alarm_AlarmManagerService(JNIEnv* env);
+};
+
+using namespace android;
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGE("GetEnv failed!");
+ return result;
+ }
+ ALOG_ASSERT(env, "Could not retrieve the env!");
+
+ register_android_server_alarm_AlarmManagerService(env);
+ return JNI_VERSION_1_4;
+}
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index cc3017ab0777..e4b5d19e67c9 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -870,6 +870,14 @@ public final class MediaParser {
*/
public static final String PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS =
"android.media.mediaParser.overrideInBandCaptionDeclarations";
+ /**
+ * Sets whether a track for EMSG events should be exposed in case of parsing a container that
+ * supports them. {@code boolean} expected. Default value is {@link false}.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_EXPOSE_EMSG_TRACK =
+ "android.media.mediaParser.exposeEmsgTrack";
// Private constants.
@@ -880,6 +888,7 @@ public final class MediaParser {
private static final String TS_MODE_MULTI_PMT = "multi_pmt";
private static final String TS_MODE_HLS = "hls";
private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6;
+ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
@IntDef(
value = {
@@ -1218,6 +1227,7 @@ public final class MediaParser {
throw new ParsingException(e);
}
if (result == Extractor.RESULT_END_OF_INPUT) {
+ mExtractorInput = null;
return false;
}
if (result == Extractor.RESULT_SEEK) {
@@ -1309,6 +1319,10 @@ public final class MediaParser {
return new MatroskaExtractor(flags);
case PARSER_NAME_FMP4:
flags |=
+ getBooleanParameter(PARAMETER_EXPOSE_EMSG_TRACK)
+ ? FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK
+ : 0;
+ flags |=
getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
: 0;
@@ -1674,6 +1688,9 @@ public final class MediaParser {
if (cryptoData != mLastReceivedCryptoData) {
mLastOutputCryptoInfo =
createNewCryptoInfoAndPopulateWithCryptoData(cryptoData);
+ // We are using in-band crypto info, so the IV will be ignored. But we prevent
+ // it from being null because toString assumes it non-null.
+ mLastOutputCryptoInfo.iv = EMPTY_BYTE_ARRAY;
}
} else /* We must populate the full CryptoInfo. */ {
// CryptoInfo.pattern is not accessible to the user, so the user needs to feed
@@ -1916,8 +1933,10 @@ public final class MediaParser {
// format for convenient use from ExoPlayer.
result.setString("crypto-mode-fourcc", format.drmInitData.schemeType);
}
+ if (format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) {
+ result.setLong("subsample-offset-us-long", format.subsampleOffsetUs);
+ }
// LACK OF SUPPORT FOR:
- // format.containerMimeType;
// format.id;
// format.metadata;
// format.stereoMode;
@@ -2102,6 +2121,7 @@ public final class MediaParser {
PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT, Boolean.class);
expectedTypeByParameterName.put(
PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_EXPOSE_EMSG_TRACK, Boolean.class);
// We do not check PARAMETER_EXPOSE_CAPTION_FORMATS here, and we do it in setParameters
// instead. Checking that the value is a List is insufficient to catch wrong parameter
// value types.
diff --git a/apex/media/framework/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
index 081e76ab0215..6560afedab0f 100644
--- a/apex/media/framework/java/android/media/MediaSession2.java
+++ b/apex/media/framework/java/android/media/MediaSession2.java
@@ -404,7 +404,7 @@ public class MediaSession2 implements AutoCloseable {
mCallback.onPostConnect(MediaSession2.this, controllerInfo);
connected = true;
} finally {
- if (!connected) {
+ if (!connected || isClosed()) {
if (DEBUG) {
Log.d(TAG, "Rejecting connection or notifying that session is closed"
+ ", controllerInfo=" + controllerInfo);
diff --git a/apex/statsd/TEST_MAPPING b/apex/statsd/TEST_MAPPING
index 93f108707d9e..331fe77c708d 100644
--- a/apex/statsd/TEST_MAPPING
+++ b/apex/statsd/TEST_MAPPING
@@ -6,5 +6,14 @@
{
"name" : "LibStatsPullTests"
}
+ ],
+
+ "postsubmit" : [
+ {
+ "name" : "CtsStatsdHostTestCases"
+ },
+ {
+ "name" : "GtsStatsdHostTestCases"
+ }
]
}
diff --git a/apex/statsd/framework/test/Android.bp b/apex/statsd/framework/test/Android.bp
index b113d595b57c..5cc5647bc760 100644
--- a/apex/statsd/framework/test/Android.bp
+++ b/apex/statsd/framework/test/Android.bp
@@ -14,12 +14,8 @@
android_test {
name: "FrameworkStatsdTest",
- platform_apis: true,
- srcs: [
- // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
- ":framework-statsd-sources",
- "**/*.java",
- ],
+ sdk_version: "module_current",
+ srcs: [ "**/*.java" ],
manifest: "AndroidManifest.xml",
static_libs: [
"androidx.test.rules",
@@ -28,9 +24,10 @@ android_test {
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
+ "framework-statsd.impl",
],
test_suites: [
"device-tests",
"mts",
],
-} \ No newline at end of file
+}
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
index 240222ea9411..6108a324e15e 100644
--- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
@@ -33,7 +33,6 @@ import com.android.internal.os.StatsdConfigProto.PullAtomPackages;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import com.android.internal.os.statsd.StatsConfigUtils;
import com.android.internal.os.statsd.protos.TestAtoms;
import com.android.os.AtomsProto.Atom;
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/StatsConfigUtils.java
index d0d140092586..b5afb94886de 100644
--- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/StatsConfigUtils.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.internal.os.statsd;
+package com.android.internal.os.statsd.libstats;
import static com.google.common.truth.Truth.assertThat;
diff --git a/api/current.txt b/api/current.txt
index de013e2883a9..1d2286b333bb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2926,8 +2926,13 @@ package android.accessibilityservice {
field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
field public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; // 0xd
field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; // 0xe
+ field public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; // 0xe
+ field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11; // 0xb
+ field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc
+ field public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; // 0xd
field public static final int GLOBAL_ACTION_BACK = 1; // 0x1
field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
+ field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa
field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8
field public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; // 0x4
field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
@@ -24074,6 +24079,7 @@ package android.media {
field public static final int TYPE_IP = 20; // 0x14
field public static final int TYPE_LINE_ANALOG = 5; // 0x5
field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+ field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
field public static final int TYPE_TELEPHONY = 18; // 0x12
field public static final int TYPE_TV_TUNER = 17; // 0x11
field public static final int TYPE_UNKNOWN = 0; // 0x0
@@ -27900,6 +27906,7 @@ package android.media.audiofx {
field public static final java.util.UUID EFFECT_TYPE_DYNAMICS_PROCESSING;
field public static final java.util.UUID EFFECT_TYPE_ENV_REVERB;
field public static final java.util.UUID EFFECT_TYPE_EQUALIZER;
+ field @NonNull public static final java.util.UUID EFFECT_TYPE_HAPTIC_GENERATOR;
field public static final java.util.UUID EFFECT_TYPE_LOUDNESS_ENHANCER;
field public static final java.util.UUID EFFECT_TYPE_NS;
field public static final java.util.UUID EFFECT_TYPE_PRESET_REVERB;
@@ -28252,6 +28259,13 @@ package android.media.audiofx {
field public short numBands;
}
+ public class HapticGenerator extends android.media.audiofx.AudioEffect implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public static android.media.audiofx.HapticGenerator create(int);
+ method public static boolean isAvailable();
+ method public int setEnabled(boolean);
+ }
+
public class LoudnessEnhancer extends android.media.audiofx.AudioEffect {
ctor public LoudnessEnhancer(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.RuntimeException, java.lang.UnsupportedOperationException;
method public float getTargetGain() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
@@ -47039,7 +47053,7 @@ package android.telephony {
public abstract class CellLocation {
ctor public CellLocation();
method public static android.telephony.CellLocation getEmpty();
- method public static void requestLocationUpdate();
+ method @Deprecated public static void requestLocationUpdate();
}
public abstract class CellSignalStrength {
@@ -49101,6 +49115,7 @@ package android.telephony.ims.feature {
}
public static class MmTelFeature.MmTelCapabilities {
+ method public final boolean isCapable(int);
field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8
field public static final int CAPABILITY_TYPE_UT = 4; // 0x4
field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 1afe4493e33c..c91c39a4293d 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,4 +1,16 @@
// Signature format: 2.0
+package android.app {
+
+ public class AppOpsManager {
+ field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
+ }
+
+ public class NotificationManager {
+ method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
+ }
+
+}
+
package android.content.rollback {
public class RollbackManagerFrameworkInitializer {
@@ -21,24 +33,6 @@ package android.graphics {
package android.net {
- public final class TetheredClient implements android.os.Parcelable {
- ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
- method public int describeContents();
- method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
- method @NonNull public android.net.MacAddress getMacAddress();
- method public int getTetheringType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
- }
-
- public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.net.LinkAddress getAddress();
- method @Nullable public String getHostname();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
- }
-
public final class TetheringConstants {
field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
@@ -58,69 +52,15 @@ package android.net {
method @NonNull public String[] getTetheringErroredIfaces();
method public boolean isTetheringSupported();
method public boolean isTetheringSupported(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
method @Deprecated public int setUsbTethering(boolean);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
method @Deprecated public int tether(@NonNull String);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
method @Deprecated public int untether(@NonNull String);
- field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
- field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
- field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
- field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
- field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
- field public static final int TETHERING_BLUETOOTH = 2; // 0x2
- field public static final int TETHERING_ETHERNET = 5; // 0x5
- field public static final int TETHERING_INVALID = -1; // 0xffffffff
- field public static final int TETHERING_NCM = 4; // 0x4
- field public static final int TETHERING_USB = 1; // 0x1
- field public static final int TETHERING_WIFI = 0; // 0x0
- field public static final int TETHERING_WIFI_P2P = 3; // 0x3
- field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
- field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
- field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
- field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
- field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
- field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
- field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
- field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
- field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
- field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
- field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
- field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
- field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
- field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
- field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
- field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
- field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
- field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
- field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
- field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
- }
-
- public static interface TetheringManager.OnTetheringEntitlementResultListener {
- method public void onTetheringEntitlementResult(int);
- }
-
- public static interface TetheringManager.StartTetheringCallback {
- method public default void onTetheringFailed(int);
- method public default void onTetheringStarted();
}
public static interface TetheringManager.TetheringEventCallback {
- method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
- method public default void onError(@NonNull String, int);
- method public default void onOffloadStatusChanged(int);
method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
- method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheringSupported(boolean);
- method public default void onUpstreamChanged(@Nullable android.net.Network);
}
public static class TetheringManager.TetheringInterfaceRegexps {
@@ -129,26 +69,14 @@ package android.net {
method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
}
- public static class TetheringManager.TetheringRequest {
- method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
- method @Nullable public android.net.LinkAddress getLocalIpv4Address();
- method public boolean getShouldShowEntitlementUi();
- method public int getTetheringType();
- method public boolean isExemptFromEntitlementCheck();
- }
-
- public static class TetheringManager.TetheringRequest.Builder {
- ctor public TetheringManager.TetheringRequest.Builder(int);
- method @NonNull public android.net.TetheringManager.TetheringRequest build();
- method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
- method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
- method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
- }
-
}
package android.os {
+ public class Binder implements android.os.IBinder {
+ method public final void markVintfStability();
+ }
+
public class StatsFrameworkInitializer {
method public static void registerServiceWrappers();
method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
diff --git a/api/removed.txt b/api/removed.txt
index 5a24f625d146..58dbeb8a7d3f 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,10 +1,6 @@
// Signature format: 2.0
package android.app {
- public class ActivityManager {
- method @Deprecated public static int getMaxNumPictureInPictureActions();
- }
-
public class Notification implements android.os.Parcelable {
method @Deprecated public String getChannel();
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 66cb53396982..11db781dd42f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2525,6 +2525,7 @@ package android.hardware.hdmi {
public final class HdmiControlManager {
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
method @Nullable public android.hardware.hdmi.HdmiClient getClient(int);
method @NonNull public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevices();
method public int getPhysicalAddress();
@@ -3673,8 +3674,13 @@ package android.hardware.usb {
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+ field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+ field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START";
+ field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+ field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
field public static final long FUNCTION_ADB = 1L; // 0x1L
field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
@@ -4166,10 +4172,6 @@ package android.media {
field public static final int ROLE_OUTPUT = 2; // 0x2
}
- public final class AudioDeviceInfo {
- field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
- }
-
public final class AudioFocusInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.media.AudioAttributes getAttributes();
@@ -7047,6 +7049,7 @@ package android.net.wifi {
method public boolean areFeaturesSupported(long);
method public int describeContents();
method public int getMaxSupportedClients();
+ method @NonNull public int[] getSupportedChannelList(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR;
field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L
@@ -12243,7 +12246,6 @@ package android.telephony.ims.feature {
ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
ctor public MmTelFeature.MmTelCapabilities(int);
method public final void addCapabilities(int);
- method public final boolean isCapable(int);
method public final void removeCapabilities(int);
}
@@ -12805,6 +12807,7 @@ package android.webkit {
method public void super_computeScroll();
method public boolean super_dispatchKeyEvent(android.view.KeyEvent);
method public int super_getScrollBarStyle();
+ method @Nullable public android.view.WindowInsets super_onApplyWindowInsets(@Nullable android.view.WindowInsets);
method public void super_onDrawVerticalScrollBar(android.graphics.Canvas, android.graphics.drawable.Drawable, int, int, int, int);
method public boolean super_onGenericMotionEvent(android.view.MotionEvent);
method public boolean super_onHoverEvent(android.view.MotionEvent);
@@ -13007,6 +13010,7 @@ package android.webkit {
method public android.os.Handler getHandler(android.os.Handler);
method public default boolean isVisibleToUserForAutofill(int);
method public void onActivityResult(int, int, android.content.Intent);
+ method @Nullable public default android.view.WindowInsets onApplyWindowInsets(@Nullable android.view.WindowInsets);
method public void onAttachedToWindow();
method public default boolean onCheckIsTextEditor();
method public void onConfigurationChanged(android.content.res.Configuration);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 09544c11f8b7..3acc22528438 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -180,11 +180,6 @@ package android.telecom {
package android.telephony {
- public final class PreciseDataConnectionState implements android.os.Parcelable {
- method @Deprecated @Nullable public android.net.LinkProperties getDataConnectionLinkProperties();
- method @Deprecated public int getDataConnectionNetworkType();
- }
-
public class TelephonyManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void answerRingingCall();
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public boolean endCall();
diff --git a/api/test-current.txt b/api/test-current.txt
index 8ae5866b66de..f1a3566f7b45 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20,6 +20,7 @@ package android {
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
@@ -35,6 +36,7 @@ package android {
public static final class R.bool {
field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
+ field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006
}
public static final class R.string {
@@ -82,6 +84,7 @@ package android.app {
method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
+ method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
@@ -144,6 +147,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void stopSystemLockTaskMode();
method public static boolean supportsMultiWindow(android.content.Context);
method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
+ field public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440; // 0x1b8
field public static final int INVALID_STACK_ID = -1; // 0xffffffff
field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
@@ -3290,6 +3294,7 @@ package android.provider {
field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
field public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
+ field public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
}
@@ -3298,6 +3303,7 @@ package android.provider {
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
field public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
field public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
+ field public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
field public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
field public static final String AUTOFILL_SERVICE = "autofill_service";
field public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -3318,6 +3324,7 @@ package android.provider {
field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
+ field public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option";
field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
@@ -4695,7 +4702,6 @@ package android.telephony.ims.feature {
ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
ctor public MmTelFeature.MmTelCapabilities(int);
method public final void addCapabilities(int);
- method public final boolean isCapable(int);
method public final void removeCapabilities(int);
}
@@ -5002,7 +5008,6 @@ package android.util {
field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
- field public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
}
@@ -5146,7 +5151,7 @@ package android.view {
}
public final class SurfaceControl implements android.os.Parcelable {
- ctor public SurfaceControl(@NonNull android.view.SurfaceControl);
+ ctor public SurfaceControl(@NonNull android.view.SurfaceControl, @NonNull String);
method public static long acquireFrameRateFlexibilityToken();
method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
method public static void releaseFrameRateFlexibilityToken(long);
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 73d9c2268a35..63ba4aa16662 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -73,6 +73,12 @@ ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_TEMP_LIST:
ArrayReturn: android.app.UiAutomation#executeShellCommandRw(String):
+ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[]) parameter #3:
+ Method parameter should be Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int) parameter #3:
+ Method parameter should be Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#getKeyphrases():
+ Method should return Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
ArrayReturn: android.location.GnssMeasurementsEvent#GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]) parameter #1:
ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
@@ -116,7 +122,7 @@ ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQue
ArrayReturn: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0:
ArrayReturn: android.view.Display#getSupportedWideColorGamut():
- Method should return Collection<ColorSpace> (or subclass) instead of raw array; was `android.graphics.ColorSpace[]`
+
ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
@@ -211,6 +217,36 @@ BannedThrow: android.os.Process#getThreadScheduler(int):
+BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#allowPrivilegedPlaybackCapture(boolean):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.allowPrivilegedPlaybackCapture(boolean)
+BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeMixRule(int, Object):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.excludeMixRule(int,Object)
+BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeRule(android.media.AudioAttributes, int):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.excludeRule(android.media.AudioAttributes,int)
+BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeCapability(int):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.NetworkCapabilities.Builder.removeCapability(int)
+BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeTransportType(int):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.NetworkCapabilities.Builder.removeTransportType(int)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateDnsslLifetime(long):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateDnsslLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixPreferredLifetime(long):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updatePrefixPreferredLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixValidLifetime(long):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updatePrefixValidLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRdnssLifetime(long):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRdnssLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouteInfoLifetime(long):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRouteInfoLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouterLifetime(long):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRouterLifetime(long)
+BuilderSetStyle: android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc():
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.ThreadPolicy.Builder.detectExplicitGc()
+BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse():
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.detectIncorrectContextUse()
+BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#permitIncorrectContextUse():
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.permitIncorrectContextUse()
+
+
CallbackInterface: android.app.prediction.AppPredictor.Callback:
CallbackInterface: android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback:
@@ -345,8 +381,12 @@ ExecutorRegistration: android.os.IncidentManager#requestAuthorization(int, Strin
ExecutorRegistration: android.os.RemoteCallback#RemoteCallback(android.os.RemoteCallback.OnResultListener, android.os.Handler):
+ExecutorRegistration: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
+ Registration methods should have overload that accepts delivery Executor: `countPermissionApps`
ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
+ExecutorRegistration: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
+ Registration methods should have overload that accepts delivery Executor: `setCallback`
ExecutorRegistration: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener):
ExecutorRegistration: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener):
@@ -365,6 +405,8 @@ ExecutorRegistration: android.telephony.mbms.vendor.MbmsStreamingServiceBase#ini
ExecutorRegistration: android.telephony.mbms.vendor.MbmsStreamingServiceBase#startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback):
+ExecutorRegistration: android.window.WindowOrganizer#applySyncTransaction(android.window.WindowContainerTransaction, android.window.WindowContainerTransactionCallback):
+ Registration methods should have overload that accepts delivery Executor: `applySyncTransaction`
ForbiddenSuperClass: android.app.AppDetailsActivity:
@@ -385,6 +427,12 @@ GenericException: android.service.autofill.augmented.FillWindow#finalize():
+GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByCategory():
+ Getter should be on the built object, not the builder: method android.hardware.display.BrightnessConfiguration.Builder.getMaxCorrectionsByCategory()
+GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByPackageName():
+ Getter should be on the built object, not the builder: method android.hardware.display.BrightnessConfiguration.Builder.getMaxCorrectionsByPackageName()
+
+
GetterSetterNames: android.app.NotificationChannel#isBlockableSystem():
GetterSetterNames: android.app.NotificationChannel#isImportanceLockedByCriticalDeviceFunction():
@@ -461,6 +509,8 @@ IntentBuilderName: android.app.backup.BackupManager#getConfigurationIntent(Strin
IntentBuilderName: android.app.backup.BackupManager#getDataManagementIntent(String):
+IntentBuilderName: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale):
+ Methods creating an Intent should be named `create<Foo>Intent()`, was `getManageKeyphraseIntent`
IntentName: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
@@ -509,6 +559,8 @@ ListenerLast: android.hardware.camera2.CameraDevice#createCustomCaptureSession(a
ListenerLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper) parameter #2:
+ListenerLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler) parameter #3:
+ Listeners should always be at end of argument list (method `countPermissionApps`)
ListenerLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler) parameter #2:
ListenerLast: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) parameter #1:
@@ -541,6 +593,142 @@ MinMaxConstant: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setAttributionTag(String):
+ android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getAttributionTag()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setAttributionTag(String)
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setFlags(int):
+ android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getFlags()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setFlags(int)
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setOpNames(java.util.List<java.lang.String>):
+ android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getOpNames()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setOpNames(java.util.List<java.lang.String>)
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setPackageName(String):
+ android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getPackageName()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setPackageName(String)
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setUid(int):
+ android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getUid()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setUid(int)
+MissingGetterMatchingBuilder: android.content.integrity.RuleSet.Builder#addRules(java.util.List<android.content.integrity.Rule>):
+ android.content.integrity.RuleSet does not declare a `getRuless()` method matching method android.content.integrity.RuleSet.Builder.addRules(java.util.List<android.content.integrity.Rule>)
+MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection):
+ android.hardware.display.BrightnessConfiguration does not declare a `getCorrectionByCategorys()` method matching method android.hardware.display.BrightnessConfiguration.Builder.addCorrectionByCategory(int,android.hardware.display.BrightnessCorrection)
+MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection):
+ android.hardware.display.BrightnessConfiguration does not declare a `getCorrectionByPackageNames()` method matching method android.hardware.display.BrightnessConfiguration.Builder.addCorrectionByPackageName(String,android.hardware.display.BrightnessCorrection)
+MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#setDescription(String):
+ android.hardware.display.BrightnessConfiguration does not declare a `getDescription()` method matching method android.hardware.display.BrightnessConfiguration.Builder.setDescription(String)
+MissingGetterMatchingBuilder: android.hardware.lights.LightsRequest.Builder#setLight(android.hardware.lights.Light, android.hardware.lights.LightState):
+ android.hardware.lights.LightsRequest does not declare a `getLight()` method matching method android.hardware.lights.LightsRequest.Builder.setLight(android.hardware.lights.Light,android.hardware.lights.LightState)
+MissingGetterMatchingBuilder: android.media.VolumeShaper.Configuration.Builder#setOptionFlags(int):
+ android.media.VolumeShaper.Configuration does not declare a `getOptionFlags()` method matching method android.media.VolumeShaper.Configuration.Builder.setOptionFlags(int)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setDevice(android.media.AudioDeviceInfo):
+ android.media.audiopolicy.AudioMix does not declare a `getDevice()` method matching method android.media.audiopolicy.AudioMix.Builder.setDevice(android.media.AudioDeviceInfo)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setFormat(android.media.AudioFormat):
+ android.media.audiopolicy.AudioMix does not declare a `getFormat()` method matching method android.media.audiopolicy.AudioMix.Builder.setFormat(android.media.AudioFormat)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setRouteFlags(int):
+ android.media.audiopolicy.AudioMix does not declare a `getRouteFlags()` method matching method android.media.audiopolicy.AudioMix.Builder.setRouteFlags(int)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addMixRule(int, Object):
+ android.media.audiopolicy.AudioMixingRule does not declare a `getMixRules()` method matching method android.media.audiopolicy.AudioMixingRule.Builder.addMixRule(int,Object)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addRule(android.media.AudioAttributes, int):
+ android.media.audiopolicy.AudioMixingRule does not declare a `getRules()` method matching method android.media.audiopolicy.AudioMixingRule.Builder.addRule(android.media.AudioAttributes,int)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#addMix(android.media.audiopolicy.AudioMix):
+ android.media.audiopolicy.AudioPolicy does not declare a `getMixs()` method matching method android.media.audiopolicy.AudioPolicy.Builder.addMix(android.media.audiopolicy.AudioMix)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener):
+ android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyFocusListener()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener):
+ android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyStatusListener()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback):
+ android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyVolumeCallback()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsAudioFocusPolicy(boolean):
+ android.media.audiopolicy.AudioPolicy does not declare a `isIsAudioFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsAudioFocusPolicy(boolean)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsTestFocusPolicy(boolean):
+ android.media.audiopolicy.AudioPolicy does not declare a `isIsTestFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsTestFocusPolicy(boolean)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setLooper(android.os.Looper):
+ android.media.audiopolicy.AudioPolicy does not declare a `getLooper()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setLooper(android.os.Looper)
+MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setBytesRemaining(long):
+ android.net.CaptivePortalData does not declare a `getBytesRemaining()` method matching method android.net.CaptivePortalData.Builder.setBytesRemaining(long)
+MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setExpiryTime(long):
+ android.net.CaptivePortalData does not declare a `getExpiryTime()` method matching method android.net.CaptivePortalData.Builder.setExpiryTime(long)
+MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setRefreshTime(long):
+ android.net.CaptivePortalData does not declare a `getRefreshTime()` method matching method android.net.CaptivePortalData.Builder.setRefreshTime(long)
+MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#addCapability(int):
+ android.net.NetworkCapabilities does not declare a `getCapabilitys()` method matching method android.net.NetworkCapabilities.Builder.addCapability(int)
+MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorPackageName(String):
+ android.net.NetworkCapabilities does not declare a `getRequestorPackageName()` method matching method android.net.NetworkCapabilities.Builder.setRequestorPackageName(String)
+MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorUid(int):
+ android.net.NetworkCapabilities does not declare a `getRequestorUid()` method matching method android.net.NetworkCapabilities.Builder.setRequestorUid(int)
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setShouldShowEntitlementUi(boolean):
+ android.net.TetheringManager.TetheringRequest does not declare a `shouldShowEntitlementUi()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setShouldShowEntitlementUi(boolean)
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setStaticIpv4Addresses(android.net.LinkAddress, android.net.LinkAddress):
+ android.net.TetheringManager.TetheringRequest does not declare a `getStaticIpv4Addresses()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setStaticIpv4Addresses(android.net.LinkAddress,android.net.LinkAddress)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setActualLifetime(long):
+ android.net.metrics.ApfProgramEvent does not declare a `getActualLifetime()` method matching method android.net.metrics.ApfProgramEvent.Builder.setActualLifetime(long)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setCurrentRas(int):
+ android.net.metrics.ApfProgramEvent does not declare a `getCurrentRas()` method matching method android.net.metrics.ApfProgramEvent.Builder.setCurrentRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFilteredRas(int):
+ android.net.metrics.ApfProgramEvent does not declare a `getFilteredRas()` method matching method android.net.metrics.ApfProgramEvent.Builder.setFilteredRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFlags(boolean, boolean):
+ android.net.metrics.ApfProgramEvent does not declare a `isFlags()` method matching method android.net.metrics.ApfProgramEvent.Builder.setFlags(boolean,boolean)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setLifetime(long):
+ android.net.metrics.ApfProgramEvent does not declare a `getLifetime()` method matching method android.net.metrics.ApfProgramEvent.Builder.setLifetime(long)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setProgramLength(int):
+ android.net.metrics.ApfProgramEvent does not declare a `getProgramLength()` method matching method android.net.metrics.ApfProgramEvent.Builder.setProgramLength(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDroppedRas(int):
+ android.net.metrics.ApfStats does not declare a `getDroppedRas()` method matching method android.net.metrics.ApfStats.Builder.setDroppedRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDurationMs(long):
+ android.net.metrics.ApfStats does not declare a `getDurationMs()` method matching method android.net.metrics.ApfStats.Builder.setDurationMs(long)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMatchingRas(int):
+ android.net.metrics.ApfStats does not declare a `getMatchingRas()` method matching method android.net.metrics.ApfStats.Builder.setMatchingRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMaxProgramSize(int):
+ android.net.metrics.ApfStats does not declare a `getMaxProgramSize()` method matching method android.net.metrics.ApfStats.Builder.setMaxProgramSize(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setParseErrors(int):
+ android.net.metrics.ApfStats does not declare a `getParseErrors()` method matching method android.net.metrics.ApfStats.Builder.setParseErrors(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdates(int):
+ android.net.metrics.ApfStats does not declare a `getProgramUpdates()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdates(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAll(int):
+ android.net.metrics.ApfStats does not declare a `getProgramUpdatesAll()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdatesAll(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAllowingMulticast(int):
+ android.net.metrics.ApfStats does not declare a `getProgramUpdatesAllowingMulticast()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdatesAllowingMulticast(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setReceivedRas(int):
+ android.net.metrics.ApfStats does not declare a `getReceivedRas()` method matching method android.net.metrics.ApfStats.Builder.setReceivedRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setZeroLifetimeRas(int):
+ android.net.metrics.ApfStats does not declare a `getZeroLifetimeRas()` method matching method android.net.metrics.ApfStats.Builder.setZeroLifetimeRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setDurationMs(int):
+ android.net.metrics.DhcpClientEvent does not declare a `getDurationMs()` method matching method android.net.metrics.DhcpClientEvent.Builder.setDurationMs(int)
+MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setMsg(String):
+ android.net.metrics.DhcpClientEvent does not declare a `getMsg()` method matching method android.net.metrics.DhcpClientEvent.Builder.setMsg(String)
+MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setDurationMs(long):
+ android.net.metrics.ValidationProbeEvent does not declare a `getDurationMs()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setDurationMs(long)
+MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setProbeType(int, boolean):
+ android.net.metrics.ValidationProbeEvent does not declare a `getProbeType()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setProbeType(int,boolean)
+MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setReturnCode(int):
+ android.net.metrics.ValidationProbeEvent does not declare a `getReturnCode()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setReturnCode(int)
+MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUniqueIdIncluded(boolean):
+ android.security.keystore.KeyGenParameterSpec does not declare a `isUniqueIdIncluded()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUniqueIdIncluded(boolean)
+MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation):
+ android.service.autofill.Dataset does not declare a `getFieldInlinePresentation()` method matching method android.service.autofill.Dataset.Builder.setFieldInlinePresentation(android.view.autofill.AutofillId,android.view.autofill.AutofillValue,java.util.regex.Pattern,android.service.autofill.InlinePresentation)
+MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setClientState(android.os.Bundle):
+ android.service.autofill.augmented.FillResponse does not declare a `getClientState()` method matching method android.service.autofill.augmented.FillResponse.Builder.setClientState(android.os.Bundle)
+MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setFillWindow(android.service.autofill.augmented.FillWindow):
+ android.service.autofill.augmented.FillResponse does not declare a `getFillWindow()` method matching method android.service.autofill.augmented.FillResponse.Builder.setFillWindow(android.service.autofill.augmented.FillWindow)
+MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setInlineSuggestions(java.util.List<android.service.autofill.Dataset>):
+ android.service.autofill.augmented.FillResponse does not declare a `getInlineSuggestions()` method matching method android.service.autofill.augmented.FillResponse.Builder.setInlineSuggestions(java.util.List<android.service.autofill.Dataset>)
+MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
+ android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
+MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setIsAdhocConferenceCall(boolean):
+ android.telecom.ConnectionRequest does not declare a `isIsAdhocConferenceCall()` method matching method android.telecom.ConnectionRequest.Builder.setIsAdhocConferenceCall(boolean)
+MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeFromInCall(android.os.ParcelFileDescriptor):
+ android.telecom.ConnectionRequest does not declare a `getRttPipeFromInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeFromInCall(android.os.ParcelFileDescriptor)
+MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeToInCall(android.os.ParcelFileDescriptor):
+ android.telecom.ConnectionRequest does not declare a `getRttPipeToInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeToInCall(android.os.ParcelFileDescriptor)
+MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setShouldShowIncomingCallUi(boolean):
+ android.telecom.ConnectionRequest does not declare a `shouldShowIncomingCallUi()` method matching method android.telecom.ConnectionRequest.Builder.setShouldShowIncomingCallUi(boolean)
+MissingGetterMatchingBuilder: android.telecom.PhoneAccount.Builder#setGroupId(String):
+ android.telecom.PhoneAccount does not declare a `getGroupId()` method matching method android.telecom.PhoneAccount.Builder.setGroupId(String)
+MissingGetterMatchingBuilder: android.telephony.NetworkRegistrationInfo.Builder#setEmergencyOnly(boolean):
+ android.telephony.NetworkRegistrationInfo does not declare a `isEmergencyOnly()` method matching method android.telephony.NetworkRegistrationInfo.Builder.setEmergencyOnly(boolean)
+MissingGetterMatchingBuilder: android.telephony.ims.ImsSsData.Builder#setCallForwardingInfo(java.util.List<android.telephony.ims.ImsCallForwardInfo>):
+ android.telephony.ims.ImsSsData does not declare a `getCallForwardingInfo()` method matching method android.telephony.ims.ImsSsData.Builder.setCallForwardingInfo(java.util.List<android.telephony.ims.ImsCallForwardInfo>)
+MissingGetterMatchingBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int):
+ android.telephony.ims.stub.ImsFeatureConfiguration does not declare a `getFeatures()` method matching method android.telephony.ims.stub.ImsFeatureConfiguration.Builder.addFeature(int,int)
+MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
+ android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
+
+
MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1:
MissingNullability: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int) parameter #0:
@@ -2294,11 +2482,11 @@ NoClone: android.service.autofill.augmented.AugmentedAutofillService#dump(java.i
NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
NoClone: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0:
-
+
NoSettingsProvider: android.provider.Settings.Global#APP_OPS_CONSTANTS:
- New setting keys are not allowed (Field: APP_OPS_CONSTANTS); use getters/setters in relevant manager class
+
NoSettingsProvider: android.provider.Settings.Global#AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
NoSettingsProvider: android.provider.Settings.Global#AUTOMATIC_POWER_SAVE_MODE:
@@ -2312,7 +2500,7 @@ NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_ENABL
NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS:
NoSettingsProvider: android.provider.Settings.Global#HIDE_ERROR_DIALOGS:
- New setting keys are not allowed (Field: HIDE_ERROR_DIALOGS); use getters/setters in relevant manager class
+
NoSettingsProvider: android.provider.Settings.Global#LOCATION_GLOBAL_KILL_SWITCH:
NoSettingsProvider: android.provider.Settings.Global#LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST:
@@ -2356,21 +2544,21 @@ NoSettingsProvider: android.provider.Settings.Secure#DOZE_ALWAYS_ON:
NoSettingsProvider: android.provider.Settings.Secure#ENABLED_VR_LISTENERS:
NoSettingsProvider: android.provider.Settings.Secure#IMMERSIVE_MODE_CONFIRMATIONS:
- New setting keys are not allowed (Field: IMMERSIVE_MODE_CONFIRMATIONS); use getters/setters in relevant manager class
+
NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_DELAY_MILLIS:
NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_INTERVAL_MILLIS:
NoSettingsProvider: android.provider.Settings.Secure#LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS:
- New setting keys are not allowed (Field: LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); use getters/setters in relevant manager class
+
NoSettingsProvider: android.provider.Settings.Secure#LOCK_SCREEN_SHOW_NOTIFICATIONS:
- New setting keys are not allowed (Field: LOCK_SCREEN_SHOW_NOTIFICATIONS); use getters/setters in relevant manager class
+
NoSettingsProvider: android.provider.Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT:
- New setting keys are not allowed (Field: NFC_PAYMENT_DEFAULT_COMPONENT); use getters/setters in relevant manager class
+
NoSettingsProvider: android.provider.Settings.Secure#NOTIFICATION_BADGING:
NoSettingsProvider: android.provider.Settings.Secure#POWER_MENU_LOCKED_SHOW_CONTENT:
- New setting keys are not allowed (Field: POWER_MENU_LOCKED_SHOW_CONTENT); use getters/setters in relevant manager class
+
NoSettingsProvider: android.provider.Settings.Secure#SYNC_PARENT_SOUNDS:
NoSettingsProvider: android.provider.Settings.Secure#USER_SETUP_COMPLETE:
@@ -2384,7 +2572,7 @@ NotCloseable: android.app.ActivityView:
NotCloseable: android.app.prediction.AppPredictor:
NotCloseable: android.net.EthernetManager.TetheredInterfaceRequest:
- Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.net.EthernetManager.TetheredInterfaceRequest
+
NotCloseable: android.os.HwParcel:
NotCloseable: android.telephony.ims.stub.ImsUtImplBase:
@@ -2401,6 +2589,8 @@ OnNameExpected: android.service.notification.NotificationAssistantService#attach
OnNameExpected: android.service.quicksettings.TileService#isQuickSettingsSupported():
+OnNameExpected: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
+ If implemented by developer, should follow the on<Something> style; otherwise consider marking final
OnNameExpected: android.telephony.ims.ImsService#createMmTelFeature(int):
OnNameExpected: android.telephony.ims.ImsService#createRcsFeature(int):
@@ -2429,6 +2619,14 @@ OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#updateGro
+OptionalBuilderConstructorArgument: android.app.prediction.AppTargetEvent.Builder#Builder(android.app.prediction.AppTarget, int) parameter #0:
+ Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter target in android.app.prediction.AppTargetEvent.Builder(android.app.prediction.AppTarget target, int actionType)
+OptionalBuilderConstructorArgument: android.net.CaptivePortalData.Builder#Builder(android.net.CaptivePortalData) parameter #0:
+ Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter data in android.net.CaptivePortalData.Builder(android.net.CaptivePortalData data)
+OptionalBuilderConstructorArgument: android.os.VibrationAttributes.Builder#Builder(android.media.AudioAttributes, android.os.VibrationEffect) parameter #1:
+ Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter effect in android.os.VibrationAttributes.Builder(android.media.AudioAttributes audio, android.os.VibrationEffect effect)
+
+
PackageLayering: android.util.FeatureFlagUtils:
@@ -2625,6 +2823,8 @@ SamShouldBeLast: android.os.IHwBinder#linkToDeath(android.os.IHwBinder.DeathReci
SamShouldBeLast: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String):
+SamShouldBeLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
+ SAM-compatible parameters (such as parameter 3, "callback", in android.permission.PermissionControllerManager.countPermissionApps) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
SamShouldBeLast: android.permission.PermissionControllerManager#revokeRuntimePermissions(java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback):
@@ -2669,6 +2869,24 @@ SetterReturnsThis: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyS
+StaticFinalBuilder: android.content.integrity.RuleSet.Builder:
+ Builder must be final: android.content.integrity.RuleSet.Builder
+StaticFinalBuilder: android.hardware.display.BrightnessConfiguration.Builder:
+ Builder must be final: android.hardware.display.BrightnessConfiguration.Builder
+StaticFinalBuilder: android.media.audiopolicy.AudioMix.Builder:
+ Builder must be final: android.media.audiopolicy.AudioMix.Builder
+StaticFinalBuilder: android.media.audiopolicy.AudioMixingRule.Builder:
+ Builder must be final: android.media.audiopolicy.AudioMixingRule.Builder
+StaticFinalBuilder: android.media.audiopolicy.AudioPolicy.Builder:
+ Builder must be final: android.media.audiopolicy.AudioPolicy.Builder
+StaticFinalBuilder: android.net.CaptivePortalData.Builder:
+ Builder must be final: android.net.CaptivePortalData.Builder
+StaticFinalBuilder: android.net.TetheringManager.TetheringRequest.Builder:
+ Builder must be final: android.net.TetheringManager.TetheringRequest.Builder
+StaticFinalBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder:
+ Builder must be final: android.telephony.ims.stub.ImsFeatureConfiguration.Builder
+
+
StaticUtils: android.os.health.HealthKeys:
StaticUtils: android.service.autofill.InternalTransformation:
@@ -2693,6 +2911,18 @@ StreamFiles: android.provider.MediaStore#scanVolume(android.content.Context, jav
+UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getKeyphraseMetadata(String, java.util.Locale) parameter #1:
+ Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale) parameter #2:
+ Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+UseIcu: android.hardware.soundtrigger.KeyphraseMetadata#supportsLocale(java.util.Locale) parameter #0:
+ Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#Keyphrase(int, int, java.util.Locale, String, int[]) parameter #2:
+ Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#getLocale():
+ Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
+
UseParcelFileDescriptor: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0:
@@ -2720,7 +2950,7 @@ UserHandle: android.app.usage.StorageStatsManager#queryCratesForUser(java.util.U
UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociatedForWifiConnection(String, android.net.MacAddress, android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+
UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle):
UserHandle: android.content.pm.PackageManager#getPermissionFlags(String, String, android.os.UserHandle):
@@ -2730,7 +2960,7 @@ UserHandle: android.content.pm.PackageManager#grantRuntimePermission(String, Str
UserHandle: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle):
UserHandle: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle, String):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+
UserHandle: android.content.pm.PackageManager#updatePermissionFlags(String, String, int, int, android.os.UserHandle):
UserHandle: android.location.LocationManager#setLocationEnabledForUser(boolean, android.os.UserHandle):
@@ -2742,7 +2972,7 @@ UserHandle: android.permission.PermissionControllerManager#getRuntimePermissionB
UserHandle: android.permission.PermissionControllerManager#stageAndApplyRuntimePermissionsBackup(byte[], android.os.UserHandle):
UserHandle: android.telecom.TelecomManager#getDefaultDialerPackage(android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+
UserHandleName: android.app.ActivityView#startActivity(android.content.Intent, android.os.UserHandle):
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 878cef94b674..e21a6b288fb3 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -181,7 +181,6 @@ cc_binary {
"idmap2/Dump.cpp",
"idmap2/Lookup.cpp",
"idmap2/Main.cpp",
- "idmap2/Scan.cpp",
],
target: {
android: {
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
index 69eea8d262d2..4099671066a2 100644
--- a/cmds/idmap2/idmap2/Commands.h
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -26,6 +26,5 @@ android::idmap2::Result<android::idmap2::Unit> Create(const std::vector<std::str
android::idmap2::Result<android::idmap2::Unit> CreateMultiple(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Dump(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Lookup(const std::vector<std::string>& args);
-android::idmap2::Result<android::idmap2::Unit> Scan(const std::vector<std::string>& args);
#endif // IDMAP2_IDMAP2_COMMANDS_H_
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index fb093f0f22a4..aa6d0e76698f 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -53,8 +53,10 @@ void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
int main(int argc, char** argv) {
SYSTRACE << "main";
const NameToFunctionMap commands = {
- {"create", Create}, {"create-multiple", CreateMultiple}, {"dump", Dump}, {"lookup", Lookup},
- {"scan", Scan},
+ {"create", Create},
+ {"create-multiple", CreateMultiple},
+ {"dump", Dump},
+ {"lookup", Lookup},
};
if (argc <= 1) {
PrintUsage(commands, std::cerr);
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
deleted file mode 100644
index 36250450cc74..000000000000
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ /dev/null
@@ -1,257 +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.
- */
-
-#include <dirent.h>
-
-#include <fstream>
-#include <memory>
-#include <ostream>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "Commands.h"
-#include "android-base/properties.h"
-#include "idmap2/CommandLineOptions.h"
-#include "idmap2/CommandUtils.h"
-#include "idmap2/FileUtils.h"
-#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
-#include "idmap2/PolicyUtils.h"
-#include "idmap2/ResourceUtils.h"
-#include "idmap2/Result.h"
-#include "idmap2/SysTrace.h"
-#include "idmap2/XmlParser.h"
-
-using android::idmap2::CommandLineOptions;
-using android::idmap2::Error;
-using android::idmap2::Idmap;
-using android::idmap2::Result;
-using android::idmap2::Unit;
-using android::idmap2::policy::kPolicyOdm;
-using android::idmap2::policy::kPolicyOem;
-using android::idmap2::policy::kPolicyProduct;
-using android::idmap2::policy::kPolicyPublic;
-using android::idmap2::policy::kPolicySystem;
-using android::idmap2::policy::kPolicyVendor;
-using android::idmap2::utils::ExtractOverlayManifestInfo;
-using android::idmap2::utils::FindFiles;
-using android::idmap2::utils::OverlayManifestInfo;
-using android::idmap2::utils::PoliciesToBitmaskResult;
-
-using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
-
-namespace {
-
-struct InputOverlay {
- bool operator<(InputOverlay const& rhs) const {
- return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path);
- }
-
- std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes)
- int priority; // NOLINT(misc-non-private-member-variables-in-classes)
- std::vector<std::string> policies; // NOLINT(misc-non-private-member-variables-in-classes)
- bool ignore_overlayable; // NOLINT(misc-non-private-member-variables-in-classes)
-};
-
-bool VendorIsQOrLater() {
- constexpr int kQSdkVersion = 29;
- constexpr int kBase = 10;
- std::string version_prop = android::base::GetProperty("ro.vndk.version", "29");
- int version = strtol(version_prop.data(), nullptr, kBase);
-
- // If the string cannot be parsed, it is a development sdk codename.
- return version >= kQSdkVersion || version == 0;
-}
-
-Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector<std::string>& dirs,
- bool recursive) {
- SYSTRACE << "FindApkFiles " << dirs << " " << recursive;
- const auto predicate = [](unsigned char type, const std::string& path) -> bool {
- static constexpr size_t kExtLen = 4; // strlen(".apk")
- return type == DT_REG && path.size() > kExtLen &&
- path.compare(path.size() - kExtLen, kExtLen, ".apk") == 0;
- };
- // pass apk paths through a set to filter out duplicates
- std::set<std::string> paths;
- for (const auto& dir : dirs) {
- const auto apk_paths = FindFiles(dir, recursive, predicate);
- if (!apk_paths) {
- return Error("failed to open directory %s", dir.c_str());
- }
- paths.insert(apk_paths->cbegin(), apk_paths->cend());
- }
- return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend());
-}
-
-std::vector<std::string> PoliciesForPath(const std::string& apk_path) {
- // clang-format off
- static const std::vector<std::pair<std::string, std::string>> values = {
- {"/odm/", kPolicyOdm},
- {"/oem/", kPolicyOem},
- {"/product/", kPolicyProduct},
- {"/system/", kPolicySystem},
- {"/system_ext/", kPolicySystem},
- {"/vendor/", kPolicyVendor},
- };
- // clang-format on
-
- std::vector<std::string> fulfilled_policies = {kPolicyPublic};
- for (auto const& pair : values) {
- if (apk_path.compare(0, pair.first.size(), pair.first) == 0) {
- fulfilled_policies.emplace_back(pair.second);
- break;
- }
- }
-
- return fulfilled_policies;
-}
-
-} // namespace
-
-Result<Unit> Scan(const std::vector<std::string>& args) {
- SYSTRACE << "Scan " << args;
- std::vector<std::string> input_directories;
- std::string target_package_name;
- std::string target_apk_path;
- std::string output_directory;
- std::vector<std::string> override_policies;
- bool recursive = false;
-
- const CommandLineOptions opts =
- CommandLineOptions("idmap2 scan")
- .MandatoryOption("--input-directory", "directory containing overlay apks to scan",
- &input_directories)
- .OptionalFlag("--recursive", "also scan subfolders of overlay-directory", &recursive)
- .MandatoryOption("--target-package-name", "package name of target package",
- &target_package_name)
- .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path)
- .MandatoryOption("--output-directory",
- "directory in which to write artifacts (idmap files and overlays.list)",
- &output_directory)
- .OptionalOption(
- "--override-policy",
- "input: an overlayable policy this overlay fulfills "
- "(if none or supplied, the overlays will not have their policies overriden",
- &override_policies);
- const auto opts_ok = opts.Parse(args);
- if (!opts_ok) {
- return opts_ok.GetError();
- }
-
- const auto apk_paths = FindApkFiles(input_directories, recursive);
- if (!apk_paths) {
- return Error(apk_paths.GetError(), "failed to find apk files");
- }
-
- std::vector<InputOverlay> interesting_apks;
- for (const std::string& path : **apk_paths) {
- Result<OverlayManifestInfo> overlay_info =
- ExtractOverlayManifestInfo(path, /* assert_overlay */ false);
- if (!overlay_info) {
- return overlay_info.GetError();
- }
-
- if (!overlay_info->is_static) {
- continue;
- }
-
- if (overlay_info->target_package.empty() ||
- overlay_info->target_package != target_package_name) {
- continue;
- }
-
- if (overlay_info->priority < 0) {
- continue;
- }
-
- // Note that conditional property enablement/exclusion only applies if
- // the attribute is present. In its absence, all overlays are presumed enabled.
- if (!overlay_info->requiredSystemPropertyName.empty() &&
- !overlay_info->requiredSystemPropertyValue.empty()) {
- // if property set & equal to value, then include overlay - otherwise skip
- if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "") !=
- overlay_info->requiredSystemPropertyValue) {
- continue;
- }
- }
-
- std::vector<std::string> fulfilled_policies;
- if (!override_policies.empty()) {
- fulfilled_policies = override_policies;
- } else {
- fulfilled_policies = PoliciesForPath(path);
- }
-
- bool ignore_overlayable = false;
- if (std::find(fulfilled_policies.begin(), fulfilled_policies.end(), kPolicyVendor) !=
- fulfilled_policies.end() &&
- !VendorIsQOrLater()) {
- // If the overlay is on a pre-Q vendor partition, do not enforce overlayable
- // restrictions on this overlay because the pre-Q platform has no understanding of
- // overlayable.
- ignore_overlayable = true;
- }
-
- std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path);
-
- // Sort the static overlays in ascending priority order
- InputOverlay input{path, idmap_path, overlay_info->priority, fulfilled_policies,
- ignore_overlayable};
- interesting_apks.insert(
- std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input);
- }
-
- std::stringstream stream;
- for (const auto& overlay : interesting_apks) {
- const auto policy_bitmask = PoliciesToBitmaskResult(overlay.policies);
- if (!policy_bitmask) {
- LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
- << "\": " << policy_bitmask.GetErrorMessage();
- continue;
- }
-
- if (!Verify(overlay.idmap_path, target_apk_path, overlay.apk_path, *policy_bitmask,
- !overlay.ignore_overlayable)) {
- std::vector<std::string> create_args = {"--target-apk-path", target_apk_path,
- "--overlay-apk-path", overlay.apk_path,
- "--idmap-path", overlay.idmap_path};
- if (overlay.ignore_overlayable) {
- create_args.emplace_back("--ignore-overlayable");
- }
-
- for (const std::string& policy : overlay.policies) {
- create_args.emplace_back("--policy");
- create_args.emplace_back(policy);
- }
-
- const auto create_ok = Create(create_args);
- if (!create_ok) {
- LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
- << "\": " << create_ok.GetError().GetMessage();
- continue;
- }
- }
-
- stream << overlay.idmap_path << std::endl;
- }
-
- std::cout << stream.str();
-
- return Unit{};
-}
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index 3f03236d5e1a..c4e0e1fd8ef0 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,27 +17,13 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
-#include <sys/types.h>
-
-#include <functional>
-#include <memory>
#include <string>
-#include <vector>
namespace android::idmap2::utils {
constexpr const char* kIdmapCacheDir = "/data/resource-cache";
constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r
-typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
- FindFilesPredicate;
-std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
- const FindFilesPredicate& predicate);
-
-std::unique_ptr<std::string> ReadFile(int fd);
-
-std::unique_ptr<std::string> ReadFile(const std::string& path);
-
bool UidHasWriteAccessToPath(uid_t uid, const std::string& path);
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 3e8e32989a09..3af1f70ebe39 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -16,19 +16,7 @@
#include "idmap2/FileUtils.h"
-#include <dirent.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cerrno>
-#include <climits>
-#include <cstdlib>
-#include <cstring>
-#include <fstream>
-#include <memory>
#include <string>
-#include <utility>
-#include <vector>
#include "android-base/file.h"
#include "android-base/macros.h"
@@ -37,54 +25,6 @@
namespace android::idmap2::utils {
-std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
- const FindFilesPredicate& predicate) {
- DIR* dir = opendir(root.c_str());
- if (dir == nullptr) {
- return nullptr;
- }
- std::unique_ptr<std::vector<std::string>> vector(new std::vector<std::string>());
- struct dirent* dirent;
- while ((dirent = readdir(dir)) != nullptr) {
- const std::string path = root + "/" + dirent->d_name;
- if (predicate(dirent->d_type, path)) {
- vector->push_back(path);
- }
- if (recurse && dirent->d_type == DT_DIR && strcmp(dirent->d_name, ".") != 0 &&
- strcmp(dirent->d_name, "..") != 0) {
- auto sub_vector = FindFiles(path, recurse, predicate);
- if (!sub_vector) {
- closedir(dir);
- return nullptr;
- }
- vector->insert(vector->end(), sub_vector->begin(), sub_vector->end());
- }
- }
- closedir(dir);
-
- return vector;
-}
-
-std::unique_ptr<std::string> ReadFile(const std::string& path) {
- std::unique_ptr<std::string> str(new std::string());
- std::ifstream fin(path);
- str->append({std::istreambuf_iterator<char>(fin), std::istreambuf_iterator<char>()});
- fin.close();
- return str;
-}
-
-std::unique_ptr<std::string> ReadFile(int fd) {
- static constexpr const size_t kBufSize = 1024;
-
- std::unique_ptr<std::string> str(new std::string());
- char buf[kBufSize];
- ssize_t r;
- while ((r = read(fd, buf, sizeof(buf))) > 0) {
- str->append(buf, r);
- }
- return r == 0 ? std::move(str) : nullptr;
-}
-
#ifdef __ANDROID__
bool UidHasWriteAccessToPath(uid_t uid, const std::string& path) {
// resolve symlinks and relative paths; the directories must exist
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index 8af4037be954..5750ca1f49c5 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -14,73 +14,16 @@
* limitations under the License.
*/
-#include <dirent.h>
-#include <fcntl.h>
-
-#include <set>
#include <string>
#include "TestHelpers.h"
-#include "android-base/macros.h"
#include "android-base/stringprintf.h"
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/FileUtils.h"
#include "private/android_filesystem_config.h"
-using ::testing::NotNull;
-
namespace android::idmap2::utils {
-TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
- const auto& root = GetTestDataPath();
- auto v = utils::FindFiles(root, false,
- [](unsigned char type ATTRIBUTE_UNUSED,
- const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
- ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 7U);
- ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({
- root + "/.",
- root + "/..",
- root + "/overlay",
- root + "/target",
- root + "/signature-overlay",
- root + "/system-overlay",
- root + "/system-overlay-invalid",
- }));
-}
-
-TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
- const auto& root = GetTestDataPath();
- auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool {
- return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
- });
- ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 11U);
- ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
- std::set<std::string>(
- {root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
- root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
- root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-shared.apk",
- root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk",
- root + "/signature-overlay/signature-overlay.apk",
- root + "/system-overlay/system-overlay.apk",
- root + "/system-overlay-invalid/system-overlay-invalid.apk"}));
-}
-
-TEST(FileUtilsTests, ReadFile) {
- int pipefd[2];
- ASSERT_EQ(pipe2(pipefd, O_CLOEXEC), 0);
-
- ASSERT_EQ(write(pipefd[1], "foobar", 6), 6);
- close(pipefd[1]);
-
- auto data = ReadFile(pipefd[0]);
- ASSERT_THAT(data, NotNull());
- ASSERT_EQ(*data, "foobar");
- close(pipefd[0]);
-}
-
#ifdef __ANDROID__
TEST(FileUtilsTests, UidHasWriteAccessToPath) {
constexpr const char* tmp_path = "/data/local/tmp/test@idmap";
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index d896cf9c11ba..61751b33dcba 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -159,131 +159,6 @@ TEST_F(Idmap2BinaryTests, Dump) {
unlink(GetIdmapPath().c_str());
}
-TEST_F(Idmap2BinaryTests, Scan) {
- SKIP_TEST_IF_CANT_EXEC_IDMAP2;
-
- const std::string overlay_static_no_name_apk_path =
- GetTestDataPath() + "/overlay/overlay-no-name-static.apk";
- const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
- const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
- const std::string idmap_static_no_name_path =
- Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_no_name_apk_path);
- const std::string idmap_static_1_path =
- Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path);
- const std::string idmap_static_2_path =
- Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_2_apk_path);
-
- // single input directory, recursive
- // clang-format off
- auto result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath(),
- "--recursive",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- std::stringstream expected;
- expected << idmap_static_no_name_path << std::endl;
- expected << idmap_static_1_path << std::endl;
- expected << idmap_static_2_path << std::endl;
- ASSERT_EQ(result->stdout, expected.str());
-
- auto idmap_static_no_name_raw_string = utils::ReadFile(idmap_static_no_name_path);
- auto idmap_static_no_name_raw_stream = std::istringstream(*idmap_static_no_name_raw_string);
- auto idmap_static_no_name = Idmap::FromBinaryStream(idmap_static_no_name_raw_stream);
- ASSERT_TRUE(idmap_static_no_name);
- ASSERT_IDMAP(**idmap_static_no_name, GetTargetApkPath(), overlay_static_no_name_apk_path);
-
- auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path);
- auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string);
- auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream);
- ASSERT_TRUE(idmap_static_1);
- ASSERT_IDMAP(**idmap_static_1, GetTargetApkPath(), overlay_static_1_apk_path);
-
- auto idmap_static_2_raw_string = utils::ReadFile(idmap_static_2_path);
- auto idmap_static_2_raw_stream = std::istringstream(*idmap_static_2_raw_string);
- auto idmap_static_2 = Idmap::FromBinaryStream(idmap_static_2_raw_stream);
- ASSERT_TRUE(idmap_static_2);
- ASSERT_IDMAP(**idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path);
-
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-
- // multiple input directories, non-recursive
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath() + "/target",
- "--input-directory", GetTestDataPath() + "/overlay",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, expected.str());
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-
- // the same input directory given twice, but no duplicate entries
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath(),
- "--input-directory", GetTestDataPath(),
- "--recursive",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, expected.str());
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-
- // no APKs in input-directory: ok, but no output
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTempDirPath(),
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, "");
-
- // the signature idmap failing to generate should not cause scanning to fail
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath(),
- "--recursive",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, expected.str());
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-}
-
TEST_F(Idmap2BinaryTests, Lookup) {
SKIP_TEST_IF_CANT_EXEC_IDMAP2;
diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh
index b4ebab0c7ffe..84daeecdb21e 100755
--- a/cmds/idmap2/valgrind.sh
+++ b/cmds/idmap2/valgrind.sh
@@ -53,7 +53,5 @@ valgrind="valgrind --error-exitcode=1 -q --track-origins=yes --leak-check=full"
_eval "idmap2 create" "$valgrind idmap2 create --policy public --target-apk-path $target_path --overlay-apk-path $overlay_path --idmap-path $idmap_path"
_eval "idmap2 dump" "$valgrind idmap2 dump --idmap-path $idmap_path"
_eval "idmap2 lookup" "$valgrind idmap2 lookup --idmap-path $idmap_path --config '' --resid test.target:string/str1"
-_eval "idmap2 scan" "$valgrind idmap2 scan --input-directory ${prefix}/tests/data/overlay --recursive --target-package-name test.target --target-apk-path $target_path --output-directory /tmp --override-policy public"
-_eval "idmap2 verify" "$valgrind idmap2 verify --idmap-path $idmap_path"
_eval "idmap2_tests" "$valgrind $ANDROID_HOST_OUT/nativetest64/idmap2_tests/idmap2_tests"
exit $errors
diff --git a/cmds/input/Android.bp b/cmds/input/Android.bp
index a0ebde63eb6e..1ee9dd355290 100644
--- a/cmds/input/Android.bp
+++ b/cmds/input/Android.bp
@@ -1,8 +1,7 @@
// Copyright 2008 The Android Open Source Project
//
-java_binary {
+sh_binary {
name: "input",
- wrapper: "input",
- srcs: ["**/*.java"],
+ src: "input",
}
diff --git a/cmds/input/input b/cmds/input/input
index 2625eba17153..d7d041431b49 100755
--- a/cmds/input/input
+++ b/cmds/input/input
@@ -1,3 +1,2 @@
#!/system/bin/sh
-export CLASSPATH=/system/framework/input.jar
-exec app_process /system/bin com.android.commands.input.Input "$@"
+cmd input "$@"
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
deleted file mode 100644
index 08216d9b3f1d..000000000000
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * 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.android.commands.input;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-
-import android.hardware.input.InputManager;
-import android.os.SystemClock;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import com.android.internal.os.BaseCommand;
-
-import java.io.PrintStream;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Command that sends key events to the device, either by their keycode, or by
- * desired character output.
- */
-
-public class Input extends BaseCommand {
- private static final String TAG = "Input";
- private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
- private static final String INVALID_DISPLAY_ARGUMENTS =
- "Error: Invalid arguments for display ID.";
-
- private static final float DEFAULT_PRESSURE = 1.0f;
- private static final float NO_PRESSURE = 0.0f;
-
- private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
- put("keyboard", InputDevice.SOURCE_KEYBOARD);
- put("dpad", InputDevice.SOURCE_DPAD);
- put("gamepad", InputDevice.SOURCE_GAMEPAD);
- put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN);
- put("mouse", InputDevice.SOURCE_MOUSE);
- put("stylus", InputDevice.SOURCE_STYLUS);
- put("trackball", InputDevice.SOURCE_TRACKBALL);
- put("touchpad", InputDevice.SOURCE_TOUCHPAD);
- put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION);
- put("joystick", InputDevice.SOURCE_JOYSTICK);
- }};
-
- private static final Map<String, InputCmd> COMMANDS = new HashMap<String, InputCmd>();
-
- /**
- * Command-line entry point.
- *
- * @param args The command-line arguments
- */
- public static void main(String[] args) {
- (new Input()).run(args);
- }
-
- Input() {
- COMMANDS.put("text", new InputText());
- COMMANDS.put("keyevent", new InputKeyEvent());
- COMMANDS.put("tap", new InputTap());
- COMMANDS.put("swipe", new InputSwipe());
- COMMANDS.put("draganddrop", new InputDragAndDrop());
- COMMANDS.put("press", new InputPress());
- COMMANDS.put("roll", new InputRoll());
- COMMANDS.put("motionevent", new InputMotionEvent());
- }
-
- @Override
- public void onRun() throws Exception {
- String arg = nextArgRequired();
- int inputSource = InputDevice.SOURCE_UNKNOWN;
-
- // Get source (optional).
- if (SOURCES.containsKey(arg)) {
- inputSource = SOURCES.get(arg);
- arg = nextArgRequired();
- }
-
- // Get displayId (optional).
- int displayId = INVALID_DISPLAY;
- if ("-d".equals(arg)) {
- displayId = getDisplayId();
- arg = nextArgRequired();
- }
-
- // Get command and run.
- InputCmd cmd = COMMANDS.get(arg);
- if (cmd != null) {
- try {
- cmd.run(inputSource, displayId);
- return;
- } catch (NumberFormatException ex) {
- throw new IllegalArgumentException(INVALID_ARGUMENTS + arg);
- }
- }
-
- throw new IllegalArgumentException("Error: Unknown command: " + arg);
- }
-
- private int getDisplayId() {
- String displayArg = nextArgRequired();
- if ("INVALID_DISPLAY".equalsIgnoreCase(displayArg)) {
- return INVALID_DISPLAY;
- } else if ("DEFAULT_DISPLAY".equalsIgnoreCase(displayArg)) {
- return DEFAULT_DISPLAY;
- } else {
- try {
- final int displayId = Integer.parseInt(displayArg);
- if (displayId == INVALID_DISPLAY) {
- return INVALID_DISPLAY;
- }
- return Math.max(displayId, 0);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException(INVALID_DISPLAY_ARGUMENTS);
- }
- }
- }
-
- class InputText implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
- sendText(inputSource, nextArgRequired(), displayId);
- }
-
- /**
- * Convert the characters of string text into key event's and send to
- * device.
- *
- * @param text is a string of characters you want to input to the device.
- */
- private void sendText(int source, final String text, int displayId) {
- final StringBuffer buff = new StringBuffer(text);
- boolean escapeFlag = false;
- for (int i = 0; i < buff.length(); i++) {
- if (escapeFlag) {
- escapeFlag = false;
- if (buff.charAt(i) == 's') {
- buff.setCharAt(i, ' ');
- buff.deleteCharAt(--i);
- }
- }
- if (buff.charAt(i) == '%') {
- escapeFlag = true;
- }
- }
-
- final char[] chars = buff.toString().toCharArray();
- final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
- final KeyEvent[] events = kcm.getEvents(chars);
- for (int i = 0; i < events.length; i++) {
- KeyEvent e = events[i];
- if (source != e.getSource()) {
- e.setSource(source);
- }
- e.setDisplayId(displayId);
- injectKeyEvent(e);
- }
- }
- }
-
- class InputKeyEvent implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- String arg = nextArgRequired();
- final boolean longpress = "--longpress".equals(arg);
- if (longpress) {
- arg = nextArgRequired();
- }
-
- do {
- final int keycode = KeyEvent.keyCodeFromString(arg);
- sendKeyEvent(inputSource, keycode, longpress, displayId);
- } while ((arg = nextArg()) != null);
- }
-
- private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
- final long now = SystemClock.uptimeMillis();
- int repeatCount = 0;
-
- KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
- 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
- inputSource);
- event.setDisplayId(displayId);
-
- injectKeyEvent(event);
- if (longpress) {
- repeatCount++;
- injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
- KeyEvent.FLAG_LONG_PRESS));
- }
- injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
- }
- }
-
- class InputTap implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- sendTap(inputSource, Float.parseFloat(nextArgRequired()),
- Float.parseFloat(nextArgRequired()), displayId);
- }
-
- void sendTap(int inputSource, float x, float y, int displayId) {
- final long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
- displayId);
- injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
- }
- }
-
- class InputPress extends InputTap {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
- sendTap(inputSource, 0.0f, 0.0f, displayId);
- }
- }
-
- class InputSwipe implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- sendSwipe(inputSource, displayId, false);
- }
-
- void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
- // Parse two points and duration.
- final float x1 = Float.parseFloat(nextArgRequired());
- final float y1 = Float.parseFloat(nextArgRequired());
- final float x2 = Float.parseFloat(nextArgRequired());
- final float y2 = Float.parseFloat(nextArgRequired());
- String durationArg = nextArg();
- int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
- if (duration < 0) {
- duration = 300;
- }
-
- final long down = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
- displayId);
- if (isDragDrop) {
- // long press until drag start.
- try {
- Thread.sleep(ViewConfiguration.getLongPressTimeout());
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- long now = SystemClock.uptimeMillis();
- final long endTime = down + duration;
- while (now < endTime) {
- final long elapsedTime = now - down;
- final float alpha = (float) elapsedTime / duration;
- injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
- lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
- now = SystemClock.uptimeMillis();
- }
- injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
- displayId);
- }
- }
-
- class InputDragAndDrop extends InputSwipe {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- sendSwipe(inputSource, displayId, true);
- }
- }
-
- class InputRoll implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
- sendMove(inputSource, Float.parseFloat(nextArgRequired()),
- Float.parseFloat(nextArgRequired()), displayId);
- }
-
- /**
- * Sends a simple zero-pressure move event.
- *
- * @param inputSource the InputDevice.SOURCE_* sending the input event
- * @param dx change in x coordinate due to move
- * @param dy change in y coordinate due to move
- */
- private void sendMove(int inputSource, float dx, float dy, int displayId) {
- final long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f,
- displayId);
- }
- }
-
- class InputMotionEvent implements InputCmd {
- @Override
- public void run(int inputSource, int displayId) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- sendMotionEvent(inputSource, nextArgRequired(), Float.parseFloat(nextArgRequired()),
- Float.parseFloat(nextArgRequired()), displayId);
- }
-
- private void sendMotionEvent(int inputSource, String motionEventType, float x, float y,
- int displayId) {
- final int action;
- final float pressure;
-
- switch (motionEventType.toUpperCase()) {
- case "DOWN":
- action = MotionEvent.ACTION_DOWN;
- pressure = DEFAULT_PRESSURE;
- break;
- case "UP":
- action = MotionEvent.ACTION_UP;
- pressure = NO_PRESSURE;
- break;
- case "MOVE":
- action = MotionEvent.ACTION_MOVE;
- pressure = DEFAULT_PRESSURE;
- break;
- default:
- throw new IllegalArgumentException("Unknown motionevent " + motionEventType);
- }
-
- final long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId);
- }
- }
-
- /**
- * Abstract class for command
- * use nextArgRequired or nextArg to check next argument if necessary.
- */
- private interface InputCmd {
- void run(int inputSource, int displayId);
- }
-
- private static void injectKeyEvent(KeyEvent event) {
- InputManager.getInstance().injectInputEvent(event,
- InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
- }
-
- private static int getInputDeviceId(int inputSource) {
- final int DEFAULT_DEVICE_ID = 0;
- int[] devIds = InputDevice.getDeviceIds();
- for (int devId : devIds) {
- InputDevice inputDev = InputDevice.getDevice(devId);
- if (inputDev.supportsSource(inputSource)) {
- return devId;
- }
- }
- return DEFAULT_DEVICE_ID;
- }
-
- /**
- * Builds a MotionEvent and injects it into the event stream.
- *
- * @param inputSource the InputDevice.SOURCE_* sending the input event
- * @param action the MotionEvent.ACTION_* for the event
- * @param downTime the value of the ACTION_DOWN event happened
- * @param when the value of SystemClock.uptimeMillis() at which the event happened
- * @param x x coordinate of event
- * @param y y coordinate of event
- * @param pressure pressure of event
- */
- private static void injectMotionEvent(int inputSource, int action, long downTime, long when,
- float x, float y, float pressure, int displayId) {
- final float DEFAULT_SIZE = 1.0f;
- final int DEFAULT_META_STATE = 0;
- final float DEFAULT_PRECISION_X = 1.0f;
- final float DEFAULT_PRECISION_Y = 1.0f;
- final int DEFAULT_EDGE_FLAGS = 0;
- MotionEvent event = MotionEvent.obtain(downTime, when, action, x, y, pressure, DEFAULT_SIZE,
- DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y,
- getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS);
- event.setSource(inputSource);
- if (displayId == INVALID_DISPLAY && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- displayId = DEFAULT_DISPLAY;
- }
- event.setDisplayId(displayId);
- InputManager.getInstance().injectInputEvent(event,
- InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
- }
-
- private static final float lerp(float a, float b, float alpha) {
- return (b - a) * alpha + a;
- }
-
- private static final int getSource(int inputSource, int defaultSource) {
- return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource;
- }
-
- @Override
- public void onShowUsage(PrintStream out) {
- out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]");
- out.println();
- out.println("The sources are: ");
- for (String src : SOURCES.keySet()) {
- out.println(" " + src);
- }
- out.println();
- out.printf("-d: specify the display ID.\n"
- + " (Default: %d for key event, %d for motion event if not specified.)",
- INVALID_DISPLAY, DEFAULT_DISPLAY);
- out.println();
- out.println("The commands and default sources are:");
- out.println(" text <string> (Default: touchscreen)");
- out.println(" keyevent [--longpress] <key code number or name> ..."
- + " (Default: keyboard)");
- out.println(" tap <x> <y> (Default: touchscreen)");
- out.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]"
- + " (Default: touchscreen)");
- out.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
- + " (Default: touchscreen)");
- out.println(" press (Default: trackball)");
- out.println(" roll <dx> <dy> (Default: trackball)");
- out.println(" motionevent <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
- }
-}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 0617eb6c0e66..124f815f51f0 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -216,10 +216,6 @@ cc_binary {
// address: true,
//},
},
- debuggable: {
- // Add a flag to enable stats log printing from statsd on debug builds.
- cflags: ["-DVERY_VERBOSE_PRINTING"],
- },
},
proto: {
diff --git a/cmds/statsd/TEST_MAPPING b/cmds/statsd/TEST_MAPPING
index 8dee073aca22..a7a4cf14182e 100644
--- a/cmds/statsd/TEST_MAPPING
+++ b/cmds/statsd/TEST_MAPPING
@@ -3,5 +3,15 @@
{
"name" : "statsd_test"
}
+ ],
+
+ "postsubmit" : [
+ {
+ "name" : "CtsStatsdHostTestCases"
+ },
+ {
+ "name" : "GtsStatsdHostTestCases"
+ }
]
-} \ No newline at end of file
+
+}
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index e7b32c56551a..05e9ec3a1769 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -409,11 +409,9 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
onWatchdogRollbackOccurredLocked(event);
}
-#ifdef VERY_VERBOSE_PRINTING
if (mPrintAllLogs) {
ALOGI("%s", event->ToString().c_str());
}
-#endif
resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
// Hard-coded logic to update the isolated uid's in the uid-map.
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 23f2584655b0..c0f54a0995ac 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -139,10 +139,8 @@ public:
int64_t getLastReportTimeNs(const ConfigKey& key);
inline void setPrintLogs(bool enabled) {
-#ifdef VERY_VERBOSE_PRINTING
std::lock_guard<std::mutex> lock(mMetricsMutex);
mPrintAllLogs = enabled;
-#endif
}
// Add a specific config key to the possible configs to dump ASAP.
@@ -276,9 +274,7 @@ private:
//Last time we wrote metadata to disk.
int64_t mLastMetadataWriteNs = 0;
-#ifdef VERY_VERBOSE_PRINTING
bool mPrintAllLogs = false;
-#endif
FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 322648229d0e..d5e331495164 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -484,7 +484,8 @@ void StatsService::print_cmd_help(int out) {
dprintf(out, " Clear cached puller data.\n");
dprintf(out, "\n");
dprintf(out, "usage: adb shell cmd stats print-logs\n");
- dprintf(out, " Only works on eng build\n");
+ dprintf(out, " Requires root privileges.\n");
+ dprintf(out, " Can be disabled by calling adb shell cmd stats print-logs 0\n");
}
status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
@@ -865,18 +866,19 @@ status_t StatsService::cmd_clear_puller_cache(int out) {
}
status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
- VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
- AIBinder_getCallingUid());
- if (checkPermission(kPermissionDump)) {
- bool enabled = true;
- if (args.size() >= 2) {
- enabled = atoi(args[1].c_str()) != 0;
- }
- mProcessor->setPrintLogs(enabled);
- return NO_ERROR;
- } else {
+ Status status = checkUid(AID_ROOT);
+ if (!status.isOk()) {
return PERMISSION_DENIED;
}
+
+ VLOG("StatsService::cmd_print_logs with pid %i, uid %i", AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ bool enabled = true;
+ if (args.size() >= 2) {
+ enabled = atoi(args[1].c_str()) != 0;
+ }
+ mProcessor->setPrintLogs(enabled);
+ return NO_ERROR;
}
bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index 5722f923d11e..6d9beb8f718d 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -60,11 +60,11 @@ void AlarmTracker::addSubscription(const Subscription& subscription) {
}
int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) {
- if (currentTimeSec <= mAlarmSec) {
+ if (currentTimeSec < mAlarmSec) {
return mAlarmSec;
}
int64_t periodsForward =
- ((currentTimeSec - mAlarmSec) * MS_PER_SEC - 1) / mAlarmConfig.period_millis() + 1;
+ ((currentTimeSec - mAlarmSec) * MS_PER_SEC) / mAlarmConfig.period_millis() + 1;
return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC;
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 258e4aae146d..e70eac88c769 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -583,10 +583,13 @@ message Atom {
SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
SettingSnapshot setting_snapshot = 10080 [(module) = "framework"];
BlobInfo blob_info = 10081 [(module) = "framework"];
- DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"];
+ DataUsageBytesTransfer data_usage_bytes_transfer =
+ 10082 [(module) = "framework", (truncate_timestamp) = true];
BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
- 10083 [(module) = "framework"];
+ 10083 [(module) = "framework", (truncate_timestamp) = true];
DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
+ GeneralExternalStorageAccessStats general_external_storage_access_stats =
+ 10085 [(module) = "mediaprovider"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -4566,6 +4569,31 @@ message VmsClientConnectionStateChanged {
optional State state = 2;
}
+message MimeTypes {
+ repeated string mime_types = 1;
+}
+
+/**
+ * Logs statistics regarding accesses to external storage.
+ * All stats are normalized for one day period.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message GeneralExternalStorageAccessStats {
+ optional int32 uid = 1 [(is_uid) = true];
+ // Total number of accesses like creation, open, delete and rename/update.
+ // Includes file path and ContentResolver accesses
+ optional uint32 total_accesses = 2;
+ // Number of file path accesses, as opposed to file path and ContentResolver.
+ optional uint32 file_path_accesses = 3;
+ // Number of accesses on secondary volumes like SD cards.
+ // Includes file path and ContentResolver accesses
+ optional uint32 secondary_storage_accesses = 4;
+ // Comma-separated list of mime types that were accessed.
+ optional MimeTypes mime_types_accessed = 5 [(log_mode) = MODE_BYTES];
+}
+
/**
* Logs when MediaProvider has successfully finished scanning a storage volume.
*
@@ -4961,6 +4989,8 @@ message BlobCommitted {
ERROR_DURING_COMMIT = 2;
// Commit Failed: Digest of the data did not match Blob digest
DIGEST_MISMATCH = 3;
+ // Commit Failed: Allowed count limit exceeded
+ COUNT_LIMIT_EXCEEDED = 4;
}
optional Result result = 4;
}
@@ -4993,6 +5023,8 @@ message BlobLeased{
LEASE_EXPIRY_INVALID = 4;
// Lease Failed: Leasee has exceeded the total data lease limit
DATA_SIZE_LIMIT_EXCEEDED = 5;
+ // Leasee Failed: Allowed count limit exceeded
+ COUNT_LIMIT_EXCEEDED = 6;
}
optional Result result = 4;
}
@@ -5160,6 +5192,12 @@ message DataUsageBytesTransfer {
// record is combined across opportunistic data subscriptions.
// See {@link SubscriptionManager#setOpportunistic}.
optional DataSubscriptionState opportunistic_data_sub = 10;
+
+ // Indicate whether NR is connected, server side could use this with RAT type to determine if
+ // the record is for 5G NSA (Non Stand Alone) mode, where the primary cell is still LTE and
+ // network allocates a secondary 5G cell so telephony reports RAT = LTE along with NR state as
+ // connected.
+ optional bool is_nr_connected = 11;
}
/**
@@ -6143,14 +6181,18 @@ message ProcessStatsAvailablePagesProto {
* Pulled from ProcessStatsService.java
*/
message ProcStats {
- optional ProcessStatsSectionProto proc_stats_section = 1;
+ optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES];
+ // Data pulled from device into this is sometimes sharded across multiple atoms to work around
+ // a size limit. When this happens, this shard ID will contain an increasing 1-indexed integer
+ // with the number of this shard.
+ optional int32 shard_id = 2;
}
/**
* Pulled from ProcessStatsService.java
*/
message ProcStatsPkgProc {
- optional ProcessStatsSectionProto proc_stats_section = 1;
+ optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES];
}
// Next Tag: 2
@@ -6168,7 +6210,7 @@ message NotificationRemoteViewsProto {
* Pulled from NotificationManagerService.java
*/
message NotificationRemoteViews {
- optional NotificationRemoteViewsProto notification_remote_views = 1;
+ optional NotificationRemoteViewsProto notification_remote_views = 1 [(log_mode) = MODE_BYTES];
}
/**
@@ -6236,7 +6278,7 @@ message DNDModeProto {
// May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
optional string id = 5;
optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other
- optional DNDPolicyProto policy = 7;
+ optional DNDPolicyProto policy = 7 [(log_mode) = MODE_BYTES];
}
/**
@@ -6401,7 +6443,7 @@ message PowerProfileProto {
* Pulled from PowerProfile.java
*/
message PowerProfile {
- optional PowerProfileProto power_profile = 1;
+ optional PowerProfileProto power_profile = 1 [(log_mode) = MODE_BYTES];
}
/**
@@ -8096,7 +8138,7 @@ message DeviceIdentifierAccessDenied {
message TrainInfo {
optional int64 train_version_code = 1;
- optional TrainExperimentIds train_experiment_id = 2;
+ optional TrainExperimentIds train_experiment_id = 2 [(log_mode) = MODE_BYTES];
optional string train_name = 3;
@@ -9703,6 +9745,7 @@ message RuntimeAppOpAccess {
UNIFORM = 1;
RARELY_USED = 2;
BOOT_TIME_SAMPLING = 3;
+ UNIFORM_OPS = 4;
}
// sampling strategy used to collect this message
@@ -11143,8 +11186,8 @@ message BlobInfo {
optional int64 expiry_timestamp_millis = 3;
// List of committers of this Blob
- optional BlobCommitterListProto committers = 4;
+ optional BlobCommitterListProto committers = 4 [(log_mode) = MODE_BYTES];
// List of leasees of this Blob
- optional BlobLeaseeListProto leasees = 5;
+ optional BlobLeaseeListProto leasees = 5 [(log_mode) = MODE_BYTES];
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8a9ec7456e55..46c377037542 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -92,63 +92,43 @@ StatsPullerManager::StatsPullerManager()
}
bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ vector<shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, configKey, eventTimeNs, data, useUids);
+ return PullLocked(tagId, configKey, eventTimeNs, data);
}
bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids) {
+ vector<std::shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+ return PullLocked(tagId, uids, eventTimeNs, data);
}
bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
- const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
- bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
vector<int32_t> uids;
- if (useUids) {
- auto uidProviderIt = mPullUidProviders.find(configKey);
- if (uidProviderIt == mPullUidProviders.end()) {
- ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
- configKey.ToString().c_str());
- StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
- return false;
- }
- sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
- if (pullUidProvider == nullptr) {
- ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
- configKey.ToString().c_str());
- StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
- return false;
- }
- uids = pullUidProvider->getPullAtomUids(tagId);
+ const auto& uidProviderIt = mPullUidProviders.find(configKey);
+ if (uidProviderIt == mPullUidProviders.end()) {
+ ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
}
- return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+ sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
+ if (pullUidProvider == nullptr) {
+ ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
+ }
+ uids = pullUidProvider->getPullAtomUids(tagId);
+ return PullLocked(tagId, uids, eventTimeNs, data);
}
bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
- const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
- bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
VLOG("Initiating pulling %d", tagId);
- if (useUids) {
- for (int32_t uid : uids) {
- PullerKey key = {.atomTag = tagId, .uid = uid};
- auto pullerIt = kAllPullAtomInfo.find(key);
- if (pullerIt != kAllPullAtomInfo.end()) {
- bool ret = pullerIt->second->Pull(eventTimeNs, data);
- VLOG("pulled %zu items", data->size());
- if (!ret) {
- StatsdStats::getInstance().notePullFailed(tagId);
- }
- return ret;
- }
- }
- StatsdStats::getInstance().notePullerNotFound(tagId);
- ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
- return false; // Return early since we don't know what to pull.
- } else {
- PullerKey key = {.atomTag = tagId, .uid = -1};
+ for (int32_t uid : uids) {
+ PullerKey key = {.atomTag = tagId, .uid = uid};
auto pullerIt = kAllPullAtomInfo.find(key);
if (pullerIt != kAllPullAtomInfo.end()) {
bool ret = pullerIt->second->Pull(eventTimeNs, data);
@@ -158,9 +138,10 @@ bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
}
return ret;
}
- ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
- return false; // Return early since we don't know what to pull.
}
+ StatsdStats::getInstance().notePullerNotFound(tagId);
+ ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
+ return false; // Return early since we don't know what to pull.
}
bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
@@ -352,8 +333,7 @@ int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
const int64_t coolDownNs, const int64_t timeoutNs,
const vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& callback,
- bool useUid) {
+ const shared_ptr<IPullAtomCallback>& callback) {
std::lock_guard<std::mutex> _l(mLock);
VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
@@ -368,16 +348,15 @@ void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t a
sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs,
actualTimeoutNs, additiveFields);
- PullerKey key = {.atomTag = atomTag, .uid = useUid ? uid : -1};
+ PullerKey key = {.atomTag = atomTag, .uid = uid};
AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(),
new PullAtomCallbackDeathCookie(this, key, puller));
kAllPullAtomInfo[key] = puller;
}
-void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag,
- bool useUids) {
+void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) {
std::lock_guard<std::mutex> _l(mLock);
- PullerKey key = {.atomTag = atomTag, .uid = useUids ? uid : -1};
+ PullerKey key = {.atomTag = atomTag, .uid = uid};
if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag,
/*registered=*/false);
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 194a0f5edba8..489cbdbe5400 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -102,11 +102,11 @@ public:
// If the metric wants to make any change to the data, like timestamps, they
// should make a copy as this data may be shared with multiple metrics.
virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+ vector<std::shared_ptr<LogEvent>>* data);
// Same as above, but directly specify the allowed uids to pull from.
virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+ vector<std::shared_ptr<LogEvent>>* data);
// Clear pull data cache immediately.
int ForceClearPullerCache();
@@ -118,10 +118,9 @@ public:
void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
const int64_t timeoutNs, const vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& callback,
- bool useUid = true);
+ const shared_ptr<IPullAtomCallback>& callback);
- void UnregisterPullAtomCallback(const int uid, const int32_t atomTag, bool useUids = true);
+ void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
@@ -153,10 +152,10 @@ private:
std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+ vector<std::shared_ptr<LogEvent>>* data);
bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids);
+ vector<std::shared_ptr<LogEvent>>* data);
// locks for data receiver and StatsCompanionService changes
std::mutex mLock;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 5987a723a421..9b684f1248c5 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -733,6 +733,11 @@ bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret)
return false;
}
+bool ValueMetricProducer::multipleBucketsSkipped(const int64_t numBucketsForward) {
+ // Skip buckets if this is a pulled metric or a pushed metric that is diffed.
+ return numBucketsForward > 1 && (mIsPulled || mUseDiff);
+}
+
void ValueMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
@@ -910,8 +915,9 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(
interval.sampleSize += 1;
}
- // Only trigger the tracker if all intervals are correct
- if (useAnomalyDetection) {
+ // Only trigger the tracker if all intervals are correct and we have not skipped the bucket due
+ // to MULTIPLE_BUCKETS_SKIPPED.
+ if (useAnomalyDetection && !multipleBucketsSkipped(calcBucketsForwardCount(eventTimeNs))) {
// TODO: propgate proper values down stream when anomaly support doubles
long wholeBucketVal = intervals[0].value.long_value;
auto prev = mCurrentFullBucket.find(eventKey);
@@ -961,9 +967,7 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
int64_t bucketEndTime = fullBucketEndTimeNs;
int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
- // Skip buckets if this is a pulled metric or a pushed metric that is diffed.
- if (numBucketsForward > 1 && (mIsPulled || mUseDiff)) {
-
+ if (multipleBucketsSkipped(numBucketsForward)) {
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
// Something went wrong. Maybe the device was sleeping for a long time. It is better
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index b359af745c91..e72002e88533 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -219,6 +219,8 @@ private:
void pullAndMatchEventsLocked(const int64_t timestampNs);
+ bool multipleBucketsSkipped(const int64_t numBucketsForward);
+
void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
index 322cfaf68a41..64ea219c8465 100644
--- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -43,23 +43,47 @@ TEST(AlarmTrackerTest, TestTriggerTimestamp) {
alarm.set_offset_millis(15 * MS_PER_SEC);
alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr
int64_t startMillis = 100000000 * MS_PER_SEC;
+ int64_t nextAlarmTime = startMillis / MS_PER_SEC + 15;
AlarmTracker tracker(startMillis, startMillis, alarm, kConfigKey, subscriberAlarmMonitor);
- EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15));
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10;
std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet =
subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
EXPECT_TRUE(firedAlarmSet.empty());
tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
- EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15));
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+ EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
currentTimeSec = startMillis / MS_PER_SEC + 7000;
+ nextAlarmTime = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60;
firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
ASSERT_EQ(firedAlarmSet.size(), 1u);
tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
EXPECT_TRUE(firedAlarmSet.empty());
- EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15 + 2 * 60 * 60));
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+ EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
+
+ // Alarm fires exactly on time.
+ currentTimeSec = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60;
+ nextAlarmTime = startMillis / MS_PER_SEC + 15 + 3 * 60 * 60;
+ firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+ ASSERT_EQ(firedAlarmSet.size(), 1u);
+ tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+ EXPECT_TRUE(firedAlarmSet.empty());
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+ EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
+
+ // Alarm fires exactly 1 period late.
+ currentTimeSec = startMillis / MS_PER_SEC + 15 + 4 * 60 * 60;
+ nextAlarmTime = startMillis / MS_PER_SEC + 15 + 5 * 60 * 60;
+ firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+ ASSERT_EQ(firedAlarmSet.size(), 1u);
+ tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+ EXPECT_TRUE(firedAlarmSet.empty());
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+ EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
}
} // namespace statsd
diff --git a/cmds/statsd/tests/external/StatsPullerManager_test.cpp b/cmds/statsd/tests/external/StatsPullerManager_test.cpp
index c76e85ec75e6..0d539f477016 100644
--- a/cmds/statsd/tests/external/StatsPullerManager_test.cpp
+++ b/cmds/statsd/tests/external/StatsPullerManager_test.cpp
@@ -89,10 +89,10 @@ public:
sp<StatsPullerManager> createPullerManagerAndRegister() {
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
shared_ptr<FakePullAtomCallback> cb1 = SharedRefBase::make<FakePullAtomCallback>(uid1);
- pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1, true);
+ pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
shared_ptr<FakePullAtomCallback> cb2 = SharedRefBase::make<FakePullAtomCallback>(uid2);
- pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2, true);
- pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1, true);
+ pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2);
+ pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1);
return pullerManager;
}
} // anonymous namespace
@@ -101,14 +101,14 @@ TEST(StatsPullerManagerTest, TestPullInvalidUid) {
sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data));
}
TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) {
sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data, true));
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data));
ASSERT_EQ(data.size(), 1);
EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
ASSERT_EQ(data[0]->getValues().size(), 1);
@@ -121,7 +121,7 @@ TEST(StatsPullerManagerTest, TestPullInvalidConfigKey) {
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data));
}
TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
@@ -130,7 +130,7 @@ TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data, true));
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data));
EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
ASSERT_EQ(data[0]->getValues().size(), 1);
EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2);
@@ -142,7 +142,7 @@ TEST(StatsPullerManagerTest, TestPullConfigKeyNoPullerWithUid) {
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data));
}
} // namespace statsd
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 5997bedcdf2d..caea42dfe032 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -137,9 +137,9 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
@@ -310,10 +310,10 @@ TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
@@ -388,7 +388,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
@@ -440,9 +440,9 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
return true;
@@ -527,9 +527,9 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
return true;
@@ -566,7 +566,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
GaugeMetric metric;
@@ -665,16 +665,16 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) {
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
@@ -737,23 +737,23 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) {
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
@@ -815,10 +815,10 @@ TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _))
// Bucket start.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
return true;
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 5666501d7d51..98892507e78d 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -294,9 +294,9 @@ TEST(ValueMetricProducerTest, TestFirstBucket) {
TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
@@ -368,19 +368,19 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialize bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
return true;
}))
// Partial bucket.
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(
@@ -434,9 +434,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
return true;
@@ -505,7 +505,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
metric.set_use_absolute_value_on_reset(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -565,7 +565,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -621,23 +621,23 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1); // Third condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180));
@@ -770,11 +770,11 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) {
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Return(true))
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120));
@@ -830,7 +830,7 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(true));
ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
@@ -854,16 +854,16 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs,
bucket2StartTimeNs - 100); // Condition change to false time.
data->clear();
@@ -1081,11 +1081,54 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) {
std::ceil(1.0 * event6.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
}
+TEST(ValueMetricProducerTest, TestAnomalyDetectionMultipleBucketsSkipped) {
+ sp<AlarmMonitor> alarmMonitor;
+ Alert alert;
+ alert.set_id(101);
+ alert.set_metric_id(metricId);
+ alert.set_trigger_if_sum_gt(100);
+ alert.set_num_buckets(1);
+ const int32_t refPeriodSec = 3;
+ alert.set_refractory_period_secs(refPeriodSec);
+
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 0));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs,
+ bucket3StartTimeNs + 100); // Condition changed to false time.
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 100, 120));
+ return true;
+ }));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
+ sp<AnomalyTracker> anomalyTracker = valueProducer->addAnomalyTracker(alert, alarmMonitor);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
+
+ // multiple buckets should be skipped here.
+ valueProducer->onConditionChanged(false, bucket3StartTimeNs + 100);
+
+ // No alert is fired when multiple buckets are skipped.
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
+}
+
// Test value metric no condition, the pull on bucket boundary come in time and too late
TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -1164,10 +1207,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -1175,7 +1218,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
}))
// condition becomes false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
@@ -1227,10 +1270,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -1238,7 +1281,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
}))
// condition becomes false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
@@ -1246,7 +1289,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
}))
// condition becomes true again
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130));
@@ -1677,9 +1720,9 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
@@ -1753,9 +1796,9 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
@@ -1858,9 +1901,9 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
metric.mutable_dimensions_in_what()->add_child()->set_field(1);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
@@ -1961,9 +2004,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfB
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// Used by onConditionChanged.
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
@@ -1995,9 +2038,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // Condition change to true.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -2034,16 +2077,16 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50));
return false;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to false.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -2077,9 +2120,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
metric.set_max_pull_delay_sec(0);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120));
return true;
@@ -2124,9 +2167,9 @@ TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
return true;
@@ -2156,12 +2199,12 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Return(false))
// Second onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2233,10 +2276,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
for (int i = 0; i < 2000; i++) {
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
}
@@ -2290,10 +2333,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed)
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
@@ -2301,7 +2344,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed)
}))
// Second onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2369,10 +2412,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
@@ -2380,7 +2423,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
}))
// Second onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2442,10 +2485,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Start bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
@@ -2475,17 +2518,17 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
return true;
@@ -2518,24 +2561,24 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
@@ -2582,10 +2625,10 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
@@ -2625,19 +2668,19 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketI
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
@@ -2681,10 +2724,10 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketI
TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Second onConditionChanged.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
@@ -2692,7 +2735,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
}))
// Third onConditionChanged.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7));
@@ -2752,10 +2795,10 @@ TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Initialization.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
@@ -2782,19 +2825,19 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucke
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
@@ -2822,10 +2865,10 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First on condition changed.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
@@ -2833,7 +2876,7 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
}))
// Second on condition changed.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -2867,10 +2910,10 @@ TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First condition change.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
@@ -2878,7 +2921,7 @@ TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
}))
// 2nd condition change.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
@@ -2886,7 +2929,7 @@ TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
}))
// 3rd condition change.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
@@ -2931,10 +2974,10 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Initial pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
@@ -2969,10 +3012,10 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Initial pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
@@ -3013,17 +3056,17 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initial pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(
@@ -3069,10 +3112,10 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
@@ -3080,7 +3123,7 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges
}))
// condition becomes false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20));
@@ -3119,10 +3162,10 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
return true;
@@ -3170,10 +3213,10 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
@@ -3210,10 +3253,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequeste
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10));
return true;
@@ -3256,10 +3299,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWron
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
return true;
@@ -3314,10 +3357,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWro
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3325,7 +3368,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWro
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 100);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15));
@@ -3380,10 +3423,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown)
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3391,7 +3434,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown)
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10000);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15));
@@ -3436,10 +3479,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3486,10 +3529,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSki
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3497,7 +3540,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSki
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15));
@@ -3560,10 +3603,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3571,7 +3614,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000);
data->clear();
data->push_back(
@@ -3651,10 +3694,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
@@ -3663,7 +3706,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) {
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
@@ -3740,10 +3783,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenForceBucketSplitBefor
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3751,7 +3794,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenForceBucketSplitBefor
}))
// App Update.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1000);
data->clear();
data->push_back(
@@ -3806,10 +3849,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
return true;
@@ -3857,10 +3900,10 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First condition change event.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
for (int i = 0; i < 2000; i++) {
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
@@ -3877,7 +3920,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10));
@@ -3976,10 +4019,10 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
// Set up ValueMetricProducer.
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -3987,7 +4030,7 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
@@ -3995,7 +4038,7 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
}))
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9));
@@ -4003,7 +4046,7 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
@@ -4011,7 +4054,7 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
@@ -4178,10 +4221,10 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
// Set up ValueMetricProducer.
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -4189,7 +4232,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
@@ -4203,7 +4246,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
@@ -4211,7 +4254,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
@@ -4408,10 +4451,10 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7));
@@ -4420,7 +4463,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
}))
// Uid 1 process state change from kStateUnknown -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
data->push_back(
@@ -4433,7 +4476,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
}))
// Uid 2 process state change from kStateUnknown -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40);
data->clear();
data->push_back(
@@ -4446,7 +4489,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
}))
// Uid 1 process state change from Foreground -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20);
data->clear();
data->push_back(
@@ -4459,7 +4502,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
}))
// Uid 1 process state change from Background -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40);
data->clear();
data->push_back(
@@ -4472,7 +4515,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
}))
// Dump report pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50);
data->clear();
data->push_back(
@@ -4852,10 +4895,10 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
"BATTERY_SAVER_MODE_STATE");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition changed to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
data->clear();
data->push_back(
@@ -4864,7 +4907,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
}))
// Battery saver mode state changed to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
data->clear();
data->push_back(
@@ -4873,7 +4916,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
}))
// Condition changed to false.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index eeb38a4644fd..39232c194ada 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -38,11 +38,10 @@ public:
int64_t nextPulltimeNs, int64_t intervalNs));
MOCK_METHOD3(UnRegisterReceiver,
void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver));
- MOCK_METHOD5(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids));
- MOCK_METHOD5(Pull,
- bool(const int pullCode, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids));
+ MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data));
+ MOCK_METHOD4(Pull, bool(const int pullCode, const vector<int32_t>& uids,
+ const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data));
MOCK_METHOD2(RegisterPullUidProvider,
void(const ConfigKey& configKey, wp<PullUidProvider> provider));
MOCK_METHOD2(UnregisterPullUidProvider,
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index e384b6ac7c84..4fa4135e983f 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -190,9 +190,9 @@ TEST(ShellSubscriberTest, testPulledSubscription) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
const vector<int32_t> uids = {AID_SYSTEM};
- EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _))
.WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1));
data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2));
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index 6384fb12ca68..51bcad115cc5 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -342,6 +342,9 @@ public class TestDrive {
.addPullAtomPackages(PullAtomPackages.newBuilder()
.setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER)
.addPackages("AID_STATSD"))
+ .addPullAtomPackages(PullAtomPackages.newBuilder()
+ .setAtomId(Atom.GENERAL_EXTERNAL_STORAGE_ACCESS_STATS_FIELD_NUMBER)
+ .addPackages("com.google.android.providers.media.module"))
.setHashStringsInMetricReport(false);
}
}
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index fe270a480d83..fed9c43faa38 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -68,6 +68,8 @@ public final class Telecom extends BaseCommand {
private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
+ private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression";
+
/**
* Change the system dialer package name if a package name was specified,
* Example: adb shell telecom set-system-dialer <PACKAGE>
@@ -115,6 +117,8 @@ public final class Telecom extends BaseCommand {
+ "usage: telecom set-sim-count <COUNT>\n"
+ "usage: telecom get-sim-config\n"
+ "usage: telecom get-max-phones\n"
+ + "usage: telecom stop-block-suppression: Stop suppressing the blocked number"
+ + " provider after a call to emergency services.\n"
+ "usage: telecom set-emer-phone-account-filter <PACKAGE>\n"
+ "\n"
+ "telecom set-phone-account-enabled: Enables the given phone account, if it has"
@@ -207,6 +211,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_UNREGISTER_PHONE_ACCOUNT:
runUnregisterPhoneAccount();
break;
+ case COMMAND_STOP_BLOCK_SUPPRESSION:
+ runStopBlockSuppression();
+ break;
case COMMAND_SET_DEFAULT_DIALER:
runSetDefaultDialer();
break;
@@ -324,8 +331,13 @@ public final class Telecom extends BaseCommand {
System.out.println("Success - " + handle + " unregistered.");
}
+ private void runStopBlockSuppression() throws RemoteException {
+ mTelecomService.stopBlockSuppression();
+ }
+
private void runSetDefaultDialer() throws RemoteException {
- final String packageName = nextArgRequired();
+ String packageName = nextArg();
+ if ("default".equals(packageName)) packageName = null;
mTelecomService.setTestDefaultDialer(packageName);
System.out.println("Success - " + packageName + " set as override default dialer.");
}
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 3449010ee99f..ab2f42b97e3b 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -18839,7 +18839,6 @@ HSPLandroid/telephony/PreciseDataConnectionState;-><init>(Landroid/os/Parcel;)V
HSPLandroid/telephony/PreciseDataConnectionState;-><init>(Landroid/os/Parcel;Landroid/telephony/PreciseDataConnectionState$1;)V
HPLandroid/telephony/PreciseDataConnectionState;->equals(Ljava/lang/Object;)Z
HPLandroid/telephony/PreciseDataConnectionState;->getDataConnectionApn()Ljava/lang/String;
-HPLandroid/telephony/PreciseDataConnectionState;->getDataConnectionLinkProperties()Landroid/net/LinkProperties;
HPLandroid/telephony/PreciseDataConnectionState;->getNetworkType()I
HPLandroid/telephony/PreciseDataConnectionState;->getState()I
HSPLandroid/telephony/PreciseDataConnectionState;->toString()Ljava/lang/String;
@@ -42425,10 +42424,6 @@ Landroid/hardware/camera2/impl/CameraMetadataNative$Key;
Landroid/hardware/camera2/impl/CameraMetadataNative;
Landroid/hardware/camera2/impl/GetCommand;
Landroid/hardware/camera2/impl/SetCommand;
-Landroid/hardware/camera2/legacy/LegacyCameraDevice;
-Landroid/hardware/camera2/legacy/LegacyExceptionUtils$BufferQueueAbandonedException;
-Landroid/hardware/camera2/legacy/LegacyMetadataMapper;
-Landroid/hardware/camera2/legacy/PerfMeasurement;
Landroid/hardware/camera2/marshal/MarshalHelpers;
Landroid/hardware/camera2/marshal/MarshalQueryable;
Landroid/hardware/camera2/marshal/MarshalRegistry$MarshalToken;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index a11f1a15af18..e43c7d42868c 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -417,9 +417,6 @@ android.app.INotificationManager
android.app.IProcessObserver$Stub$Proxy
android.app.IProcessObserver$Stub
android.app.IProcessObserver
-android.app.IRequestFinishCallback$Stub$Proxy
-android.app.IRequestFinishCallback$Stub
-android.app.IRequestFinishCallback
android.app.ISearchManager$Stub$Proxy
android.app.ISearchManager$Stub
android.app.ISearchManager
@@ -2297,10 +2294,6 @@ android.hardware.camera2.impl.CameraMetadataNative$Key
android.hardware.camera2.impl.CameraMetadataNative
android.hardware.camera2.impl.GetCommand
android.hardware.camera2.impl.SetCommand
-android.hardware.camera2.legacy.LegacyCameraDevice
-android.hardware.camera2.legacy.LegacyExceptionUtils$BufferQueueAbandonedException
-android.hardware.camera2.legacy.LegacyMetadataMapper
-android.hardware.camera2.legacy.PerfMeasurement
android.hardware.camera2.marshal.MarshalHelpers
android.hardware.camera2.marshal.MarshalQueryable
android.hardware.camera2.marshal.MarshalRegistry$MarshalToken
@@ -9990,8 +9983,6 @@ dalvik.system.CloseGuard$DefaultReporter
dalvik.system.CloseGuard$Reporter
dalvik.system.CloseGuard$Tracker
dalvik.system.CloseGuard
-dalvik.system.DalvikLogHandler
-dalvik.system.DalvikLogging
dalvik.system.DelegateLastClassLoader
dalvik.system.DexClassLoader
dalvik.system.DexFile$1
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 837ad6391986..b5b0ce3a65f3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -498,32 +498,27 @@ public abstract class AccessibilityService extends Service {
/**
* Action to send the KEYCODE_HEADSETHOOK KeyEvent, which is used to answer/hang up calls and
* play/stop media
- * @hide
*/
public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10;
/**
* Action to trigger the Accessibility Button
- * @hide
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11;
/**
* Action to bring up the Accessibility Button's chooser menu
- * @hide
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12;
/**
* Action to trigger the Accessibility Shortcut. This shortcut has a hardware trigger and can
* be activated by holding down the two volume keys.
- * @hide
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13;
/**
* Action to show Launcher's all apps.
- * @hide
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14;
@@ -1907,6 +1902,11 @@ public abstract class AccessibilityService extends Service {
* location in that application. For example going back, going
* home, opening recents, etc.
*
+ * <p>
+ * Note: The global action ids themselves give no information about the current availability
+ * of their corresponding actions. To determine if a global action is available, use
+ * {@link #getSystemActions()}
+ *
* @param action The action to perform.
* @return Whether the action was successfully performed.
*
diff --git a/core/java/android/annotation/SystemApi.java b/core/java/android/annotation/SystemApi.java
index 4ac00983af13..a468439c8e74 100644
--- a/core/java/android/annotation/SystemApi.java
+++ b/core/java/android/annotation/SystemApi.java
@@ -23,7 +23,6 @@ import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
-import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -41,7 +40,6 @@ import java.lang.annotation.Target;
*/
@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
@Retention(RetentionPolicy.RUNTIME)
-@Repeatable(SystemApi.Container.class) // TODO(b/146727827): make this non-repeatable
public @interface SystemApi {
enum Client {
/**
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2f10176e8fdd..4a982dd1a411 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -150,7 +150,6 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -220,8 +219,8 @@ import java.util.function.Consumer;
* <a name="Fragments"></a>
* <h3>Fragments</h3>
*
- * <p>The {@link android.support.v4.app.FragmentActivity} subclass
- * can make use of the {@link android.support.v4.app.Fragment} class to better
+ * <p>The {@link androidx.fragment.app.FragmentActivity} subclass
+ * can make use of the {@link androidx.fragment.app.Fragment} class to better
* modularize their code, build more sophisticated user interfaces for larger
* screens, and help scale their application between small and large screens.</p>
*
@@ -1101,7 +1100,7 @@ public class Activity extends ContextThemeWrapper
/**
* Return the LoaderManager for this activity, creating it if needed.
*
- * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportLoaderManager()}
+ * @deprecated Use {@link androidx.fragment.app.FragmentActivity#getSupportLoaderManager()}
*/
@Deprecated
public LoaderManager getLoaderManager() {
@@ -3160,7 +3159,7 @@ public class Activity extends ContextThemeWrapper
* Return the FragmentManager for interacting with fragments associated
* with this activity.
*
- * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()}
+ * @deprecated Use {@link androidx.fragment.app.FragmentActivity#getSupportFragmentManager()}
*/
@Deprecated
public FragmentManager getFragmentManager() {
@@ -3173,7 +3172,7 @@ public class Activity extends ContextThemeWrapper
* method and before {@link Fragment#onCreate Fragment.onCreate()}.
*
* @deprecated Use {@link
- * android.support.v4.app.FragmentActivity#onAttachFragment(android.support.v4.app.Fragment)}
+ * androidx.fragment.app.FragmentActivity#onAttachFragment(androidx.fragment.app.Fragment)}
*/
@Deprecated
public void onAttachFragment(Fragment fragment) {
@@ -3798,22 +3797,6 @@ public class Activity extends ContextThemeWrapper
return false;
}
- private static final class RequestFinishCallback extends IRequestFinishCallback.Stub {
- private final WeakReference<Activity> mActivityRef;
-
- RequestFinishCallback(WeakReference<Activity> activityRef) {
- mActivityRef = activityRef;
- }
-
- @Override
- public void requestFinish() {
- Activity activity = mActivityRef.get();
- if (activity != null) {
- activity.mHandler.post(activity::finishAfterTransition);
- }
- }
- }
-
/**
* Called when the activity has detected the user's press of the back
* key. The default implementation simply finishes the current activity,
@@ -3837,9 +3820,8 @@ public class Activity extends ContextThemeWrapper
try {
// Inform activity task manager that the activity received a back press
// while at the root of the task. This call allows ActivityTaskManager
- // to intercept or defer finishing.
- ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken,
- new RequestFinishCallback(new WeakReference<>(this)));
+ // to intercept or move the task to the back.
+ ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken);
} catch (RemoteException e) {
finishAfterTransition();
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7c4c19dde4d0..a88c6a890844 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1019,12 +1019,6 @@ public class ActivityManager {
return ActivityTaskManager.getMaxRecentTasksStatic();
}
- /** @removed */
- @Deprecated
- public static int getMaxNumPictureInPictureActions() {
- return 3;
- }
-
/**
* Information you can set and retrieve about the current activity within the recent task list.
*/
@@ -3739,7 +3733,8 @@ public class ActivityManager {
* manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
* </p>
*
- * @param state The state data
+ * @param state The state data. To be advised, <b>DO NOT</b> include sensitive information/data
+ * (PII, SPII, or other sensitive user data) here. Maximum length is 128 bytes.
*/
public void setProcessStateSummary(@Nullable byte[] state) {
try {
@@ -4941,4 +4936,19 @@ public class ActivityManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Resets the state of the {@link com.android.server.am.AppErrors} instance.
+ * This is intended for use with CTS only.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.RESET_APP_ERRORS)
+ public void resetAppErrors() {
+ try {
+ getService().resetAppErrors();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 505cfe7e6d28..9e15c1fca077 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -103,17 +103,16 @@ public abstract class ActivityManagerInternal {
IBinder whitelistToken, long duration);
/**
- * Allows for a {@link PendingIntent} to be whitelisted to start activities from background.
+ * Allows a {@link PendingIntent} to start activities from background.
*/
public abstract void setPendingIntentAllowBgActivityStarts(
- IIntentSender target, IBinder whitelistToken, int flags);
+ IIntentSender target, IBinder allowlistToken, int flags);
/**
- * Voids {@link PendingIntent}'s privilege to be whitelisted to start activities from
- * background.
+ * Voids {@link PendingIntent}'s privilege to start activities from background.
*/
public abstract void clearPendingIntentAllowBgActivityStarts(IIntentSender target,
- IBinder whitelistToken);
+ IBinder allowlistToken);
/**
* Allow DeviceIdleController to tell us about what apps are whitelisted.
@@ -387,8 +386,8 @@ public abstract class ActivityManagerInternal {
/** Returns true if the given uid is the app in the foreground. */
public abstract boolean isAppForeground(int uid);
- /** Returns true if the given uid is currently marked 'bad' */
- public abstract boolean isAppBad(ApplicationInfo info);
+ /** Returns true if the given process name and uid is currently marked 'bad' */
+ public abstract boolean isAppBad(String processName, int uid);
/** Remove pending backup for the given userId. */
public abstract void clearPendingBackup(@UserIdInt int userId);
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 0f31529451fb..4283d7ad2a62 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -31,6 +31,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.DisplayMetrics;
import android.util.Singleton;
import java.util.List;
@@ -139,6 +140,8 @@ public class ActivityTaskManager {
public static final String EXTRA_IGNORE_TARGET_SECURITY =
"android.app.extra.EXTRA_IGNORE_TARGET_SECURITY";
+ /** The minimal size of a display's long-edge needed to support split-screen multi-window. */
+ public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440;
private static int sMaxRecentTasks = -1;
@@ -282,8 +285,23 @@ public class ActivityTaskManager {
com.android.internal.R.bool.config_supportsMultiWindow);
}
- /** Returns true if the system supports split screen multi-window. */
+ /**
+ * Returns {@code true} if the display the context is associated with supports split screen
+ * multi-window.
+ *
+ * @throws UnsupportedOperationException if the supplied {@link Context} is not associated with
+ * a display.
+ */
public static boolean supportsSplitScreenMultiWindow(Context context) {
+ DisplayMetrics dm = new DisplayMetrics();
+ context.getDisplay().getRealMetrics(dm);
+
+ int widthDp = (int) (dm.widthPixels / dm.density);
+ int heightDp = (int) (dm.heightPixels / dm.density);
+ if (Math.max(widthDp, heightDp) < DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP) {
+ return false;
+ }
+
return supportsMultiWindow(context)
&& Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 89e1f5a28a35..9b13d256aea6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -86,6 +86,8 @@ import android.graphics.Canvas;
import android.graphics.HardwareRenderer;
import android.hardware.display.DisplayManagerGlobal;
import android.inputmethodservice.InputMethodService;
+import android.media.MediaFrameworkInitializer;
+import android.media.MediaServiceManager;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.Proxy;
@@ -269,9 +271,6 @@ public final class ActivityThread extends ClientTransactionHandler {
/** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */
public static final int SERVICE_DONE_EXECUTING_STOP = 2;
- // Whether to invoke an activity callback after delivering new configuration.
- private static final boolean REPORT_TO_ACTIVITY = true;
-
/** Use foreground GC policy (less pause time) and higher JIT weight. */
private static final int VM_PROCESS_STATE_JANK_PERCEPTIBLE = 0;
/** Use background GC policy and default JIT threshold. */
@@ -4971,7 +4970,8 @@ public final class ActivityThread extends ClientTransactionHandler {
private void relaunchAllActivities(boolean preserveWindows) {
for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
final ActivityClientRecord r = entry.getValue();
- if (!r.activity.mFinished) {
+ // Schedule relaunch the activity if it is not a local object or finishing.
+ if (!r.activity.mFinished && !(r.token instanceof Binder)) {
if (preserveWindows && r.window != null) {
r.mPreserveWindow = true;
}
@@ -5537,18 +5537,14 @@ public final class ActivityThread extends ClientTransactionHandler {
}
/**
- * Updates the configuration for an Activity. The ActivityClientRecord's
- * {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for
- * that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering
- * the updated Configuration.
- * @param r ActivityClientRecord representing the Activity.
- * @param newBaseConfig The new configuration to use. This may be augmented with
- * {@link ActivityClientRecord#overrideConfig}.
+ * Updates the configuration for an Activity in its current display.
+ *
+ * @see #performConfigurationChangedForActivity(ActivityClientRecord, Configuration, int,
+ * boolean)
*/
private void performConfigurationChangedForActivity(ActivityClientRecord r,
Configuration newBaseConfig) {
- performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId(),
- false /* movedToDifferentDisplay */);
+ performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId());
}
/**
@@ -5560,17 +5556,16 @@ public final class ActivityThread extends ClientTransactionHandler {
* @param newBaseConfig The new configuration to use. This may be augmented with
* {@link ActivityClientRecord#overrideConfig}.
* @param displayId The id of the display where the Activity currently resides.
- * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
* @return {@link Configuration} instance sent to client, null if not sent.
*/
private Configuration performConfigurationChangedForActivity(ActivityClientRecord r,
- Configuration newBaseConfig, int displayId, boolean movedToDifferentDisplay) {
+ Configuration newBaseConfig, int displayId) {
r.tmpConfig.setTo(newBaseConfig);
if (r.overrideConfig != null) {
r.tmpConfig.updateFrom(r.overrideConfig);
}
final Configuration reportedConfig = performActivityConfigurationChanged(r.activity,
- r.tmpConfig, r.overrideConfig, displayId, movedToDifferentDisplay);
+ r.tmpConfig, r.overrideConfig, displayId);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
return reportedConfig;
}
@@ -5597,10 +5592,6 @@ public final class ActivityThread extends ClientTransactionHandler {
* @param newConfig The new configuration.
*/
private void performConfigurationChanged(ComponentCallbacks2 cb, Configuration newConfig) {
- if (!REPORT_TO_ACTIVITY) {
- return;
- }
-
// ContextThemeWrappers may override the configuration for that context. We must check and
// apply any overrides defined.
Configuration contextThemeWrapperOverrideConfig = null;
@@ -5625,12 +5616,10 @@ public final class ActivityThread extends ClientTransactionHandler {
* from the base global configuration. This is supplied by
* ActivityManager.
* @param displayId Id of the display where activity currently resides.
- * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
* @return Configuration sent to client, null if no changes and not moved to different display.
*/
private Configuration performActivityConfigurationChanged(Activity activity,
- Configuration newConfig, Configuration amOverrideConfig, int displayId,
- boolean movedToDifferentDisplay) {
+ Configuration newConfig, Configuration amOverrideConfig, int displayId) {
if (activity == null) {
throw new IllegalArgumentException("No activity provided.");
}
@@ -5643,6 +5632,7 @@ public final class ActivityThread extends ClientTransactionHandler {
// callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition
handleWindowingModeChangeIfNeeded(activity, newConfig);
+ final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId);
boolean shouldReportChange = false;
if (activity.mCurrentConfig == null) {
shouldReportChange = true;
@@ -5656,8 +5646,7 @@ public final class ActivityThread extends ClientTransactionHandler {
amOverrideConfig)) {
// Nothing significant, don't proceed with updating and reporting.
return null;
- } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
- || !REPORT_TO_ACTIVITY) {
+ } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
// If this activity doesn't handle any of the config changes, then don't bother
// calling onConfigurationChanged. Otherwise, report to the activity for the
// changes.
@@ -5691,11 +5680,6 @@ public final class ActivityThread extends ClientTransactionHandler {
final Configuration configToReport = createNewConfigAndUpdateIfNotNull(newConfig,
contextThemeWrapperOverrideConfig);
- if (!REPORT_TO_ACTIVITY) {
- // Not configured to report to activity.
- return configToReport;
- }
-
if (movedToDifferentDisplay) {
activity.dispatchMovedToDisplay(displayId, configToReport);
}
@@ -5988,8 +5972,6 @@ public final class ActivityThread extends ClientTransactionHandler {
if (DEBUG_CONFIGURATION) Slog.w(TAG, "Not found target activity to report to: " + r);
return;
}
- final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY
- && displayId != r.activity.getDisplayId();
synchronized (r) {
if (overrideConfig.isOtherSeqNewer(r.mPendingOverrideConfig)) {
@@ -6003,6 +5985,7 @@ public final class ActivityThread extends ClientTransactionHandler {
r.mPendingOverrideConfig = null;
}
+ final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId);
if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
&& !movedToDifferentDisplay) {
if (DEBUG_CONFIGURATION) {
@@ -6018,29 +6001,34 @@ public final class ActivityThread extends ClientTransactionHandler {
final ViewRootImpl viewRoot = r.activity.mDecor != null
? r.activity.mDecor.getViewRootImpl() : null;
- if (movedToDifferentDisplay) {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity moved to display, activity:"
- + r.activityInfo.name + ", displayId=" + displayId
+ if (DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Handle activity config changed, activity:"
+ + r.activityInfo.name + ", displayId=" + r.activity.getDisplayId()
+ + (movedToDifferentDisplay ? (", newDisplayId=" + displayId) : "")
+ ", config=" + overrideConfig);
-
- final Configuration reportedConfig = performConfigurationChangedForActivity(r,
- mCompatConfiguration, displayId, true /* movedToDifferentDisplay */);
- if (viewRoot != null) {
- viewRoot.onMovedToDisplay(displayId, reportedConfig);
- }
- } else {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
- + r.activityInfo.name + ", config=" + overrideConfig);
- performConfigurationChangedForActivity(r, mCompatConfiguration);
}
+ final Configuration reportedConfig = performConfigurationChangedForActivity(r,
+ mCompatConfiguration,
+ movedToDifferentDisplay ? displayId : r.activity.getDisplayId());
// Notify the ViewRootImpl instance about configuration changes. It may have initiated this
// update to make sure that resources are updated before updating itself.
if (viewRoot != null) {
+ if (movedToDifferentDisplay) {
+ viewRoot.onMovedToDisplay(displayId, reportedConfig);
+ }
viewRoot.updateConfiguration(displayId);
}
mSomeActivitiesChanged = true;
}
+ /**
+ * Checks if the display id of activity is different from the given one. Note that
+ * {@link #INVALID_DISPLAY} means no difference.
+ */
+ private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) {
+ return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId();
+ }
+
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
if (start) {
try {
@@ -7665,6 +7653,7 @@ public final class ActivityThread extends ClientTransactionHandler {
public static void initializeMainlineModules() {
TelephonyFrameworkInitializer.setTelephonyServiceManager(new TelephonyServiceManager());
StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager());
+ MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager());
}
private void purgePendingResources() {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c498e926ba47..b40dd0053846 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -732,6 +732,10 @@ public class AppOpsManager {
public static final int SAMPLING_STRATEGY_BOOT_TIME_SAMPLING =
FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__BOOT_TIME_SAMPLING;
+ /** @hide */
+ public static final int SAMPLING_STRATEGY_UNIFORM_OPS =
+ FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM_OPS;
+
/**
* Strategies used for message sampling
* @hide
@@ -741,7 +745,8 @@ public class AppOpsManager {
SAMPLING_STRATEGY_DEFAULT,
SAMPLING_STRATEGY_UNIFORM,
SAMPLING_STRATEGY_RARELY_USED,
- SAMPLING_STRATEGY_BOOT_TIME_SAMPLING
+ SAMPLING_STRATEGY_BOOT_TIME_SAMPLING,
+ SAMPLING_STRATEGY_UNIFORM_OPS
})
public @interface SamplingStrategy {}
@@ -1456,8 +1461,12 @@ public class AppOpsManager {
/**
* AppOp granted to apps that we are started via {@code am instrument -e --no-isolated-storage}
*
+ * <p>MediaProvider is the only component (outside of system server) that should care about this
+ * app op, hence {@code SystemApi.Client.MODULE_LIBRARIES}.
+ *
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
/** {@link #sAppOpsToNote} not initialized yet for this op */
@@ -7542,6 +7551,7 @@ public class AppOpsManager {
*
* @hide
*/
+ @SuppressWarnings("AndroidFrameworkClientSidePermissionCheck")
public int noteProxyOpNoThrow(int op, @Nullable String proxiedPackageName, int proxiedUid,
@Nullable String proxiedAttributionTag, @Nullable String message) {
int myUid = Process.myUid();
@@ -8349,7 +8359,7 @@ public class AppOpsManager {
* @hide
*/
private static boolean isCollectingStackTraces() {
- if (sConfig.getSampledOpCode() == OP_NONE &&
+ if (sConfig.getSampledOpCode() == OP_NONE && sConfig.getAcceptableLeftDistance() == 0 &&
sConfig.getExpirationTimeSinceBootMillis() >= SystemClock.elapsedRealtime()) {
return false;
}
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index cfe0aff05d4a..e7b3e14bfda7 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -502,7 +502,7 @@ public final class ApplicationExitInfo implements Parcelable {
* Return the defining kernel user identifier, maybe different from {@link #getRealUid} and
* {@link #getPackageUid}, if an external service has the
* {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set
- * to <code>true<code> and was bound with the flag
+ * to <code>true</code> and was bound with the flag
* {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will
* be the kernel user identifier of the external service provider.
*/
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index bac432e42318..15237beee805 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -48,17 +48,18 @@ public class ApplicationLoaders {
ClassLoader parent, String classLoaderName) {
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
librarySearchPath, libraryPermittedPath, parent, classLoaderName,
- null);
+ null, null);
}
ClassLoader getClassLoaderWithSharedLibraries(
String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String classLoaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
- libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
+ libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
}
/**
@@ -77,14 +78,22 @@ public class ApplicationLoaders {
return loader;
}
+ // TODO(b/142191088): allow (Java) shared libraries to have <uses-native-library>
+ // Until that is supported, assume that all native shared libraries are used.
+ // "ALL" is a magic string that libnativeloader uses to unconditionally add all available
+ // native shared libraries to the classloader.
+ List<String> nativeSharedLibraries = new ArrayList<>();
+ nativeSharedLibraries.add("ALL");
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
- librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries);
+ librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey,
- String classLoaderName, List<ClassLoader> sharedLibraries) {
+ String classLoaderName, List<ClassLoader> sharedLibraries,
+ List<String> nativeSharedLibraries) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -113,7 +122,8 @@ public class ApplicationLoaders {
ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
- targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
+ targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -185,7 +195,8 @@ public class ApplicationLoaders {
// assume cached libraries work with current sdk since they are built-in
ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/,
null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/,
- null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/);
+ null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/,
+ null /* nativeSharedLibraries */);
if (classLoader == null) {
// bad configuration or break in classloading code
@@ -255,7 +266,8 @@ public class ApplicationLoaders {
// The cache key is passed separately to enable the stub WebView to be cached under the
// stub's APK path, when the actual package path is the donor APK.
return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
- cacheKey, null /* classLoaderName */, null /* sharedLibraries */);
+ cacheKey, null /* classLoaderName */, null /* sharedLibraries */,
+ null /* nativeSharedLibraries */);
}
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5acd02165b83..dedd8705ef55 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -764,26 +764,27 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public void revokeRuntimePermission(String packageName, String permName, UserHandle user) {
+ revokeRuntimePermission(packageName, permName, user, null);
+ }
+
+ @Override
+ public void revokeRuntimePermission(String packageName, String permName, UserHandle user,
+ String reason) {
if (DEBUG_TRACE_PERMISSION_UPDATES
&& shouldTraceGrant(packageName, permName, user.getIdentifier())) {
Log.i(TAG, "App " + mContext.getPackageName() + " is revoking " + packageName + " "
- + permName + " for user " + user.getIdentifier(), new RuntimeException());
+ + permName + " for user " + user.getIdentifier() + " with reason " + reason,
+ new RuntimeException());
}
try {
mPermissionManager
- .revokeRuntimePermission(packageName, permName, user.getIdentifier());
+ .revokeRuntimePermission(packageName, permName, user.getIdentifier(), reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, UserHandle user,
- String reason) {
- // TODO evanseverson: impl
- }
-
- @Override
public int getPermissionFlags(String permName, String packageName, UserHandle user) {
try {
return mPermissionManager
@@ -2686,7 +2687,7 @@ public class ApplicationPackageManager extends PackageManager {
public void addPreferredActivity(IntentFilter filter,
int match, ComponentName[] set, ComponentName activity) {
try {
- mPM.addPreferredActivity(filter, match, set, activity, getUserId());
+ mPM.addPreferredActivity(filter, match, set, activity, getUserId(), false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2696,7 +2697,7 @@ public class ApplicationPackageManager extends PackageManager {
public void addPreferredActivityAsUser(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId) {
try {
- mPM.addPreferredActivity(filter, match, set, activity, userId);
+ mPM.addPreferredActivity(filter, match, set, activity, userId, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2733,6 +2734,16 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
+ public void addUniquePreferredActivity(@NonNull IntentFilter filter, int match,
+ @Nullable ComponentName[] set, @NonNull ComponentName activity) {
+ try {
+ mPM.addPreferredActivity(filter, match, set, activity, getUserId(), true);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public int getPreferredActivities(List<IntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
try {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9849e590c06b..9613e58fb943 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1900,26 +1900,31 @@ class ContextImpl extends Context {
@Override
public Object getSystemService(String name) {
- // We may override this API from outer context.
- final boolean isUiContext = isUiContext() || getOuterContext().isUiContext();
- // Check incorrect Context usage.
- if (isUiComponent(name) && !isUiContext && vmIncorrectContextUseEnabled()) {
- final String errorMessage = "Tried to access visual service "
- + SystemServiceRegistry.getSystemServiceClassName(name)
- + " from a non-visual Context:" + getOuterContext();
- final String message = "Visual services, such as WindowManager, WallpaperService or "
- + "LayoutInflater should be accessed from Activity or other visual Context. "
- + "Use an Activity or a Context created with "
- + "Context#createWindowContext(int, Bundle), which are adjusted to the "
- + "configuration and visual bounds of an area on screen.";
- final Exception exception = new IllegalAccessException(errorMessage);
- StrictMode.onIncorrectContextUsed(message, exception);
- Log.e(TAG, errorMessage + message, exception);
+ if (vmIncorrectContextUseEnabled()) {
+ // We may override this API from outer context.
+ final boolean isUiContext = isUiContext() || isOuterUiContext();
+ // Check incorrect Context usage.
+ if (isUiComponent(name) && !isUiContext) {
+ final String errorMessage = "Tried to access visual service "
+ + SystemServiceRegistry.getSystemServiceClassName(name)
+ + " from a non-visual Context:" + getOuterContext();
+ final String message = "Visual services, such as WindowManager, WallpaperService "
+ + "or LayoutInflater should be accessed from Activity or other visual "
+ + "Context. Use an Activity or a Context created with "
+ + "Context#createWindowContext(int, Bundle), which are adjusted to "
+ + "the configuration and visual bounds of an area on screen.";
+ final Exception exception = new IllegalAccessException(errorMessage);
+ StrictMode.onIncorrectContextUsed(message, exception);
+ Log.e(TAG, errorMessage + " " + message, exception);
+ }
}
-
return SystemServiceRegistry.getSystemService(this, name);
}
+ private boolean isOuterUiContext() {
+ return getOuterContext() != null && getOuterContext().isUiContext();
+ }
+
@Override
public String getSystemServiceName(Class<?> serviceClass) {
return SystemServiceRegistry.getSystemServiceName(serviceClass);
@@ -2372,7 +2377,7 @@ class ContextImpl extends Context {
context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
mResources.getLoaders()));
- context.mIsUiContext = isUiContext() || getOuterContext().isUiContext();
+ context.mIsUiContext = isUiContext() || isOuterUiContext();
return context;
}
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index e4e5ba37ddbf..f7fb3c34ce1b 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -102,7 +102,7 @@ import java.lang.reflect.InvocationTargetException;
* While the Fragment API was introduced in
* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API
* at is also available for use on older platforms through
- * {@link android.support.v4.app.FragmentActivity}. See the blog post
+ * {@link androidx.fragment.app.FragmentActivity}. See the blog post
* <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html">
* Fragments For All</a> for more details.
*
@@ -258,8 +258,8 @@ import java.lang.reflect.InvocationTargetException;
* pressing back will pop it to return the user to whatever previous state
* the activity UI was in.
*
- * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
- * {@link android.support.v4.app.Fragment} for consistent behavior across all devices
+ * @deprecated Use the <a href="{@docRoot}jetpack">Jetpack Fragment Library</a>
+ * {@link androidx.fragment.app.Fragment} for consistent behavior across all devices
* and access to <a href="{@docRoot}topic/libraries/architecture/lifecycle.html">Lifecycle</a>.
*/
@Deprecated
@@ -432,7 +432,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* through {@link FragmentManager#saveFragmentInstanceState(Fragment)
* FragmentManager.saveFragmentInstanceState}.
*
- * @deprecated Use {@link android.support.v4.app.Fragment.SavedState}
+ * @deprecated Use {@link androidx.fragment.app.Fragment.SavedState}
*/
@Deprecated
public static class SavedState implements Parcelable {
@@ -479,7 +479,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when
* there is an instantiation failure.
*
- * @deprecated Use {@link android.support.v4.app.Fragment.InstantiationException}
+ * @deprecated Use {@link androidx.fragment.app.Fragment.InstantiationException}
*/
@Deprecated
static public class InstantiationException extends AndroidRuntimeException {
@@ -1055,7 +1055,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
/**
* Return the LoaderManager for this fragment, creating it if needed.
*
- * @deprecated Use {@link android.support.v4.app.Fragment#getLoaderManager()}
+ * @deprecated Use {@link androidx.fragment.app.Fragment#getLoaderManager()}
*/
@Deprecated
public LoaderManager getLoaderManager() {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 12ef0159798a..3b6a7b8f7592 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -680,4 +680,17 @@ interface IActivityManager {
* Return whether the app freezer is supported (true) or not (false) by this system.
*/
boolean isAppFreezerSupported();
+
+
+ /**
+ * Kills uid with the reason of permission change.
+ */
+ void killUidForPermissionChange(int appId, int userId, String reason);
+
+ /**
+ * Resets the state of the {@link com.android.server.am.AppErrors} instance.
+ * This is intended for testing within the CTS only and is protected by
+ * android.permission.RESET_APP_ERRORS.
+ */
+ void resetAppErrors();
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index f428b48bb51f..72a3637d8e07 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -26,7 +26,6 @@ import android.app.IAppTask;
import android.app.IAssistDataReceiver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
-import android.app.IRequestFinishCallback;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
import android.app.ITaskStackListener;
@@ -458,9 +457,7 @@ interface IActivityTaskManager {
/**
* Reports that an Activity received a back key press when there were no additional activities
- * on the back stack. If the Activity should be finished, the callback will be invoked. A
- * callback is used instead of finishing the activity directly from the server such that the
- * client may perform actions prior to finishing.
+ * on the back stack.
*/
- void onBackPressedOnTaskRoot(in IBinder activityToken, in IRequestFinishCallback callback);
+ void onBackPressedOnTaskRoot(in IBinder activityToken);
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0deef53c478f..a970322601ec 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -185,6 +185,7 @@ interface INotificationManager
List<ComponentName> getEnabledNotificationListeners(int userId);
ComponentName getAllowedNotificationAssistantForUser(int userId);
ComponentName getAllowedNotificationAssistant();
+ boolean hasEnabledNotificationListener(String packageName, int userId);
@UnsupportedAppUsage
int getZenMode();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f9b48e710148..aa6a08b6d2e4 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -412,6 +412,12 @@ public final class LoadedApk {
return;
}
for (SharedLibraryInfo lib : sharedLibraries) {
+ if (lib.isNative()) {
+ // Native shared lib doesn't contribute to the native lib search path. Its name is
+ // sent to libnativeloader and then the native shared lib is exported from the
+ // default linker namespace.
+ continue;
+ }
List<String> paths = lib.getAllCodePaths();
outSeenPaths.addAll(paths);
for (String path : paths) {
@@ -696,6 +702,12 @@ public final class LoadedApk {
}
List<ClassLoader> loaders = new ArrayList<>();
for (SharedLibraryInfo info : sharedLibraries) {
+ if (info.isNative()) {
+ // Native shared lib doesn't contribute to the native lib search path. Its name is
+ // sent to libnativeloader and then the native shared lib is exported from the
+ // default linker namespace.
+ continue;
+ }
loaders.add(createSharedLibraryLoader(
info, isBundledApp, librarySearchPath, libraryPermittedPath));
}
@@ -802,12 +814,9 @@ public final class LoadedApk {
makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
- String libraryPermittedPath = mDataDir;
- if (mActivityThread == null) {
- // In a zygote context where mActivityThread is null we can't access the app data dir
- // and including this in libraryPermittedPath would cause SELinux denials.
- libraryPermittedPath = "";
- }
+ // Including an inaccessible dir in libraryPermittedPath would cause SELinux denials
+ // when the loader attempts to canonicalise the path. so we don't.
+ String libraryPermittedPath = canAccessDataDir() ? mDataDir : "";
if (isBundledApp) {
// For bundled apps, add the base directory of the app (e.g.,
@@ -898,10 +907,19 @@ public final class LoadedApk {
mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
libraryPermittedPath);
+ List<String> nativeSharedLibraries = new ArrayList<>();
+ if (mApplicationInfo.sharedLibraryInfos != null) {
+ for (SharedLibraryInfo info : mApplicationInfo.sharedLibraryInfos) {
+ if (info.isNative()) {
+ nativeSharedLibraries.add(info.getName());
+ }
+ }
+ }
+
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
- mApplicationInfo.classLoaderName, sharedLibraries);
+ mApplicationInfo.classLoaderName, sharedLibraries, nativeSharedLibraries);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
setThreadPolicy(oldPolicy);
@@ -951,6 +969,33 @@ public final class LoadedApk {
}
}
+ /**
+ * Return whether we can access the package's private data directory in order to be able to
+ * load code from it.
+ */
+ private boolean canAccessDataDir() {
+ // In a zygote context where mActivityThread is null we can't access the app data dir.
+ if (mActivityThread == null) {
+ return false;
+ }
+
+ // A package can access its own data directory (the common case, so short-circuit it).
+ if (Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
+ return true;
+ }
+
+ // Temporarily disable logging of disk reads on the Looper thread as this is necessary -
+ // and the loader will access the directory anyway if we don't check it.
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
+ try {
+ // We are constructing a classloader for a different package. It is likely,
+ // but not certain, that we can't acccess its app data dir - so check.
+ return new File(mDataDir).canExecute();
+ } finally {
+ setThreadPolicy(oldPolicy);
+ }
+ }
+
@UnsupportedAppUsage
public ClassLoader getClassLoader() {
synchronized (this) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 03b941d43932..68e65612971c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -96,9 +96,9 @@ import com.android.internal.util.ContrastColorUtil;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -958,7 +958,7 @@ public class Notification implements Parcelable
*
* @hide
*/
- private IBinder mWhitelistToken;
+ private IBinder mAllowlistToken;
/**
* Must be set by a process to start associating tokens with Notification objects
@@ -966,7 +966,7 @@ public class Notification implements Parcelable
*
* @hide
*/
- static public IBinder processWhitelistToken;
+ static public IBinder processAllowlistToken;
/**
* {@link #extras} key: this is the title of the notification,
@@ -1620,7 +1620,7 @@ public class Notification implements Parcelable
* of non-textual RemoteInputs do not access these remote inputs.
*/
public RemoteInput[] getDataOnlyRemoteInputs() {
- return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
+ return getParcelableArrayFromBundle(mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class);
}
/**
@@ -1802,8 +1802,8 @@ public class Notification implements Parcelable
checkContextualActionNullFields();
ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
- RemoteInput[] previousDataInputs =
- (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
+ RemoteInput[] previousDataInputs = getParcelableArrayFromBundle(
+ mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class);
if (previousDataInputs != null) {
for (RemoteInput input : previousDataInputs) {
dataOnlyInputs.add(input);
@@ -2245,12 +2245,12 @@ public class Notification implements Parcelable
{
int version = parcel.readInt();
- mWhitelistToken = parcel.readStrongBinder();
- if (mWhitelistToken == null) {
- mWhitelistToken = processWhitelistToken;
+ mAllowlistToken = parcel.readStrongBinder();
+ if (mAllowlistToken == null) {
+ mAllowlistToken = processAllowlistToken;
}
// Propagate this token to all pending intents that are unmarshalled from the parcel.
- parcel.setClassCookie(PendingIntent.class, mWhitelistToken);
+ parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
when = parcel.readLong();
creationTime = parcel.readLong();
@@ -2368,7 +2368,7 @@ public class Notification implements Parcelable
* @hide
*/
public void cloneInto(Notification that, boolean heavy) {
- that.mWhitelistToken = this.mWhitelistToken;
+ that.mAllowlistToken = this.mAllowlistToken;
that.when = this.when;
that.creationTime = this.creationTime;
that.mSmallIcon = this.mSmallIcon;
@@ -2678,7 +2678,7 @@ public class Notification implements Parcelable
private void writeToParcelImpl(Parcel parcel, int flags) {
parcel.writeInt(1);
- parcel.writeStrongBinder(mWhitelistToken);
+ parcel.writeStrongBinder(mAllowlistToken);
parcel.writeLong(when);
parcel.writeLong(creationTime);
if (mSmallIcon == null && icon != 0) {
@@ -4801,7 +4801,6 @@ public class Notification implements Parcelable
contentView.setViewVisibility(R.id.time, View.GONE);
contentView.setImageViewIcon(R.id.profile_badge, null);
contentView.setViewVisibility(R.id.profile_badge, View.GONE);
- contentView.setViewVisibility(R.id.alerted_icon, View.GONE);
mN.mUsesStandardHeader = false;
}
@@ -5374,8 +5373,8 @@ public class Notification implements Parcelable
big.setViewVisibility(R.id.actions_container, View.GONE);
}
- RemoteInputHistoryItem[] replyText = (RemoteInputHistoryItem[])
- mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle(
+ mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class);
if (validRemoteInput && replyText != null && replyText.length > 0
&& !TextUtils.isEmpty(replyText[0].getText())
&& p.maxRemoteInputHistory > 0) {
@@ -8161,8 +8160,9 @@ public class Notification implements Parcelable
if (mBuilder.mActions.size() > 0) {
maxRows--;
}
- RemoteInputHistoryItem[] remoteInputHistory = (RemoteInputHistoryItem[])
- mBuilder.mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ RemoteInputHistoryItem[] remoteInputHistory = getParcelableArrayFromBundle(
+ mBuilder.mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS,
+ RemoteInputHistoryItem.class);
if (remoteInputHistory != null
&& remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) {
// Let's remove some messages to make room for the remote input history.
@@ -9585,8 +9585,8 @@ public class Notification implements Parcelable
mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
- Notification[] pages = getNotificationArrayFromBundle(
- wearableBundle, KEY_PAGES);
+ Notification[] pages = getParcelableArrayFromBundle(
+ wearableBundle, KEY_PAGES, Notification.class);
if (pages != null) {
Collections.addAll(mPages, pages);
}
@@ -10844,17 +10844,22 @@ public class Notification implements Parcelable
}
/**
- * Get an array of Notification objects from a parcelable array bundle field.
+ * Get an array of Parcelable objects from a parcelable array bundle field.
* Update the bundle to have a typed array so fetches in the future don't need
* to do an array copy.
*/
- private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
- Parcelable[] array = bundle.getParcelableArray(key);
- if (array instanceof Notification[] || array == null) {
- return (Notification[]) array;
+ @Nullable
+ private static <T extends Parcelable> T[] getParcelableArrayFromBundle(
+ Bundle bundle, String key, Class<T> itemClass) {
+ final Parcelable[] array = bundle.getParcelableArray(key);
+ final Class<?> arrayClass = Array.newInstance(itemClass, 0).getClass();
+ if (arrayClass.isInstance(array) || array == null) {
+ return (T[]) array;
+ }
+ final T[] typedArray = (T[]) Array.newInstance(itemClass, array.length);
+ for (int i = 0; i < array.length; i++) {
+ typedArray[i] = (T) array[i];
}
- Notification[] typedArray = Arrays.copyOf(array, array.length,
- Notification[].class);
bundle.putParcelableArray(key, typedArray);
return typedArray;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index eef9c022fda8..3f1e5614b90c 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -1539,6 +1540,25 @@ public class NotificationManager {
}
}
+ /**
+ * Whether the given user has an enabled
+ * {@link android.service.notification.NotificationListenerService} with the given package name.
+ *
+ * @param packageName the package name of the NotificationListenerService class
+ * @param userHandle the handle of the user that set the listener
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("UserHandle")
+ public boolean hasEnabledNotificationListener(@NonNull String packageName,
+ @NonNull UserHandle userHandle) {
+ INotificationManager service = getService();
+ try {
+ return service.hasEnabledNotificationListener(packageName, userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
private Context mContext;
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 4b0d2f86ad34..bca6f39e1ded 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -175,14 +175,33 @@ import java.util.concurrent.atomic.AtomicLong;
*
* Caching can be disabled completely by initializing {@code sEnabled} to false and rebuilding.
*
+ * To test a binder cache, create one or more tests that exercise the binder method. This
+ * should be done twice: once with production code and once with a special image that sets
+ * {@code DEBUG} and {@code VERIFY} true. In the latter case, verify that no cache
+ * inconsistencies are reported. If a cache inconsistency is reported, however, it might be a
+ * false positive. This happens if the server side data can be read and written non-atomically
+ * with respect to cache invalidation.
+ *
* @param <Query> The class used to index cache entries: must be hashable and comparable
* @param <Result> The class holding cache entries; use a boxed primitive if possible
*
* {@hide}
*/
public abstract class PropertyInvalidatedCache<Query, Result> {
- private static final long NONCE_UNSET = 0;
- private static final long NONCE_DISABLED = -1;
+ /**
+ * Reserved nonce values. The code is written assuming that these
+ * values are contiguous.
+ */
+ private static final int NONCE_UNSET = 0;
+ private static final int NONCE_DISABLED = 1;
+ private static final int NONCE_CORKED = 2;
+ private static final int NONCE_RESERVED = NONCE_CORKED + 1;
+
+ /**
+ * The names of the nonces
+ */
+ private static final String[] sNonceName =
+ new String[]{ "unset", "disabled", "corked" };
private static final String TAG = "PropertyInvalidatedCache";
private static final boolean DEBUG = false;
@@ -195,11 +214,28 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
@GuardedBy("mLock")
private long mMisses = 0;
+ @GuardedBy("mLock")
+ private long mMissDisabled[] = new long[]{ 0, 0, 0 };
+
+ @GuardedBy("mLock")
+ private long mMissOverflow = 0;
+
+ @GuardedBy("mLock")
+ private long mHighWaterMark = 0;
+
// Most invalidation is done in a static context, so the counters need to be accessible.
@GuardedBy("sCorkLock")
private static final HashMap<String, Long> sInvalidates = new HashMap<>();
/**
+ * Record the number of invalidate or cork calls that were nops because
+ * the cache was already corked. This is static because invalidation is
+ * done in a static context.
+ */
+ @GuardedBy("sCorkLock")
+ private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>();
+
+ /**
* If sEnabled is false then all cache operations are stubbed out. Set
* it to false inside test processes.
*/
@@ -271,7 +307,15 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
true /* LRU access order */) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
- return size() > maxEntries;
+ final int size = size();
+ if (size > mHighWaterMark) {
+ mHighWaterMark = size;
+ }
+ if (size > maxEntries) {
+ mMissOverflow++;
+ return true;
+ }
+ return false;
}
};
synchronized (sCorkLock) {
@@ -363,14 +407,21 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
// Let access to mDisabled race: it's atomic anyway.
long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
for (;;) {
- if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET) {
+ if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET ||
+ currentNonce == NONCE_CORKED) {
+ if (!mDisabled) {
+ // Do not bother collecting statistics if the cache is
+ // locally disabled.
+ synchronized (mLock) {
+ mMissDisabled[(int) currentNonce]++;
+ }
+ }
+
if (DEBUG) {
if (!mDisabled) {
Log.d(TAG, String.format(
"cache %s %s for %s",
- cacheName(),
- currentNonce == NONCE_DISABLED ? "disabled" : "unset",
- queryToString(query)));
+ cacheName(), sNonceName[(int) currentNonce], queryToString(query)));
}
}
return recompute(query);
@@ -383,10 +434,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (cachedResult != null) mHits++;
} else {
if (DEBUG) {
- Log.d(TAG,
- String.format("clearing cache %s because nonce changed [%s] -> [%s]",
- cacheName(),
- mLastSeenNonce, currentNonce));
+ Log.d(TAG, String.format(
+ "clearing cache %s of %d entries because nonce changed [%s] -> [%s]",
+ cacheName(), mCache.size(),
+ mLastSeenNonce, currentNonce));
}
mCache.clear();
mLastSeenNonce = currentNonce;
@@ -517,6 +568,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (DEBUG) {
Log.d(TAG, "ignoring invalidation due to cork: " + name);
}
+ final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
+ sCorkedInvalidates.put(name, count + 1);
return;
}
invalidateCacheLocked(name);
@@ -538,7 +591,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
long newValue;
do {
newValue = NoPreloadHolder.next();
- } while (newValue == NONCE_UNSET || newValue == NONCE_DISABLED);
+ } while (newValue >= 0 && newValue < NONCE_RESERVED);
final String newValueString = Long.toString(newValue);
if (DEBUG) {
Log.d(TAG,
@@ -567,13 +620,21 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* @param name Name of the cache-key property to cork
*/
public static void corkInvalidations(@NonNull String name) {
+ if (!sEnabled) {
+ if (DEBUG) {
+ Log.w(TAG, String.format(
+ "cache cork %s suppressed", name));
+ }
+ return;
+ }
+
synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
Log.d(TAG, String.format("corking %s: numberCorks=%s", name, numberCorks));
}
- // If we're the first ones to cork this cache, set the cache to the unset state so
+ // If we're the first ones to cork this cache, set the cache to the corked state so
// existing caches talk directly to their services while we've corked updates.
// Make sure we don't clobber a disabled cache value.
@@ -584,8 +645,11 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (numberCorks == 0) {
final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) {
- SystemProperties.set(name, Long.toString(NONCE_UNSET));
+ SystemProperties.set(name, Long.toString(NONCE_CORKED));
}
+ } else {
+ final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
+ sCorkedInvalidates.put(name, count + 1);
}
sCorks.put(name, numberCorks + 1);
if (DEBUG) {
@@ -602,6 +666,14 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* @param name Name of the cache-key property to uncork
*/
public static void uncorkInvalidations(@NonNull String name) {
+ if (!sEnabled) {
+ if (DEBUG) {
+ Log.w(TAG, String.format(
+ "cache uncork %s suppressed", name));
+ }
+ return;
+ }
+
synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
@@ -729,8 +801,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) {
Log.e(TAG, String.format(
- "cache %s inconsistent for %s",
- cacheName(), queryToString(query)));
+ "cache %s inconsistent for %s is %s should be %s",
+ cacheName(), queryToString(query),
+ proposedResult, resultToCompare));
}
// Always return the "true" result in verification mode.
return resultToCompare;
@@ -784,18 +857,23 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
private void dumpContents(PrintWriter pw, String[] args) {
long invalidateCount;
-
+ long corkedInvalidates;
synchronized (sCorkLock) {
invalidateCount = sInvalidates.getOrDefault(mPropertyName, (long) 0);
+ corkedInvalidates = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0);
}
synchronized (mLock) {
pw.println(String.format(" Cache Property Name: %s", cacheName()));
- pw.println(String.format(" Hits: %d, Misses: %d, Invalidates: %d",
- mHits, mMisses, invalidateCount));
+ pw.println(String.format(" Hits: %d, Misses: %d, Invalidates: %d, Overflows: %d",
+ mHits, mMisses, invalidateCount, mMissOverflow));
+ pw.println(String.format(" Miss-corked: %d, Miss-unset: %d, Miss-other: %d," +
+ " CorkedInvalidates: %d",
+ mMissDisabled[NONCE_CORKED], mMissDisabled[NONCE_UNSET],
+ mMissDisabled[NONCE_DISABLED], corkedInvalidates));
pw.println(String.format(" Last Observed Nonce: %d", mLastSeenNonce));
- pw.println(String.format(" Current Size: %d, Max Size: %d",
- mCache.entrySet().size(), mMaxEntries));
+ pw.println(String.format(" Current Size: %d, Max Size: %d, HW Mark: %d",
+ mCache.size(), mMaxEntries, mHighWaterMark));
pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true"));
Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java
index 82cc2c4daa0b..a1fcf53a2c37 100644
--- a/core/java/android/app/QueuedWork.java
+++ b/core/java/android/app/QueuedWork.java
@@ -80,7 +80,7 @@ public class QueuedWork {
/** Work queued via {@link #queue} */
@GuardedBy("sLock")
- private static final LinkedList<Runnable> sWork = new LinkedList<>();
+ private static LinkedList<Runnable> sWork = new LinkedList<>();
/** If new work can be delayed or not */
@GuardedBy("sLock")
@@ -252,8 +252,8 @@ public class QueuedWork {
LinkedList<Runnable> work;
synchronized (sLock) {
- work = (LinkedList<Runnable>) sWork.clone();
- sWork.clear();
+ work = sWork;
+ sWork = new LinkedList<>();
// Remove all msg-s as all work will be processed now
getHandler().removeMessages(QueuedWorkHandler.MSG_RUN);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index c20c11fea1e4..59997ccab687 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -101,11 +101,11 @@ import android.location.ICountryDetector;
import android.location.ILocationManager;
import android.location.LocationManager;
import android.media.AudioManager;
+import android.media.MediaFrameworkInitializer;
import android.media.MediaRouter;
import android.media.midi.IMidiManager;
import android.media.midi.MidiManager;
import android.media.projection.MediaProjectionManager;
-import android.media.session.MediaSessionManager;
import android.media.soundtrigger.SoundTriggerManager;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
@@ -855,13 +855,6 @@ public final class SystemServiceRegistry {
return new ConsumerIrManager(ctx);
}});
- registerService(Context.MEDIA_SESSION_SERVICE, MediaSessionManager.class,
- new CachedServiceFetcher<MediaSessionManager>() {
- @Override
- public MediaSessionManager createService(ContextImpl ctx) {
- return new MediaSessionManager(ctx);
- }});
-
registerService(Context.TRUST_SERVICE, TrustManager.class,
new StaticServiceFetcher<TrustManager>() {
@Override
@@ -1335,6 +1328,7 @@ public final class SystemServiceRegistry {
WifiFrameworkInitializer.registerServiceWrappers();
StatsFrameworkInitializer.registerServiceWrappers();
RollbackManagerFrameworkInitializer.initialize();
+ MediaFrameworkInitializer.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 5247e1839f77..fe509dea2def 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -33,6 +33,15 @@
},
{
"file_patterns": ["(/|^)AppOpsManager.java"],
+ "name": "CtsStatsdHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.cts.statsd.atom.UidAtomTests#testAppOps"
+ }
+ ]
+ },
+ {
+ "file_patterns": ["(/|^)AppOpsManager.java"],
"name": "CtsPermission2TestCases",
"options": [
{
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 82e988109db8..ce51dba76780 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -294,7 +294,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
}
final long identity = Binder.clearCallingIdentity();
try {
- mPermissionManager.revokeRuntimePermission(packageName, permission, userId);
+ mPermissionManager.revokeRuntimePermission(packageName, permission, userId, null);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 3bc043ee0912..b1a62bf42cc4 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -16,6 +16,7 @@
package android.app.backup;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -34,6 +35,8 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -195,6 +198,19 @@ public class BackupManager {
@SystemApi
public static final int ERROR_TRANSPORT_INVALID = -2;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ OperationType.BACKUP,
+ OperationType.MIGRATION
+ })
+ public @interface OperationType {
+ // A regular backup / restore operation.
+ int BACKUP = 0;
+ // A full migration: all app data for non-system apps is eligible.
+ int MIGRATION = 1;
+ }
+
private Context mContext;
@UnsupportedAppUsage
private static IBackupManager sService;
@@ -736,7 +752,7 @@ public class BackupManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.BACKUP)
public int requestBackup(String[] packages, BackupObserver observer) {
- return requestBackup(packages, observer, null, 0);
+ return requestBackup(packages, observer, null, 0, OperationType.BACKUP);
}
/**
@@ -761,6 +777,31 @@ public class BackupManager {
@RequiresPermission(android.Manifest.permission.BACKUP)
public int requestBackup(String[] packages, BackupObserver observer,
BackupManagerMonitor monitor, int flags) {
+ return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP);
+ }
+
+ /**
+ * Request an immediate backup, providing an observer to which results of the backup operation
+ * will be published. The Android backup system will decide for each package whether it will
+ * be full app data backup or key/value-pair-based backup.
+ *
+ * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
+ * provided packages using the remote transport.
+ *
+ * @param packages List of package names to backup.
+ * @param observer The {@link BackupObserver} to receive callbacks during the backup
+ * operation. Could be {@code null}.
+ * @param monitor The {@link BackupManagerMonitorWrapper} to receive callbacks of important
+ * events during the backup operation. Could be {@code null}.
+ * @param flags {@link #FLAG_NON_INCREMENTAL_BACKUP}.
+ * @param operationType {@link OperationType}
+ * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
+ * @throws IllegalArgumentException on null or empty {@code packages} param.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BACKUP)
+ public int requestBackup(String[] packages, BackupObserver observer,
+ BackupManagerMonitor monitor, int flags, @OperationType int operationType) {
checkServiceBinder();
if (sService != null) {
try {
@@ -770,7 +811,8 @@ public class BackupManager {
BackupManagerMonitorWrapper monitorWrapper = monitor == null
? null
: new BackupManagerMonitorWrapper(monitor);
- return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags);
+ return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags,
+ operationType);
} catch (RemoteException e) {
Log.e(TAG, "requestBackup() couldn't connect");
}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 4940976133c5..96b5dd593bbe 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -678,7 +678,7 @@ interface IBackupManager {
* {@link android.app.backup.IBackupManager.requestBackupForUser} for the calling user id.
*/
int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor,
- int flags);
+ int flags, int operationType);
/**
* Cancel all running backups. After this call returns, no currently running backups will
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
index d8675f39d452..6e93af6a053b 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -41,6 +41,7 @@ interface ITimeZoneDetectorService {
TimeZoneConfiguration getConfiguration();
boolean updateConfiguration(in TimeZoneConfiguration configuration);
void addConfigurationListener(ITimeZoneConfigurationListener listener);
+ void removeConfigurationListener(ITimeZoneConfigurationListener listener);
boolean suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
void suggestTelephonyTimeZone(in TelephonyTimeZoneSuggestion timeZoneSuggestion);
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 786acba94ef4..7885613bfb59 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -29,7 +29,7 @@ import android.content.Context;
@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
public interface TimeZoneDetector {
- /**
+ /**
* Returns the current user's time zone capabilities. See {@link TimeZoneCapabilities}.
*/
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
@@ -74,11 +74,26 @@ public interface TimeZoneDetector {
boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration);
/**
+ * An interface that can be used to listen for changes to the time zone detector configuration.
+ */
+ interface TimeZoneConfigurationListener {
+ /** Called when the configuration changes. There are no guarantees about the thread used. */
+ void onChange(@NonNull TimeZoneConfiguration configuration);
+ }
+
+ /**
* Registers a listener that will be informed when the configuration changes. The complete
* configuration is passed to the listener, not just the properties that have changed.
*/
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- void addConfigurationListener(@NonNull ITimeZoneConfigurationListener listener);
+ void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener);
+
+ /**
+ * Removes a listener previously passed to
+ * {@link #addConfigurationListener(ITimeZoneConfigurationListener)}
+ */
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ void removeConfigurationListener(@NonNull TimeZoneConfigurationListener listener);
/**
* A shared utility method to create a {@link ManualTimeZoneSuggestion}.
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
index 978cb218fbba..6bd365fad6f6 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.ArraySet;
import android.util.Log;
/**
@@ -34,13 +35,16 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector {
private final ITimeZoneDetectorService mITimeZoneDetectorService;
+ private ITimeZoneConfigurationListener mConfigurationReceiver;
+ private ArraySet<TimeZoneConfigurationListener> mConfigurationListeners;
+
public TimeZoneDetectorImpl() throws ServiceNotFoundException {
mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
}
@Override
- @NonNull
+ @NonNull
public TimeZoneCapabilities getCapabilities() {
if (DEBUG) {
Log.d(TAG, "getCapabilities called");
@@ -78,14 +82,70 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector {
}
@Override
- public void addConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) {
+ public void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener) {
if (DEBUG) {
Log.d(TAG, "addConfigurationListener called: " + listener);
}
- try {
- mITimeZoneDetectorService.addConfigurationListener(listener);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (this) {
+ if (mConfigurationListeners.contains(listener)) {
+ return;
+ }
+ if (mConfigurationReceiver == null) {
+ ITimeZoneConfigurationListener iListener =
+ new ITimeZoneConfigurationListener.Stub() {
+ @Override
+ public void onChange(@NonNull TimeZoneConfiguration configuration) {
+ notifyConfigurationListeners(configuration);
+ }
+ };
+ mConfigurationReceiver = iListener;
+ }
+ if (mConfigurationListeners == null) {
+ mConfigurationListeners = new ArraySet<>();
+ }
+
+ boolean wasEmpty = mConfigurationListeners.isEmpty();
+ mConfigurationListeners.add(listener);
+ if (wasEmpty) {
+ try {
+ mITimeZoneDetectorService.addConfigurationListener(mConfigurationReceiver);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) {
+ ArraySet<TimeZoneConfigurationListener> configurationListeners;
+ synchronized (this) {
+ configurationListeners = new ArraySet<>(mConfigurationListeners);
+ }
+ int size = configurationListeners.size();
+ for (int i = 0; i < size; i++) {
+ configurationListeners.valueAt(i).onChange(configuration);
+ }
+ }
+
+ @Override
+ public void removeConfigurationListener(@NonNull TimeZoneConfigurationListener listener) {
+ if (DEBUG) {
+ Log.d(TAG, "removeConfigurationListener called: " + listener);
+ }
+
+ synchronized (this) {
+ if (mConfigurationListeners == null) {
+ return;
+ }
+ boolean wasEmpty = mConfigurationListeners.isEmpty();
+ mConfigurationListeners.remove(listener);
+ if (mConfigurationListeners.isEmpty() && !wasEmpty) {
+ try {
+ mITimeZoneDetectorService.removeConfigurationListener(mConfigurationReceiver);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8c3268c6fc14..d2a774bcb168 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1812,6 +1812,7 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
+ if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles);
return mService.removeActiveDevice(profiles);
}
} catch (RemoteException e) {
@@ -1856,6 +1857,9 @@ public final class BluetoothAdapter {
try {
mServiceLock.readLock().lock();
if (mService != null) {
+ if (DBG) {
+ Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles);
+ }
return mService.setActiveDevice(device, profiles);
}
} catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 4f5c4feb3684..df11d3adac01 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -52,6 +52,18 @@ public final class BluetoothMapClient implements BluetoothProfile {
public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
"android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
+ /**
+ * Action to notify read status changed
+ */
+ public static final String ACTION_MESSAGE_READ_STATUS_CHANGED =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED";
+
+ /**
+ * Action to notify deleted status changed
+ */
+ public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED";
+
/* Extras used in ACTION_MESSAGE_RECEIVED intent.
* NOTE: HANDLE is only valid for a single session with the device. */
public static final String EXTRA_MESSAGE_HANDLE =
@@ -65,6 +77,25 @@ public final class BluetoothMapClient implements BluetoothProfile {
public static final String EXTRA_SENDER_CONTACT_NAME =
"android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
+ /**
+ * Used as a boolean extra in ACTION_MESSAGE_DELETED_STATUS_CHANGED
+ * Contains the MAP message deleted status
+ * Possible values are:
+ * true: deleted
+ * false: undeleted
+ */
+ public static final String EXTRA_MESSAGE_DELETED_STATUS =
+ "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS";
+
+ /**
+ * Extra used in ACTION_MESSAGE_READ_STATUS_CHANGED or ACTION_MESSAGE_DELETED_STATUS_CHANGED
+ * Possible values are:
+ * 0: failure
+ * 1: success
+ */
+ public static final String EXTRA_RESULT_CODE =
+ "android.bluetooth.device.extra.RESULT_CODE";
+
/** There was an error trying to obtain the state */
public static final int STATE_ERROR = -1;
@@ -75,6 +106,12 @@ public final class BluetoothMapClient implements BluetoothProfile {
private static final int UPLOADING_FEATURE_BITMASK = 0x08;
+ /** Parameters in setMessageStatus */
+ public static final int UNREAD = 0;
+ public static final int READ = 1;
+ public static final int UNDELETED = 2;
+ public static final int DELETED = 3;
+
private BluetoothAdapter mAdapter;
private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT,
@@ -405,6 +442,38 @@ public final class BluetoothMapClient implements BluetoothProfile {
return false;
}
+ /**
+ * Set message status of message on MSE
+ * <p>
+ * When read status changed, the result will be published via
+ * {@link #ACTION_MESSAGE_READ_STATUS_CHANGED}
+ * When deleted status changed, the result will be published via
+ * {@link #ACTION_MESSAGE_DELETED_STATUS_CHANGED}
+ *
+ * @param device Bluetooth device
+ * @param handle message handle
+ * @param status <code>UNREAD</code> for "unread", <code>READ</code> for
+ * "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for
+ * "deleted", otherwise return error
+ * @return <code>true</code> if request has been sent, <code>false</code> on error
+ *
+ */
+ @RequiresPermission(Manifest.permission.READ_SMS)
+ public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
+ if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
+ final IBluetoothMapClient service = getService();
+ if (service != null && isEnabled() && isValidDevice(device) && handle != null &&
+ (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) {
+ try {
+ return service.setMessageStatus(device, handle, status);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ return false;
+ }
+
private boolean isEnabled() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 4bd7b059dfaa..591a714bfb93 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -65,6 +65,13 @@ public final class CompanionDeviceManager {
/**
* A device, returned in the activity result of the {@link IntentSender} received in
* {@link Callback#onDeviceFound}
+ *
+ * Type is:
+ * <ul>
+ * <li>for classic Bluetooth - {@link android.bluetooth.BluetoothDevice}</li>
+ * <li>for Bluetooth LE - {@link android.bluetooth.le.ScanResult}</li>
+ * <li>for WiFi - {@link android.net.wifi.ScanResult}</li>
+ * </ul>
*/
public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 565f34a149bd..5fe094d082ae 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1153,6 +1153,9 @@ public class ContextWrapper extends Context {
*/
@Override
public boolean isUiContext() {
+ if (mBase == null) {
+ return false;
+ }
return mBase.isUiContext();
}
}
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 79da1f6ab282..ee9bd3d259fb 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1168,7 +1168,12 @@ public class IntentFilter implements Parcelable {
public int match(Uri data, boolean wildcardSupported) {
String host = data.getHost();
if (host == null) {
- return NO_MATCH_DATA;
+ if (wildcardSupported && mWild) {
+ // special case, if no host is provided, but the Authority is wildcard, match
+ return MATCH_CATEGORY_HOST;
+ } else {
+ return NO_MATCH_DATA;
+ }
}
if (false) Log.v("IntentFilter",
"Match host " + host + ": " + mHost);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index f257326904fd..6a8dd81051eb 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -261,7 +261,7 @@ interface IPackageManager {
in IntentFilter filter, int match, in ComponentName activity);
void addPreferredActivity(in IntentFilter filter, int match,
- in ComponentName[] set, in ComponentName activity, int userId);
+ in ComponentName[] set, in ComponentName activity, int userId, boolean removeExisting);
@UnsupportedAppUsage
void replacePreferredActivity(in IntentFilter filter, int match,
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c2ae7f12df96..df9db278e095 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1292,9 +1292,8 @@ public class PackageInstaller {
*
* @throws PackageManager.NameNotFoundException if the new owner could not be found.
* @throws SecurityException if called after the session has been committed or abandoned.
- * @throws SecurityException if the session does not update the original installer
- * @throws SecurityException if streams opened through
- * {@link #openWrite(String, long, long) are still open.
+ * @throws IllegalArgumentException if streams opened through
+ * {@link #openWrite(String, long, long) are still open.
*/
public void transfer(@NonNull String packageName)
throws PackageManager.NameNotFoundException {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d0f918751d71..42a610700051 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1494,12 +1494,12 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_ABORTED = -115;
/**
- * Installation failed return code: instant app installs are incompatible with some
- * other installation flags supplied for the operation; or other circumstances such
- * as trying to upgrade a system app via an instant app install.
+ * Installation failed return code: install type is incompatible with some other
+ * installation flags supplied for the operation; or other circumstances such as trying
+ * to upgrade a system app via an Incremental or instant app install.
* @hide
*/
- public static final int INSTALL_FAILED_INSTANT_APP_INVALID = -116;
+ public static final int INSTALL_FAILED_SESSION_INVALID = -116;
/**
* Installation parse return code: this is passed in the
@@ -4673,8 +4673,7 @@ public abstract class PackageManager {
* Marks an application exempt from having its permissions be automatically revoked when
* the app is unused for an extended period of time.
*
- * Only the installer on record that installed the given package, or a holder of
- * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+ * Only the installer on record that installed the given package is allowed to call this.
*
* Packages start in whitelisted state, and it is the installer's responsibility to
* un-whitelist the packages it installs, unless auto-revoking permissions from that package
@@ -6764,6 +6763,17 @@ public abstract class PackageManager {
public abstract void clearPackagePreferredActivities(@NonNull String packageName);
/**
+ * Same as {@link #addPreferredActivity(IntentFilter, int, ComponentName[], ComponentName)},
+ * but removes all existing entries that match this filter.
+ * @hide
+ */
+ public void addUniquePreferredActivity(@NonNull IntentFilter filter, int match,
+ @Nullable ComponentName[] set, @NonNull ComponentName activity) {
+ throw new UnsupportedOperationException(
+ "addUniquePreferredActivity not implemented in subclass");
+ }
+
+ /**
* Retrieve all preferred activities, previously added with
* {@link #addPreferredActivity}, that are
* currently registered with the system.
@@ -7474,6 +7484,7 @@ public abstract class PackageManager {
case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION";
case INSTALL_FAILED_PROCESS_NOT_DEFINED: return "INSTALL_FAILED_PROCESS_NOT_DEFINED";
+ case INSTALL_FAILED_SESSION_INVALID: return "INSTALL_FAILED_SESSION_INVALID";
default: return Integer.toString(status);
}
}
@@ -8179,7 +8190,7 @@ public abstract class PackageManager {
private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
sPackageInfoCache =
new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
- 16, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
+ 32, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
@Override
protected PackageInfo recompute(PackageInfoQuery query) {
return getPackageInfoAsUserUncached(
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5ca55c76a0a0..72246ee32186 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1704,13 +1704,6 @@ public class PackageParser {
minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
}
}
- } else if (TAG_PROFILEABLE.equals(parser.getName())) {
- for (int i = 0; i < attrs.getAttributeCount(); ++i) {
- final String attr = attrs.getAttributeName(i);
- if ("shell".equals(attr)) {
- profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
- }
- }
}
}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5f6befdcbaef..e990fd783498 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -525,6 +525,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
protLevel += "|appPredictor";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0) {
+ protLevel += "|companion";
+ }
if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
protLevel += "|retailDemo";
}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index da2a3d885fc6..862563706da7 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -79,6 +79,7 @@ public final class SharedLibraryInfo implements Parcelable {
private final long mVersion;
private final @Type int mType;
+ private final boolean mIsNative;
private final VersionedPackage mDeclaringPackage;
private final List<VersionedPackage> mDependentPackages;
private List<SharedLibraryInfo> mDependencies;
@@ -93,13 +94,14 @@ public final class SharedLibraryInfo implements Parcelable {
* @param type The lib type.
* @param declaringPackage The package that declares the library.
* @param dependentPackages The packages that depend on the library.
+ * @param isNative indicate if this shared lib is a native lib or not (i.e. java)
*
* @hide
*/
public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
String name, long version, int type,
VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
- List<SharedLibraryInfo> dependencies) {
+ List<SharedLibraryInfo> dependencies, boolean isNative) {
mPath = path;
mPackageName = packageName;
mCodePaths = codePaths;
@@ -109,6 +111,16 @@ public final class SharedLibraryInfo implements Parcelable {
mDeclaringPackage = declaringPackage;
mDependentPackages = dependentPackages;
mDependencies = dependencies;
+ mIsNative = isNative;
+ }
+
+ /** @hide */
+ public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
+ String name, long version, int type,
+ VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
+ List<SharedLibraryInfo> dependencies) {
+ this(path, packageName, codePaths, name, version, type, declaringPackage, dependentPackages,
+ dependencies, false /* isNative */);
}
private SharedLibraryInfo(Parcel parcel) {
@@ -125,6 +137,7 @@ public final class SharedLibraryInfo implements Parcelable {
mDeclaringPackage = parcel.readParcelable(null);
mDependentPackages = parcel.readArrayList(null);
mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
+ mIsNative = parcel.readBoolean();
}
/**
@@ -137,6 +150,15 @@ public final class SharedLibraryInfo implements Parcelable {
}
/**
+ * Tells whether this library is a native shared library or not.
+ *
+ * @hide
+ */
+ public boolean isNative() {
+ return mIsNative;
+ }
+
+ /**
* Gets the library name an app defines in its manifest
* to depend on the library.
*
@@ -320,6 +342,7 @@ public final class SharedLibraryInfo implements Parcelable {
parcel.writeParcelable(mDeclaringPackage, flags);
parcel.writeList(mDependentPackages);
parcel.writeTypedList(mDependencies);
+ parcel.writeBoolean(mIsNative);
}
private static String typeToString(int type) {
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 4800f8954575..4914601b0b5a 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -398,6 +398,30 @@ public class ApkLiteParseUtils {
break;
}
}
+
+ final int innerDepth = parser.getDepth();
+ int innerType;
+ while ((innerType = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (innerType != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (innerType == XmlPullParser.END_TAG || innerType == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (parser.getDepth() != innerDepth + 1) {
+ // Search only under <application>.
+ continue;
+ }
+
+ if (PackageParser.TAG_PROFILEABLE.equals(parser.getName())) {
+ for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+ final String attr = attrs.getAttributeName(i);
+ if ("shell".equals(attr)) {
+ profilableByShell = attrs.getAttributeBooleanValue(i,
+ profilableByShell);
+ }
+ }
+ }
+ }
} else if (PackageParser.TAG_OVERLAY.equals(parser.getName())) {
for (int i = 0; i < attrs.getAttributeCount(); ++i) {
final String attr = attrs.getAttributeName(i);
@@ -435,13 +459,6 @@ public class ApkLiteParseUtils {
minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
}
}
- } else if (PackageParser.TAG_PROFILEABLE.equals(parser.getName())) {
- for (int i = 0; i < attrs.getAttributeCount(); ++i) {
- final String attr = attrs.getAttributeName(i);
- if ("shell".equals(attr)) {
- profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
- }
- }
}
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 2ee0ad67b108..872098c8689e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -92,6 +92,10 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage addUsesOptionalLibrary(String libraryName);
+ ParsingPackage addUsesNativeLibrary(String libraryName);
+
+ ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
+
ParsingPackage addUsesStaticLibrary(String libraryName);
ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
@@ -219,6 +223,8 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage removeUsesOptionalLibrary(String libraryName);
+ ParsingPackage removeUsesOptionalNativeLibrary(String libraryName);
+
ParsingPackage setAnyDensity(int anyDensity);
ParsingPackage setAppComponentFactory(String appComponentFactory);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f932bc250e28..0c0dc313087e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -186,6 +186,13 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@NonNull
@DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesNativeLibraries = emptyList();
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesOptionalNativeLibraries = emptyList();
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
private List<String> usesStaticLibraries = emptyList();
@Nullable
private long[] usesStaticLibrariesVersions;
@@ -669,6 +676,27 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
}
@Override
+ public final ParsingPackageImpl addUsesOptionalNativeLibrary(String libraryName) {
+ this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public final ParsingPackageImpl addUsesNativeLibrary(String libraryName) {
+ this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+
+ @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
+ this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
+ libraryName);
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
TextUtils.safeIntern(libraryName));
@@ -982,6 +1010,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
sForInternedStringList.parcel(this.libraryNames, dest, flags);
sForInternedStringList.parcel(this.usesLibraries, dest, flags);
sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
+ sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
+ sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
dest.writeLongArray(this.usesStaticLibrariesVersions);
@@ -1144,6 +1174,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
this.libraryNames = sForInternedStringList.unparcel(in);
this.usesLibraries = sForInternedStringList.unparcel(in);
this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
+ this.usesNativeLibraries = sForInternedStringList.unparcel(in);
+ this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibrariesVersions = in.createLongArray();
@@ -1417,6 +1449,18 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@NonNull
@Override
+ public List<String> getUsesNativeLibraries() {
+ return usesNativeLibraries;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getUsesOptionalNativeLibraries() {
+ return usesOptionalNativeLibraries;
+ }
+
+ @NonNull
+ @Override
public List<String> getUsesStaticLibraries() {
return usesStaticLibraries;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 5b53c18b820c..7e0fe7dc41bf 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -230,6 +230,19 @@ public interface ParsingPackageRead extends Parcelable {
@NonNull
List<String> getUsesOptionalLibraries();
+ /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+ @NonNull
+ List<String> getUsesNativeLibraries();
+
+ /**
+ * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+ * expected to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesNativeLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalNativeLibraries();
+
/**
* TODO(b/135203078): Move static library stuff to an inner data class
* @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 431c5d7310f1..e1f08f3e55a1 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -701,6 +701,8 @@ public class ParsingPackageUtils {
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
return parseUsesLibrary(input, pkg, res, parser);
+ case "uses-native-library":
+ return parseUsesNativeLibrary(input, pkg, res, parser);
case "uses-package":
// Dependencies for app installers; we don't currently try to
// enforce this.
@@ -1510,7 +1512,7 @@ public class ParsingPackageUtils {
Uri data = null;
String dataType = null;
- String host = IntentFilter.WILDCARD;
+ String host = null;
final int numActions = intentInfo.countActions();
final int numSchemes = intentInfo.countDataSchemes();
final int numTypes = intentInfo.countDataTypes();
@@ -2017,6 +2019,8 @@ public class ParsingPackageUtils {
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
return parseUsesLibrary(input, pkg, res, parser);
+ case "uses-native-library":
+ return parseUsesNativeLibrary(input, pkg, res, parser);
case "processes":
return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
case "uses-package":
@@ -2178,6 +2182,37 @@ public class ParsingPackageUtils {
}
@NonNull
+ private static ParseResult<ParsingPackage> parseUsesNativeLibrary(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesNativeLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestUsesNativeLibrary_name);
+ boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesNativeLibrary_required,
+ true);
+
+ if (lname != null) {
+ if (req) {
+ // Upgrade to treat as stronger constraint
+ pkg.addUsesNativeLibrary(lname)
+ .removeUsesOptionalNativeLibrary(lname);
+ } else {
+ // Ignore if someone already defined as required
+ if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) {
+ pkg.addUsesOptionalNativeLibrary(lname);
+ }
+ }
+ }
+
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
throws IOException, XmlPullParserException {
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
index 8a8a066839e3..a8d2d671e5e6 100644
--- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -25,10 +25,12 @@ import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseInput.DeferredError;
import android.content.pm.parsing.result.ParseResult;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.os.Build;
import com.android.internal.R;
@@ -157,7 +159,18 @@ public class ParsedServiceUtils {
}
if (!setExported) {
- service.exported = service.getIntents().size() > 0;
+ boolean hasIntentFilters = service.getIntents().size() > 0;
+ if (hasIntentFilters) {
+ final ParseResult exportedCheckResult = input.deferError(
+ service.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S
+ + " and above) requires that an explicit value for android:exported be"
+ + " defined when intent filters are present",
+ DeferredError.MISSING_EXPORTED_FLAG);
+ if (exportedCheckResult.isError()) {
+ return input.error(exportedCheckResult);
+ }
+ }
+ service.exported = hasIntentFilters;
}
return input.success(service);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 15a184f0e5ef..62c7b85fa62d 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1573,7 +1573,6 @@ public final class AssetManager implements AutoCloseable {
private static native long nativeAssetGetLength(long assetPtr);
private static native long nativeAssetGetRemainingLength(long assetPtr);
- private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
private static native @Nullable Map nativeGetOverlayableMap(long ptr,
@NonNull String packageName);
private static native @Nullable String nativeGetOverlayablesToString(long ptr,
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 6a9e0aa047d1..9480d369065d 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -45,7 +45,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.app.UiModeManager;
import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.LocaleProto;
@@ -928,7 +927,13 @@ public final class Configuration implements Parcelable, Comparable<Configuration
fontScale = o.fontScale;
mcc = o.mcc;
mnc = o.mnc;
- locale = o.locale == null ? null : (Locale) o.locale.clone();
+ if (o.locale == null) {
+ locale = null;
+ } else if (!o.locale.equals(locale)) {
+ // Only clone a new Locale instance if we need to: the clone() is
+ // both CPU and GC intensive.
+ locale = (Locale) o.locale.clone();
+ }
o.fixUpLocaleList();
mLocaleList = o.mLocaleList;
userSetLocale = o.userSetLocale;
@@ -1624,7 +1629,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if ((mask & ActivityInfo.CONFIG_LOCALE) != 0) {
mLocaleList = delta.mLocaleList;
if (!mLocaleList.isEmpty()) {
- locale = (Locale) delta.locale.clone();
+ if (!delta.locale.equals(locale)) {
+ // Don't churn a new Locale clone unless we're actually changing it
+ locale = (Locale) delta.locale.clone();
+ }
}
}
if ((mask & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 25279b31b5d1..215990568fbf 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -249,14 +249,10 @@ public class Camera {
public static final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
/**
- * A constant meaning the normal camera connect/open will be used.
- */
- private static final int CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -2;
-
- /**
- * Used to indicate HAL version un-specified.
+ * Camera HAL device API version 3.0
+ * @hide
*/
- private static final int CAMERA_HAL_API_VERSION_UNSPECIFIED = -1;
+ public static final int CAMERA_HAL_API_VERSION_3_0 = 0x300;
/**
* Hardware face detection. It does not use much CPU.
@@ -427,7 +423,7 @@ public class Camera {
* Creates a new Camera object to access a particular hardware camera with
* given hal API version. If the same camera is opened by other applications
* or the hal API version is not supported by this device, this will throw a
- * RuntimeException.
+ * RuntimeException. As of Android 12, HAL version 1 is no longer supported.
* <p>
* You must call {@link #release()} when you are done using the camera,
* otherwise it will remain locked and be unavailable to other applications.
@@ -463,49 +459,14 @@ public class Camera {
*/
@UnsupportedAppUsage
public static Camera openLegacy(int cameraId, int halVersion) {
- if (halVersion < CAMERA_HAL_API_VERSION_1_0) {
- throw new IllegalArgumentException("Invalid HAL version " + halVersion);
+ if (halVersion < CAMERA_HAL_API_VERSION_3_0) {
+ throw new IllegalArgumentException("Unsupported HAL version " + halVersion);
}
- return new Camera(cameraId, halVersion);
- }
-
- /**
- * Create a legacy camera object.
- *
- * @param cameraId The hardware camera to access, between 0 and
- * {@link #getNumberOfCameras()}-1.
- * @param halVersion The HAL API version this camera device to be opened as.
- */
- private Camera(int cameraId, int halVersion) {
- int err = cameraInitVersion(cameraId, halVersion);
- if (checkInitErrors(err)) {
- if (err == -EACCES) {
- throw new RuntimeException("Fail to connect to camera service");
- } else if (err == -ENODEV) {
- throw new RuntimeException("Camera initialization failed");
- } else if (err == -ENOSYS) {
- throw new RuntimeException("Camera initialization failed because some methods"
- + " are not implemented");
- } else if (err == -EOPNOTSUPP) {
- throw new RuntimeException("Camera initialization failed because the hal"
- + " version is not supported by this device");
- } else if (err == -EINVAL) {
- throw new RuntimeException("Camera initialization failed because the input"
- + " arugments are invalid");
- } else if (err == -EBUSY) {
- throw new RuntimeException("Camera initialization failed because the camera"
- + " device was already opened");
- } else if (err == -EUSERS) {
- throw new RuntimeException("Camera initialization failed because the max"
- + " number of camera devices were already opened");
- }
- // Should never hit this.
- throw new RuntimeException("Unknown camera error");
- }
+ return new Camera(cameraId);
}
- private int cameraInitVersion(int cameraId, int halVersion) {
+ private int cameraInit(int cameraId) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
@@ -523,35 +484,13 @@ public class Camera {
mEventHandler = null;
}
- return native_setup(new WeakReference<Camera>(this), cameraId, halVersion,
+ return native_setup(new WeakReference<Camera>(this), cameraId,
ActivityThread.currentOpPackageName());
}
- private int cameraInitNormal(int cameraId) {
- return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
- }
-
- /**
- * Connect to the camera service using #connectLegacy
- *
- * <p>
- * This acts the same as normal except that it will return
- * the detailed error code if open fails instead of
- * converting everything into {@code NO_INIT}.</p>
- *
- * <p>Intended to use by the camera2 shim only, do <i>not</i> use this for other code.</p>
- *
- * @return a detailed errno error code, or {@code NO_ERROR} on success
- *
- * @hide
- */
- public int cameraInitUnspecified(int cameraId) {
- return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_UNSPECIFIED);
- }
-
/** used by Camera#open, Camera#open(int) */
Camera(int cameraId) {
- int err = cameraInitNormal(cameraId);
+ int err = cameraInit(cameraId);
if (checkInitErrors(err)) {
if (err == -EACCES) {
throw new RuntimeException("Fail to connect to camera service");
@@ -616,8 +555,7 @@ public class Camera {
}
@UnsupportedAppUsage
- private native final int native_setup(Object camera_this, int cameraId, int halVersion,
- String packageName);
+ private native int native_setup(Object cameraThis, int cameraId, String packageName);
private native final void native_release();
diff --git a/core/java/android/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java
index 9520dd9b4283..cbfe4fa58e40 100644
--- a/core/java/android/hardware/GeomagneticField.java
+++ b/core/java/android/hardware/GeomagneticField.java
@@ -16,7 +16,8 @@
package android.hardware;
-import java.util.GregorianCalendar;
+import java.util.Calendar;
+import java.util.TimeZone;
/**
* Estimates magnetic field at a given point on
@@ -49,71 +50,71 @@ public class GeomagneticField {
// These coefficients and the formulae used below are from:
// NOAA Technical Report: The US/UK World Magnetic Model for 2020-2025
- static private final float[][] G_COEFF = new float[][] {
- { 0.0f },
- { -29404.5f, -1450.7f },
- { -2500.0f, 2982.0f, 1676.8f },
- { 1363.9f, -2381.0f, 1236.2f, 525.7f },
- { 903.1f, 809.4f, 86.2f, -309.4f, 47.9f },
- { -234.4f, 363.1f, 187.8f, -140.7f, -151.2f, 13.7f },
- { 65.9f, 65.6f, 73.0f, -121.5f, -36.2f, 13.5f, -64.7f },
- { 80.6f, -76.8f, -8.3f, 56.5f, 15.8f, 6.4f, -7.2f, 9.8f },
- { 23.6f, 9.8f, -17.5f, -0.4f, -21.1f, 15.3f, 13.7f, -16.5f, -0.3f },
- { 5.0f, 8.2f, 2.9f, -1.4f, -1.1f, -13.3f, 1.1f, 8.9f, -9.3f, -11.9f },
- { -1.9f, -6.2f, -0.1f, 1.7f, -0.9f, 0.6f, -0.9f, 1.9f, 1.4f, -2.4f, -3.9f },
- { 3.0f, -1.4f, -2.5f, 2.4f, -0.9f, 0.3f, -0.7f, -0.1f, 1.4f, -0.6f, 0.2f, 3.1f },
- { -2.0f, -0.1f, 0.5f, 1.3f, -1.2f, 0.7f, 0.3f, 0.5f, -0.2f, -0.5f, 0.1f, -1.1f, -0.3f } };
-
-
- static private final float[][] H_COEFF = new float[][] {
- { 0.0f },
- { 0.0f, 4652.9f },
- { 0.0f, -2991.6f, -734.8f },
- { 0.0f, -82.2f, 241.8f, -542.9f },
- { 0.0f, 282.0f, -158.4f, 199.8f, -350.1f },
- { 0.0f, 47.7f, 208.4f, -121.3f, 32.2f, 99.1f },
- { 0.0f, -19.1f, 25.0f, 52.7f, -64.4f, 9.0f, 68.1f },
- { 0.0f, -51.4f, -16.8f, 2.3f, 23.5f, -2.2f, -27.2f, -1.9f },
- { 0.0f, 8.4f, -15.3f, 12.8f, -11.8f, 14.9f, 3.6f, -6.9f, 2.8f },
- { 0.0f, -23.3f, 11.1f, 9.8f, -5.1f, -6.2f, 7.8f, 0.4f, -1.5f, 9.7f },
- { 0.0f, 3.4f, -0.2f, 3.5f, 4.8f, -8.6f, -0.1f, -4.2f, -3.4f, -0.1f, -8.8f },
- { 0.0f, 0.0f, 2.6f, -0.5f, -0.4f, 0.6f, -0.2f, -1.7f, -1.6f, -3.0f, -2.0f, -2.6f },
- { 0.0f, -1.2f, 0.5f, 1.3f, -1.8f, 0.1f, 0.7f, -0.1f, 0.6f, 0.2f, -0.9f, 0.0f, 0.5f } };
-
-
- static private final float[][] DELTA_G = new float[][] {
- { 0.0f },
- { 6.7f, 7.7f },
- { -11.5f, -7.1f, -2.2f },
- { 2.8f, -6.2f, 3.4f, -12.2f },
- { -1.1f, -1.6f, -6.0f, 5.4f, -5.5f },
- { -0.3f, 0.6f, -0.7f, 0.1f, 1.2f, 1.0f },
- { -0.6f, -0.4f, 0.5f, 1.4f, -1.4f, 0.0f, 0.8f },
- { -0.1f, -0.3f, -0.1f, 0.7f, 0.2f, -0.5f, -0.8f, 1.0f },
- { -0.1f, 0.1f, -0.1f, 0.5f, -0.1f, 0.4f, 0.5f, 0.0f, 0.4f },
- { -0.1f, -0.2f, 0.0f, 0.4f, -0.3f, 0.0f, 0.3f, 0.0f, 0.0f, -0.4f },
- { 0.0f, 0.0f, 0.0f, 0.2f, -0.1f, -0.2f, 0.0f, -0.1f, -0.2f, -0.1f, 0.0f },
- { 0.0f, -0.1f, 0.0f, 0.0f, 0.0f, -0.1f, 0.0f, 0.0f, -0.1f, -0.1f, -0.1f, -0.1f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.1f } };
-
-
- static private final float[][] DELTA_H = new float[][] {
- { 0.0f },
- { 0.0f, -25.1f },
- { 0.0f, -30.2f, -23.9f },
- { 0.0f, 5.7f, -1.0f, 1.1f },
- { 0.0f, 0.2f, 6.9f, 3.7f, -5.6f },
- { 0.0f, 0.1f, 2.5f, -0.9f, 3.0f, 0.5f },
- { 0.0f, 0.1f, -1.8f, -1.4f, 0.9f, 0.1f, 1.0f },
- { 0.0f, 0.5f, 0.6f, -0.7f, -0.2f, -1.2f, 0.2f, 0.3f },
- { 0.0f, -0.3f, 0.7f, -0.2f, 0.5f, -0.3f, -0.5f, 0.4f, 0.1f },
- { 0.0f, -0.3f, 0.2f, -0.4f, 0.4f, 0.1f, 0.0f, -0.2f, 0.5f, 0.2f },
- { 0.0f, 0.0f, 0.1f, -0.3f, 0.1f, -0.2f, 0.1f, 0.0f, -0.1f, 0.2f, 0.0f },
- { 0.0f, 0.0f, 0.1f, 0.0f, 0.2f, 0.0f, 0.0f, 0.1f, 0.0f, -0.1f, 0.0f, 0.0f },
- { 0.0f, 0.0f, 0.0f, -0.1f, 0.1f, 0.0f, 0.0f, 0.0f, 0.1f, 0.0f, 0.0f, 0.0f, -0.1f } };
-
- static private final long BASE_TIME =
- new GregorianCalendar(2020, 1, 1).getTimeInMillis();
+ static private final float[][] G_COEFF = new float[][]{
+ {0.0f},
+ {-29404.5f, -1450.7f},
+ {-2500.0f, 2982.0f, 1676.8f},
+ {1363.9f, -2381.0f, 1236.2f, 525.7f},
+ {903.1f, 809.4f, 86.2f, -309.4f, 47.9f},
+ {-234.4f, 363.1f, 187.8f, -140.7f, -151.2f, 13.7f},
+ {65.9f, 65.6f, 73.0f, -121.5f, -36.2f, 13.5f, -64.7f},
+ {80.6f, -76.8f, -8.3f, 56.5f, 15.8f, 6.4f, -7.2f, 9.8f},
+ {23.6f, 9.8f, -17.5f, -0.4f, -21.1f, 15.3f, 13.7f, -16.5f, -0.3f},
+ {5.0f, 8.2f, 2.9f, -1.4f, -1.1f, -13.3f, 1.1f, 8.9f, -9.3f, -11.9f},
+ {-1.9f, -6.2f, -0.1f, 1.7f, -0.9f, 0.6f, -0.9f, 1.9f, 1.4f, -2.4f, -3.9f},
+ {3.0f, -1.4f, -2.5f, 2.4f, -0.9f, 0.3f, -0.7f, -0.1f, 1.4f, -0.6f, 0.2f, 3.1f},
+ {-2.0f, -0.1f, 0.5f, 1.3f, -1.2f, 0.7f, 0.3f, 0.5f, -0.2f, -0.5f, 0.1f, -1.1f, -0.3f}};
+
+ static private final float[][] H_COEFF = new float[][]{
+ {0.0f},
+ {0.0f, 4652.9f},
+ {0.0f, -2991.6f, -734.8f},
+ {0.0f, -82.2f, 241.8f, -542.9f},
+ {0.0f, 282.0f, -158.4f, 199.8f, -350.1f},
+ {0.0f, 47.7f, 208.4f, -121.3f, 32.2f, 99.1f},
+ {0.0f, -19.1f, 25.0f, 52.7f, -64.4f, 9.0f, 68.1f},
+ {0.0f, -51.4f, -16.8f, 2.3f, 23.5f, -2.2f, -27.2f, -1.9f},
+ {0.0f, 8.4f, -15.3f, 12.8f, -11.8f, 14.9f, 3.6f, -6.9f, 2.8f},
+ {0.0f, -23.3f, 11.1f, 9.8f, -5.1f, -6.2f, 7.8f, 0.4f, -1.5f, 9.7f},
+ {0.0f, 3.4f, -0.2f, 3.5f, 4.8f, -8.6f, -0.1f, -4.2f, -3.4f, -0.1f, -8.8f},
+ {0.0f, 0.0f, 2.6f, -0.5f, -0.4f, 0.6f, -0.2f, -1.7f, -1.6f, -3.0f, -2.0f, -2.6f},
+ {0.0f, -1.2f, 0.5f, 1.3f, -1.8f, 0.1f, 0.7f, -0.1f, 0.6f, 0.2f, -0.9f, 0.0f, 0.5f}};
+
+ static private final float[][] DELTA_G = new float[][]{
+ {0.0f},
+ {6.7f, 7.7f},
+ {-11.5f, -7.1f, -2.2f},
+ {2.8f, -6.2f, 3.4f, -12.2f},
+ {-1.1f, -1.6f, -6.0f, 5.4f, -5.5f},
+ {-0.3f, 0.6f, -0.7f, 0.1f, 1.2f, 1.0f},
+ {-0.6f, -0.4f, 0.5f, 1.4f, -1.4f, 0.0f, 0.8f},
+ {-0.1f, -0.3f, -0.1f, 0.7f, 0.2f, -0.5f, -0.8f, 1.0f},
+ {-0.1f, 0.1f, -0.1f, 0.5f, -0.1f, 0.4f, 0.5f, 0.0f, 0.4f},
+ {-0.1f, -0.2f, 0.0f, 0.4f, -0.3f, 0.0f, 0.3f, 0.0f, 0.0f, -0.4f},
+ {0.0f, 0.0f, 0.0f, 0.2f, -0.1f, -0.2f, 0.0f, -0.1f, -0.2f, -0.1f, 0.0f},
+ {0.0f, -0.1f, 0.0f, 0.0f, 0.0f, -0.1f, 0.0f, 0.0f, -0.1f, -0.1f, -0.1f, -0.1f},
+ {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.1f}};
+
+ static private final float[][] DELTA_H = new float[][]{
+ {0.0f},
+ {0.0f, -25.1f},
+ {0.0f, -30.2f, -23.9f},
+ {0.0f, 5.7f, -1.0f, 1.1f},
+ {0.0f, 0.2f, 6.9f, 3.7f, -5.6f},
+ {0.0f, 0.1f, 2.5f, -0.9f, 3.0f, 0.5f},
+ {0.0f, 0.1f, -1.8f, -1.4f, 0.9f, 0.1f, 1.0f},
+ {0.0f, 0.5f, 0.6f, -0.7f, -0.2f, -1.2f, 0.2f, 0.3f},
+ {0.0f, -0.3f, 0.7f, -0.2f, 0.5f, -0.3f, -0.5f, 0.4f, 0.1f},
+ {0.0f, -0.3f, 0.2f, -0.4f, 0.4f, 0.1f, 0.0f, -0.2f, 0.5f, 0.2f},
+ {0.0f, 0.0f, 0.1f, -0.3f, 0.1f, -0.2f, 0.1f, 0.0f, -0.1f, 0.2f, 0.0f},
+ {0.0f, 0.0f, 0.1f, 0.0f, 0.2f, 0.0f, 0.0f, 0.1f, 0.0f, -0.1f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, -0.1f, 0.1f, 0.0f, 0.0f, 0.0f, 0.1f, 0.0f, 0.0f, 0.0f, -0.1f}};
+
+ static private final long BASE_TIME = new Calendar.Builder()
+ .setTimeZone(TimeZone.getTimeZone("UTC"))
+ .setDate(2020, Calendar.JANUARY, 1)
+ .build()
+ .getTimeInMillis();
// The ratio between the Gauss-normalized associated Legendre functions and
// the Schmid quasi-normalized ones. Compute these once staticly since they
@@ -193,7 +194,7 @@ public class GeomagneticField {
// We now compute the magnetic field strength given the geocentric
// location. The magnetic field is the derivative of the potential
// function defined by the model. See NOAA Technical Report: The US/UK
- // World Magnetic Model for 2015-2020 for the derivation.
+ // World Magnetic Model for 2020-2025 for the derivation.
float gcX = 0.0f; // Geocentric northwards component.
float gcY = 0.0f; // Geocentric eastwards component.
float gcZ = 0.0f; // Geocentric downwards component.
@@ -206,7 +207,7 @@ public class GeomagneticField {
// Negative derivative with respect to latitude, divided by
// radius. This looks like the negation of the version in the
- // NOAA Techincal report because that report used
+ // NOAA Technical report because that report used
// P_n^m(sin(theta)) and we use P_n^m(cos(90 - theta)), so the
// derivative with respect to theta is negated.
gcX += relativeRadiusPower[n+2]
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index d949d6409b5c..a605f5d68f06 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -128,7 +128,7 @@ public class BiometricManager {
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for <strong>Tier 3</strong> (formerly <strong>Strong</strong>), as defined
+ * requirements for <strong>Class 3</strong> (formerly <strong>Strong</strong>), as defined
* by the Android CDD.
*
* <p>This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation.
@@ -139,7 +139,7 @@ public class BiometricManager {
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for <strong>Tier 2</strong> (formerly <strong>Weak</strong>), as defined by
+ * requirements for <strong>Class 2</strong> (formerly <strong>Weak</strong>), as defined by
* the Android CDD.
*
* <p>Note that this is a superset of {@link #BIOMETRIC_STRONG} and is defined such that
@@ -149,7 +149,7 @@ public class BiometricManager {
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for <strong>Tier 1</strong> (formerly <strong>Convenience</strong>), as
+ * requirements for <strong>Class 1</strong> (formerly <strong>Convenience</strong>), as
* defined by the Android CDD.
*
* <p>This constant is intended for use by {@link android.provider.DeviceConfig} to adjust
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index 4a46972cd200..8eb22dabbf3c 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -50,6 +50,9 @@ interface IBiometricAuthenticator {
// Determine if a user has at least one enrolled face
boolean hasEnrolledTemplates(int userId, String opPackageName);
+ // Return the LockoutTracker status for the specified user
+ int getLockoutModeForUser(int userId);
+
// Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
void resetLockout(int userId, in byte [] hardwareAuthToken);
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
index aa5ac035f4ee..754162ccba08 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
@@ -26,5 +26,5 @@ oneway interface IBiometricServiceLockoutResetCallback {
/**
* A wakelock will be held until the reciever calls back into {@param callback}
*/
- void onLockoutReset(IRemoteCallback callback);
+ void onLockoutReset(int sensorId, IRemoteCallback callback);
}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 30ee32604939..15625cdeb8f4 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -680,7 +680,7 @@ public abstract class CameraDevice implements AutoCloseable {
* </table><br>
* </p>
*
- *<p>Devices capable of streaming concurrently with other devices as described by
+ *<p>BACKWARD_COMPATIBLE devices capable of streaming concurrently with other devices as described by
* {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds} have the
* following guaranteed streams (when streaming concurrently with other devices)</p>
*
@@ -696,10 +696,14 @@ public abstract class CameraDevice implements AutoCloseable {
* </table><br>
* </p>
*
+ * <p> Devices which are not backwards-compatible, support a mandatory single stream of size sVGA with image format {@code DEPTH16} during concurrent operation.
+ *
* <p> For guaranteed concurrent stream configurations:</p>
- * <p> s720p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * <p> sVGA refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * VGA resolution (640X480) whichever is lower. </p>
+ * <p> s720p refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
* 720p(1280X720) whichever is lower. </p>
- * <p> s1440p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * <p> s1440p refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
* 1440p(1920X1440) whichever is lower. </p>
* <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
* includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices
@@ -707,6 +711,7 @@ public abstract class CameraDevice implements AutoCloseable {
* streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level
* and capabilities.</p>
*
+ *
* <p>Devices capable of outputting HEIC formats ({@link StreamConfigurationMap#getOutputFormats}
* contains {@link android.graphics.ImageFormat#HEIC}) will support substituting {@code JPEG}
* streams with {@code HEIC} in all guaranteed stream combinations for the device's hardware
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 7f834afd7b30..8469f5f981ed 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -23,14 +23,11 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
-import android.hardware.CameraInfo;
import android.hardware.CameraStatus;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.legacy.CameraDeviceUserShim;
-import android.hardware.camera2.legacy.LegacyMetadataMapper;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
@@ -405,10 +402,6 @@ public final class CameraManager {
throw new IllegalArgumentException("No cameras available on device");
}
synchronized (mLock) {
- /*
- * Get the camera characteristics from the camera service directly if it supports it,
- * otherwise get them from the legacy shim instead.
- */
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
@@ -417,34 +410,18 @@ public final class CameraManager {
try {
Size displaySize = getDisplaySize();
- // First check isHiddenPhysicalCamera to avoid supportsCamera2ApiLocked throwing
- // exception in case cameraId is a hidden physical camera.
- if (!isHiddenPhysicalCamera(cameraId) && !supportsCamera2ApiLocked(cameraId)) {
- // Legacy backwards compatibility path; build static info from the camera
- // parameters
- int id = Integer.parseInt(cameraId);
-
- String parameters = cameraService.getLegacyParameters(id);
-
- CameraInfo info = cameraService.getCameraInfo(id);
-
- characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info,
- id, displaySize);
- } else {
- // Normal path: Get the camera characteristics directly from the camera service
- CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
- try {
- info.setCameraId(Integer.parseInt(cameraId));
- } catch (NumberFormatException e) {
- // For external camera, reaching here is expected.
- Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer");
- }
- boolean hasConcurrentStreams =
- CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
- info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
- info.setDisplaySize(displaySize);
- characteristics = new CameraCharacteristics(info);
+ CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
+ try {
+ info.setCameraId(Integer.parseInt(cameraId));
+ } catch (NumberFormatException e) {
+ Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer");
}
+ boolean hasConcurrentStreams =
+ CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
+ info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
+ info.setDisplaySize(displaySize);
+ characteristics = new CameraCharacteristics(info);
+
} catch (ServiceSpecificException e) {
throwAsPublicException(e);
} catch (RemoteException e) {
@@ -500,30 +477,14 @@ public final class CameraManager {
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
try {
- if (supportsCamera2ApiLocked(cameraId)) {
- // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
- ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
- if (cameraService == null) {
- throw new ServiceSpecificException(
- ICameraService.ERROR_DISCONNECTED,
- "Camera service is currently unavailable");
- }
- cameraUser = cameraService.connectDevice(callbacks, cameraId,
- mContext.getOpPackageName(), mContext.getAttributionTag(), uid);
- } else {
- // Use legacy camera implementation for HAL1 devices
- int id;
- try {
- id = Integer.parseInt(cameraId);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
- + cameraId);
- }
-
- Log.i(TAG, "Using legacy camera HAL.");
- cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id,
- getDisplaySize());
+ ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
+ if (cameraService == null) {
+ throw new ServiceSpecificException(
+ ICameraService.ERROR_DISCONNECTED,
+ "Camera service is currently unavailable");
}
+ cameraUser = cameraService.connectDevice(callbacks, cameraId,
+ mContext.getOpPackageName(), mContext.getAttributionTag(), uid);
} catch (ServiceSpecificException e) {
if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
throw new AssertionError("Should've gone down the shim path");
@@ -1021,44 +982,6 @@ public final class CameraManager {
}
/**
- * Queries the camera service if it supports the camera2 api directly, or needs a shim.
- *
- * @param cameraId a non-{@code null} camera identifier
- * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
- */
- private boolean supportsCamera2ApiLocked(String cameraId) {
- return supportsCameraApiLocked(cameraId, API_VERSION_2);
- }
-
- /**
- * Queries the camera service if it supports a camera api directly, or needs a shim.
- *
- * @param cameraId a non-{@code null} camera identifier
- * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
- * @return {@code true} if connecting will work for that device version.
- */
- private boolean supportsCameraApiLocked(String cameraId, int apiVersion) {
- /*
- * Possible return values:
- * - NO_ERROR => CameraX API is supported
- * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception)
- * - Remote exception => If the camera service died
- *
- * Anything else is an unexpected error we don't want to recover from.
- */
- try {
- ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
- // If no camera service, no support
- if (cameraService == null) return false;
-
- return cameraService.supportsCameraApi(cameraId, apiVersion);
- } catch (RemoteException e) {
- // Camera service is now down, no support for any API level
- }
- return false;
- }
-
- /**
* Queries the camera service if a cameraId is a hidden physical camera that belongs to a
* logical camera device.
*
diff --git a/core/java/android/hardware/camera2/legacy/BurstHolder.java b/core/java/android/hardware/camera2/legacy/BurstHolder.java
deleted file mode 100644
index 23efe15fc03b..000000000000
--- a/core/java/android/hardware/camera2/legacy/BurstHolder.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.hardware.camera2.CaptureRequest;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * Immutable container for a burst of capture results.
- */
-public class BurstHolder {
- private static final String TAG = "BurstHolder";
- private final ArrayList<RequestHolder.Builder> mRequestBuilders;
- private final boolean mRepeating;
- private final int mRequestId;
-
- /**
- * Immutable container for a burst of capture results.
- *
- * @param requestId id of the burst request.
- * @param repeating true if this burst is repeating.
- * @param requests the array of {@link CaptureRequest}s for this burst.
- * @param jpegSurfaceIds a {@link Collection} of IDs for the surfaces that have jpeg outputs.
- */
- public BurstHolder(int requestId, boolean repeating, CaptureRequest[] requests,
- Collection<Long> jpegSurfaceIds) {
- mRequestBuilders = new ArrayList<>();
- int i = 0;
- for (CaptureRequest r : requests) {
- mRequestBuilders.add(new RequestHolder.Builder(requestId, /*subsequenceId*/i,
- /*request*/r, repeating, jpegSurfaceIds));
- ++i;
- }
- mRepeating = repeating;
- mRequestId = requestId;
- }
-
- /**
- * Get the id of this request.
- */
- public int getRequestId() {
- return mRequestId;
- }
-
- /**
- * Return true if this repeating.
- */
- public boolean isRepeating() {
- return mRepeating;
- }
-
- /**
- * Return the number of requests in this burst sequence.
- */
- public int getNumberOfRequests() {
- return mRequestBuilders.size();
- }
-
- /**
- * Create a list of {@link RequestHolder} objects encapsulating the requests in this burst.
- *
- * @param frameNumber the starting framenumber for this burst.
- * @return the list of {@link RequestHolder} objects.
- */
- public List<RequestHolder> produceRequestHolders(long frameNumber) {
- ArrayList<RequestHolder> holders = new ArrayList<RequestHolder>();
- int i = 0;
- for (RequestHolder.Builder b : mRequestBuilders) {
- holders.add(b.build(frameNumber + i));
- ++i;
- }
- return holders;
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
deleted file mode 100644
index 89ecd5f1ce63..000000000000
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.hardware.camera2.impl.CameraDeviceImpl;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.os.Handler;
-import android.util.Log;
-
-/**
- * Emulates a the state of a single Camera2 device.
- *
- * <p>
- * This class acts as the state machine for a camera device. Valid state transitions are given
- * in the table below:
- * </p>
- *
- * <ul>
- * <li>{@code UNCONFIGURED -> CONFIGURING}</li>
- * <li>{@code CONFIGURING -> IDLE}</li>
- * <li>{@code IDLE -> CONFIGURING}</li>
- * <li>{@code IDLE -> CAPTURING}</li>
- * <li>{@code IDLE -> IDLE}</li>
- * <li>{@code CAPTURING -> IDLE}</li>
- * <li>{@code ANY -> ERROR}</li>
- * </ul>
- */
-public class CameraDeviceState {
- private static final String TAG = "CameraDeviceState";
- private static final boolean DEBUG = false;
-
- private static final int STATE_ERROR = 0;
- private static final int STATE_UNCONFIGURED = 1;
- private static final int STATE_CONFIGURING = 2;
- private static final int STATE_IDLE = 3;
- private static final int STATE_CAPTURING = 4;
-
- private static final String[] sStateNames = { "ERROR", "UNCONFIGURED", "CONFIGURING", "IDLE",
- "CAPTURING"};
-
- private int mCurrentState = STATE_UNCONFIGURED;
- private int mCurrentError = NO_CAPTURE_ERROR;
-
- private RequestHolder mCurrentRequest = null;
-
- private Handler mCurrentHandler = null;
- private CameraDeviceStateListener mCurrentListener = null;
-
- /**
- * Error code used by {@link #setCaptureStart} and {@link #setCaptureResult} to indicate that no
- * error has occurred.
- */
- public static final int NO_CAPTURE_ERROR = -1;
-
- /**
- * CameraDeviceStateListener callbacks to be called after state transitions.
- */
- public interface CameraDeviceStateListener {
- void onError(int errorCode, Object errorArg, RequestHolder holder);
- void onConfiguring();
- void onIdle();
- void onBusy();
- void onCaptureStarted(RequestHolder holder, long timestamp);
- void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
- void onRequestQueueEmpty();
- void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId);
- }
-
- /**
- * Transition to the {@code ERROR} state.
- *
- * <p>
- * The device cannot exit the {@code ERROR} state. If the device was not already in the
- * {@code ERROR} state, {@link CameraDeviceStateListener#onError(int, RequestHolder)} will be
- * called.
- * </p>
- *
- * @param error the error to set. Should be one of the error codes defined in
- * {@link CameraDeviceImpl.CameraDeviceCallbacks}.
- */
- public synchronized void setError(int error) {
- mCurrentError = error;
- doStateTransition(STATE_ERROR);
- }
-
- /**
- * Transition to the {@code CONFIGURING} state, or {@code ERROR} if in an invalid state.
- *
- * <p>
- * If the device was not already in the {@code CONFIGURING} state,
- * {@link CameraDeviceStateListener#onConfiguring()} will be called.
- * </p>
- *
- * @return {@code false} if an error has occurred.
- */
- public synchronized boolean setConfiguring() {
- doStateTransition(STATE_CONFIGURING);
- return mCurrentError == NO_CAPTURE_ERROR;
- }
-
- /**
- * Transition to the {@code IDLE} state, or {@code ERROR} if in an invalid state.
- *
- * <p>
- * If the device was not already in the {@code IDLE} state,
- * {@link CameraDeviceStateListener#onIdle()} will be called.
- * </p>
- *
- * @return {@code false} if an error has occurred.
- */
- public synchronized boolean setIdle() {
- doStateTransition(STATE_IDLE);
- return mCurrentError == NO_CAPTURE_ERROR;
- }
-
- /**
- * Transition to the {@code CAPTURING} state, or {@code ERROR} if in an invalid state.
- *
- * <p>
- * If the device was not already in the {@code CAPTURING} state,
- * {@link CameraDeviceStateListener#onCaptureStarted(RequestHolder)} will be called.
- * </p>
- *
- * @param request A {@link RequestHolder} containing the request for the current capture.
- * @param timestamp The timestamp of the capture start in nanoseconds.
- * @param captureError Report a recoverable error for a single request using a valid
- * error code for {@code ICameraDeviceCallbacks}, or
- * {@link #NO_CAPTURE_ERROR}
- * @return {@code false} if an error has occurred.
- */
- public synchronized boolean setCaptureStart(final RequestHolder request, long timestamp,
- int captureError) {
- mCurrentRequest = request;
- doStateTransition(STATE_CAPTURING, timestamp, captureError);
- return mCurrentError == NO_CAPTURE_ERROR;
- }
-
- /**
- * Set the result for a capture.
- *
- * <p>
- * If the device was in the {@code CAPTURING} state,
- * {@link CameraDeviceStateListener#onCaptureResult(CameraMetadataNative, RequestHolder)} will
- * be called with the given result, otherwise this will result in the device transitioning to
- * the {@code ERROR} state,
- * </p>
- *
- * @param request The {@link RequestHolder} request that created this result.
- * @param result The {@link CameraMetadataNative} result to set.
- * @param captureError Report a recoverable error for a single buffer or result using a valid
- * error code for {@code ICameraDeviceCallbacks}, or
- * {@link #NO_CAPTURE_ERROR}.
- * @param captureErrorArg An argument for some error captureError codes.
- * @return {@code false} if an error has occurred.
- */
- public synchronized boolean setCaptureResult(final RequestHolder request,
- final CameraMetadataNative result,
- final int captureError, final Object captureErrorArg) {
- if (mCurrentState != STATE_CAPTURING) {
- Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
- mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
- doStateTransition(STATE_ERROR);
- return mCurrentError == NO_CAPTURE_ERROR;
- }
-
- if (mCurrentHandler != null && mCurrentListener != null) {
- if (captureError != NO_CAPTURE_ERROR) {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onError(captureError, captureErrorArg, request);
- }
- });
- } else {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onCaptureResult(result, request);
- }
- });
- }
- }
- return mCurrentError == NO_CAPTURE_ERROR;
- }
-
- public synchronized boolean setCaptureResult(final RequestHolder request,
- final CameraMetadataNative result) {
- return setCaptureResult(request, result, NO_CAPTURE_ERROR, /*errorArg*/null);
- }
-
- /**
- * Set repeating request error.
- *
- * <p>Repeating request has been stopped due to an error such as abandoned output surfaces.</p>
- *
- * @param lastFrameNumber Frame number of the last repeating request before it is stopped.
- * @param repeatingRequestId The ID of the repeating request being stopped
- */
- public synchronized void setRepeatingRequestError(final long lastFrameNumber,
- final int repeatingRequestId) {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
- }
- });
- }
-
- /**
- * Indicate that request queue (non-repeating) becomes empty.
- *
- * <p> Send notification that all non-repeating requests have been sent to camera device. </p>
- */
- public synchronized void setRequestQueueEmpty() {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onRequestQueueEmpty();
- }
- });
- }
-
- /**
- * Set the listener for state transition callbacks.
- *
- * @param handler handler on which to call the callbacks.
- * @param listener the {@link CameraDeviceStateListener} callbacks to call.
- */
- public synchronized void setCameraDeviceCallbacks(Handler handler,
- CameraDeviceStateListener listener) {
- mCurrentHandler = handler;
- mCurrentListener = listener;
- }
-
- private void doStateTransition(int newState) {
- doStateTransition(newState, /*timestamp*/0, NO_CAPTURE_ERROR);
- }
-
- private void doStateTransition(int newState, final long timestamp, final int error) {
- if (newState != mCurrentState) {
- String stateName = "UNKNOWN";
- if (newState >= 0 && newState < sStateNames.length) {
- stateName = sStateNames[newState];
- }
- Log.i(TAG, "Legacy camera service transitioning to state " + stateName);
- }
-
- // If we transitioned into a non-IDLE/non-ERROR state then mark the device as busy
- if(newState != STATE_ERROR && newState != STATE_IDLE) {
- if (mCurrentState != newState && mCurrentHandler != null &&
- mCurrentListener != null) {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onBusy();
- }
- });
- }
- }
-
- switch(newState) {
- case STATE_ERROR:
- if (mCurrentState != STATE_ERROR && mCurrentHandler != null &&
- mCurrentListener != null) {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onError(mCurrentError, /*errorArg*/null, mCurrentRequest);
- }
- });
- }
- mCurrentState = STATE_ERROR;
- break;
- case STATE_CONFIGURING:
- if (mCurrentState != STATE_UNCONFIGURED && mCurrentState != STATE_IDLE) {
- Log.e(TAG, "Cannot call configure while in state: " + mCurrentState);
- mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
- doStateTransition(STATE_ERROR);
- break;
- }
- if (mCurrentState != STATE_CONFIGURING && mCurrentHandler != null &&
- mCurrentListener != null) {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onConfiguring();
- }
- });
- }
- mCurrentState = STATE_CONFIGURING;
- break;
- case STATE_IDLE:
- if (mCurrentState == STATE_IDLE) {
- break;
- }
-
- if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
- Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
- mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
- doStateTransition(STATE_ERROR);
- break;
- }
-
- if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
- mCurrentListener != null) {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onIdle();
- }
- });
- }
- mCurrentState = STATE_IDLE;
- break;
- case STATE_CAPTURING:
- if (mCurrentState != STATE_IDLE && mCurrentState != STATE_CAPTURING) {
- Log.e(TAG, "Cannot call capture while in state: " + mCurrentState);
- mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
- doStateTransition(STATE_ERROR);
- break;
- }
-
- if (mCurrentHandler != null && mCurrentListener != null) {
- if (error != NO_CAPTURE_ERROR) {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onError(error, /*errorArg*/null, mCurrentRequest);
- }
- });
- } else {
- mCurrentHandler.post(new Runnable() {
- @Override
- public void run() {
- mCurrentListener.onCaptureStarted(mCurrentRequest, timestamp);
- }
- });
- }
- }
- mCurrentState = STATE_CAPTURING;
- break;
- default:
- throw new IllegalStateException("Transition to unknown state: " + newState);
- }
- }
-
-
-}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
deleted file mode 100644
index cf8cab2cbc44..000000000000
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ /dev/null
@@ -1,805 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.hardware.ICameraService;
-import android.hardware.Camera;
-import android.hardware.Camera.CameraInfo;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.ICameraDeviceCallbacks;
-import android.hardware.camera2.ICameraDeviceUser;
-import android.hardware.camera2.ICameraOfflineSession;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.impl.CaptureResultExtras;
-import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
-import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.params.SessionConfiguration;
-import android.hardware.camera2.utils.SubmitInfo;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.util.Log;
-import android.util.Size;
-import android.util.SparseArray;
-import android.view.Surface;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static android.system.OsConstants.EACCES;
-import static android.system.OsConstants.ENODEV;
-
-/**
- * Compatibility implementation of the Camera2 API binder interface.
- *
- * <p>
- * This is intended to be called from the same process as client
- * {@link android.hardware.camera2.CameraDevice}, and wraps a
- * {@link android.hardware.camera2.legacy.LegacyCameraDevice} that emulates Camera2 service using
- * the Camera1 API.
- * </p>
- *
- * <p>
- * Keep up to date with ICameraDeviceUser.aidl.
- * </p>
- */
-@SuppressWarnings("deprecation")
-public class CameraDeviceUserShim implements ICameraDeviceUser {
- private static final String TAG = "CameraDeviceUserShim";
-
- private static final boolean DEBUG = false;
- private static final int OPEN_CAMERA_TIMEOUT_MS = 5000; // 5 sec (same as api1 cts timeout)
-
- private final LegacyCameraDevice mLegacyDevice;
-
- private final Object mConfigureLock = new Object();
- private int mSurfaceIdCounter;
- private boolean mConfiguring;
- private final SparseArray<Surface> mSurfaces;
- private final CameraCharacteristics mCameraCharacteristics;
- private final CameraLooper mCameraInit;
- private final CameraCallbackThread mCameraCallbacks;
-
-
- protected CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera,
- CameraCharacteristics characteristics, CameraLooper cameraInit,
- CameraCallbackThread cameraCallbacks) {
- mLegacyDevice = legacyCamera;
- mConfiguring = false;
- mSurfaces = new SparseArray<Surface>();
- mCameraCharacteristics = characteristics;
- mCameraInit = cameraInit;
- mCameraCallbacks = cameraCallbacks;
-
- mSurfaceIdCounter = 0;
- }
-
- private static int translateErrorsFromCamera1(int errorCode) {
- if (errorCode == -EACCES) {
- return ICameraService.ERROR_PERMISSION_DENIED;
- }
-
- return errorCode;
- }
-
- /**
- * Create a separate looper/thread for the camera to run on; open the camera.
- *
- * <p>Since the camera automatically latches on to the current thread's looper,
- * it's important that we have our own thread with our own looper to guarantee
- * that the camera callbacks get correctly posted to our own thread.</p>
- */
- private static class CameraLooper implements Runnable, AutoCloseable {
- private final int mCameraId;
- private Looper mLooper;
- private volatile int mInitErrors;
- private final Camera mCamera = Camera.openUninitialized();
- private final ConditionVariable mStartDone = new ConditionVariable();
- private final Thread mThread;
-
- /**
- * Spin up a new thread, immediately open the camera in the background.
- *
- * <p>Use {@link #waitForOpen} to block until the camera is finished opening.</p>
- *
- * @param cameraId numeric camera Id
- *
- * @see #waitForOpen
- */
- public CameraLooper(int cameraId) {
- mCameraId = cameraId;
-
- mThread = new Thread(this, "LegacyCameraLooper");
- mThread.start();
- }
-
- public Camera getCamera() {
- return mCamera;
- }
-
- @Override
- public void run() {
- // Set up a looper to be used by camera.
- Looper.prepare();
-
- // Save the looper so that we can terminate this thread
- // after we are done with it.
- mLooper = Looper.myLooper();
- mInitErrors = mCamera.cameraInitUnspecified(mCameraId);
- mStartDone.open();
- Looper.loop(); // Blocks forever until #close is called.
- }
-
- /**
- * Quit the looper safely; then join until the thread shuts down.
- */
- @Override
- public void close() {
- if (mLooper == null) {
- return;
- }
-
- mLooper.quitSafely();
- try {
- mThread.join();
- } catch (InterruptedException e) {
- throw new AssertionError(e);
- }
-
- mLooper = null;
- }
-
- /**
- * Block until the camera opens; then return its initialization error code (if any).
- *
- * @param timeoutMs timeout in milliseconds
- *
- * @return int error code
- *
- * @throws ServiceSpecificException if the camera open times out with ({@code CAMERA_ERROR})
- */
- public int waitForOpen(int timeoutMs) {
- // Block until the camera is open asynchronously
- if (!mStartDone.block(timeoutMs)) {
- Log.e(TAG, "waitForOpen - Camera failed to open after timeout of "
- + OPEN_CAMERA_TIMEOUT_MS + " ms");
- try {
- mCamera.release();
- } catch (RuntimeException e) {
- Log.e(TAG, "connectBinderShim - Failed to release camera after timeout ", e);
- }
-
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION);
- }
-
- return mInitErrors;
- }
- }
-
- /**
- * A thread to process callbacks to send back to the camera client.
- *
- * <p>This effectively emulates one-way binder semantics when in the same process as the
- * callee.</p>
- */
- private static class CameraCallbackThread implements ICameraDeviceCallbacks {
- private static final int CAMERA_ERROR = 0;
- private static final int CAMERA_IDLE = 1;
- private static final int CAPTURE_STARTED = 2;
- private static final int RESULT_RECEIVED = 3;
- private static final int PREPARED = 4;
- private static final int REPEATING_REQUEST_ERROR = 5;
- private static final int REQUEST_QUEUE_EMPTY = 6;
-
- private final HandlerThread mHandlerThread;
- private Handler mHandler;
-
- private final ICameraDeviceCallbacks mCallbacks;
-
- public CameraCallbackThread(ICameraDeviceCallbacks callbacks) {
- mCallbacks = callbacks;
-
- mHandlerThread = new HandlerThread("LegacyCameraCallback");
- mHandlerThread.start();
- }
-
- public void close() {
- mHandlerThread.quitSafely();
- }
-
- @Override
- public void onDeviceError(final int errorCode, final CaptureResultExtras resultExtras) {
- Message msg = getHandler().obtainMessage(CAMERA_ERROR,
- /*arg1*/ errorCode, /*arg2*/ 0,
- /*obj*/ resultExtras);
- getHandler().sendMessage(msg);
- }
-
- @Override
- public void onDeviceIdle() {
- Message msg = getHandler().obtainMessage(CAMERA_IDLE);
- getHandler().sendMessage(msg);
- }
-
- @Override
- public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
- Message msg = getHandler().obtainMessage(CAPTURE_STARTED,
- /*arg1*/ (int) (timestamp & 0xFFFFFFFFL),
- /*arg2*/ (int) ( (timestamp >> 32) & 0xFFFFFFFFL),
- /*obj*/ resultExtras);
- getHandler().sendMessage(msg);
- }
-
- @Override
- public void onResultReceived(final CameraMetadataNative result,
- final CaptureResultExtras resultExtras,
- PhysicalCaptureResultInfo physicalResults[]) {
- Object[] resultArray = new Object[] { result, resultExtras };
- Message msg = getHandler().obtainMessage(RESULT_RECEIVED,
- /*obj*/ resultArray);
- getHandler().sendMessage(msg);
- }
-
- @Override
- public void onPrepared(int streamId) {
- Message msg = getHandler().obtainMessage(PREPARED,
- /*arg1*/ streamId, /*arg2*/ 0);
- getHandler().sendMessage(msg);
- }
-
- @Override
- public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
- Object[] objArray = new Object[] { lastFrameNumber, repeatingRequestId };
- Message msg = getHandler().obtainMessage(REPEATING_REQUEST_ERROR,
- /*obj*/ objArray);
- getHandler().sendMessage(msg);
- }
-
- @Override
- public void onRequestQueueEmpty() {
- Message msg = getHandler().obtainMessage(REQUEST_QUEUE_EMPTY,
- /* arg1 */ 0, /* arg2 */ 0);
- getHandler().sendMessage(msg);
- }
-
- @Override
- public IBinder asBinder() {
- // This is solely intended to be used for in-process binding.
- return null;
- }
-
- private Handler getHandler() {
- if (mHandler == null) {
- mHandler = new CallbackHandler(mHandlerThread.getLooper());
- }
- return mHandler;
- }
-
- private class CallbackHandler extends Handler {
- public CallbackHandler(Looper l) {
- super(l);
- }
-
- @Override
- public void handleMessage(Message msg) {
- try {
- switch (msg.what) {
- case CAMERA_ERROR: {
- int errorCode = msg.arg1;
- CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj;
- mCallbacks.onDeviceError(errorCode, resultExtras);
- break;
- }
- case CAMERA_IDLE:
- mCallbacks.onDeviceIdle();
- break;
- case CAPTURE_STARTED: {
- long timestamp = msg.arg2 & 0xFFFFFFFFL;
- timestamp = (timestamp << 32) | (msg.arg1 & 0xFFFFFFFFL);
- CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj;
- mCallbacks.onCaptureStarted(resultExtras, timestamp);
- break;
- }
- case RESULT_RECEIVED: {
- Object[] resultArray = (Object[]) msg.obj;
- CameraMetadataNative result = (CameraMetadataNative) resultArray[0];
- CaptureResultExtras resultExtras = (CaptureResultExtras) resultArray[1];
- mCallbacks.onResultReceived(result, resultExtras,
- new PhysicalCaptureResultInfo[0]);
- break;
- }
- case PREPARED: {
- int streamId = msg.arg1;
- mCallbacks.onPrepared(streamId);
- break;
- }
- case REPEATING_REQUEST_ERROR: {
- Object[] objArray = (Object[]) msg.obj;
- long lastFrameNumber = (Long) objArray[0];
- int repeatingRequestId = (Integer) objArray[1];
- mCallbacks.onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
- break;
- }
- case REQUEST_QUEUE_EMPTY: {
- mCallbacks.onRequestQueueEmpty();
- break;
- }
- default:
- throw new IllegalArgumentException(
- "Unknown callback message " + msg.what);
- }
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "Received remote exception during camera callback " + msg.what, e);
- }
- }
- }
- }
-
- public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks,
- int cameraId, Size displaySize) {
- if (DEBUG) {
- Log.d(TAG, "Opening shim Camera device");
- }
-
- /*
- * Put the camera open on a separate thread with its own looper; otherwise
- * if the main thread is used then the callbacks might never get delivered
- * (e.g. in CTS which run its own default looper only after tests)
- */
-
- CameraLooper init = new CameraLooper(cameraId);
-
- CameraCallbackThread threadCallbacks = new CameraCallbackThread(callbacks);
-
- // TODO: Make this async instead of blocking
- int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS);
- Camera legacyCamera = init.getCamera();
-
- // Check errors old HAL initialization
- LegacyExceptionUtils.throwOnServiceError(initErrors);
-
- // Disable shutter sounds (this will work unconditionally) for api2 clients
- legacyCamera.disableShutterSound();
-
- CameraInfo info = new CameraInfo();
- Camera.getCameraInfo(cameraId, info);
-
- Camera.Parameters legacyParameters = null;
- try {
- legacyParameters = legacyCamera.getParameters();
- } catch (RuntimeException e) {
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION,
- "Unable to get initial parameters: " + e.getMessage());
- }
-
- CameraCharacteristics characteristics =
- LegacyMetadataMapper.createCharacteristics(legacyParameters, info, cameraId,
- displaySize);
- LegacyCameraDevice device = new LegacyCameraDevice(
- cameraId, legacyCamera, characteristics, threadCallbacks);
- return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks);
- }
-
- @Override
- public void disconnect() {
- if (DEBUG) {
- Log.d(TAG, "disconnect called.");
- }
-
- if (mLegacyDevice.isClosed()) {
- Log.w(TAG, "Cannot disconnect, device has already been closed.");
- }
-
- try {
- mLegacyDevice.close();
- } finally {
- mCameraInit.close();
- mCameraCallbacks.close();
- }
- }
-
- @Override
- public SubmitInfo submitRequest(CaptureRequest request, boolean streaming) {
- if (DEBUG) {
- Log.d(TAG, "submitRequest called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot submit request, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- synchronized(mConfigureLock) {
- if (mConfiguring) {
- String err = "Cannot submit request, configuration change in progress.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
- }
- return mLegacyDevice.submitRequest(request, streaming);
- }
-
- @Override
- public SubmitInfo submitRequestList(CaptureRequest[] request, boolean streaming) {
- if (DEBUG) {
- Log.d(TAG, "submitRequestList called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot submit request list, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- synchronized(mConfigureLock) {
- if (mConfiguring) {
- String err = "Cannot submit request, configuration change in progress.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
- }
- return mLegacyDevice.submitRequestList(request, streaming);
- }
-
- @Override
- public long cancelRequest(int requestId) {
- if (DEBUG) {
- Log.d(TAG, "cancelRequest called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot cancel request, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- synchronized(mConfigureLock) {
- if (mConfiguring) {
- String err = "Cannot cancel request, configuration change in progress.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
- }
- return mLegacyDevice.cancelRequest(requestId);
- }
-
- @Override
- public boolean isSessionConfigurationSupported(SessionConfiguration sessionConfig) {
- if (sessionConfig.getSessionType() != SessionConfiguration.SESSION_REGULAR) {
- Log.e(TAG, "Session type: " + sessionConfig.getSessionType() + " is different from " +
- " regular. Legacy devices support only regular session types!");
- return false;
- }
-
- if (sessionConfig.getInputConfiguration() != null) {
- Log.e(TAG, "Input configuration present, legacy devices do not support this feature!");
- return false;
- }
-
- List<OutputConfiguration> outputConfigs = sessionConfig.getOutputConfigurations();
- if (outputConfigs.isEmpty()) {
- Log.e(TAG, "Empty output configuration list!");
- return false;
- }
-
- SparseArray<Surface> surfaces = new SparseArray<Surface>(outputConfigs.size());
- int idx = 0;
- for (OutputConfiguration outputConfig : outputConfigs) {
- List<Surface> surfaceList = outputConfig.getSurfaces();
- if (surfaceList.isEmpty() || (surfaceList.size() > 1)) {
- Log.e(TAG, "Legacy devices do not support deferred or shared surfaces!");
- return false;
- }
-
- surfaces.put(idx++, outputConfig.getSurface());
- }
-
- int ret = mLegacyDevice.configureOutputs(surfaces, /*validateSurfacesOnly*/true);
-
- return ret == LegacyExceptionUtils.NO_ERROR;
- }
-
- @Override
- public void beginConfigure() {
- if (DEBUG) {
- Log.d(TAG, "beginConfigure called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot begin configure, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- synchronized(mConfigureLock) {
- if (mConfiguring) {
- String err = "Cannot begin configure, configuration change already in progress.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
- mConfiguring = true;
- }
- }
-
- @Override
- public int[] endConfigure(int operatingMode, CameraMetadataNative sessionParams) {
- if (DEBUG) {
- Log.d(TAG, "endConfigure called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot end configure, device has been closed.";
- Log.e(TAG, err);
- synchronized(mConfigureLock) {
- mConfiguring = false;
- }
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- if (operatingMode != ICameraDeviceUser.NORMAL_MODE) {
- String err = "LEGACY devices do not support this operating mode";
- Log.e(TAG, err);
- synchronized(mConfigureLock) {
- mConfiguring = false;
- }
- throw new ServiceSpecificException(ICameraService.ERROR_ILLEGAL_ARGUMENT, err);
- }
-
- SparseArray<Surface> surfaces = null;
- synchronized(mConfigureLock) {
- if (!mConfiguring) {
- String err = "Cannot end configure, no configuration change in progress.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
- if (mSurfaces != null) {
- surfaces = mSurfaces.clone();
- }
- mConfiguring = false;
- }
- mLegacyDevice.configureOutputs(surfaces);
-
- return new int[0]; // Offline mode is not supported
- }
-
- @Override
- public void deleteStream(int streamId) {
- if (DEBUG) {
- Log.d(TAG, "deleteStream called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot delete stream, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- synchronized(mConfigureLock) {
- if (!mConfiguring) {
- String err = "Cannot delete stream, no configuration change in progress.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
- int index = mSurfaces.indexOfKey(streamId);
- if (index < 0) {
- String err = "Cannot delete stream, stream id " + streamId + " doesn't exist.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_ILLEGAL_ARGUMENT, err);
- }
- mSurfaces.removeAt(index);
- }
- }
-
- @Override
- public int createStream(OutputConfiguration outputConfiguration) {
- if (DEBUG) {
- Log.d(TAG, "createStream called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot create stream, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- synchronized(mConfigureLock) {
- if (!mConfiguring) {
- String err = "Cannot create stream, beginConfigure hasn't been called yet.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
- if (outputConfiguration.getRotation() != OutputConfiguration.ROTATION_0) {
- String err = "Cannot create stream, stream rotation is not supported.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_ILLEGAL_ARGUMENT, err);
- }
- int id = ++mSurfaceIdCounter;
- mSurfaces.put(id, outputConfiguration.getSurface());
- return id;
- }
- }
-
- @Override
- public void finalizeOutputConfigurations(int steamId, OutputConfiguration config) {
- String err = "Finalizing output configuration is not supported on legacy devices";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
-
- @Override
- public int createInputStream(int width, int height, int format) {
- String err = "Creating input stream is not supported on legacy devices";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
-
- @Override
- public Surface getInputSurface() {
- String err = "Getting input surface is not supported on legacy devices";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
-
- @Override
- public CameraMetadataNative createDefaultRequest(int templateId) {
- if (DEBUG) {
- Log.d(TAG, "createDefaultRequest called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot create default request, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- CameraMetadataNative template;
- try {
- template =
- LegacyMetadataMapper.createRequestTemplate(mCameraCharacteristics, templateId);
- } catch (IllegalArgumentException e) {
- String err = "createDefaultRequest - invalid templateId specified";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_ILLEGAL_ARGUMENT, err);
- }
-
- return template;
- }
-
- @Override
- public CameraMetadataNative getCameraInfo() {
- if (DEBUG) {
- Log.d(TAG, "getCameraInfo called.");
- }
- // TODO: implement getCameraInfo.
- Log.e(TAG, "getCameraInfo unimplemented.");
- return null;
- }
-
- @Override
- public void updateOutputConfiguration(int streamId, OutputConfiguration config) {
- // TODO: b/63912484 implement updateOutputConfiguration.
- }
-
- @Override
- public void waitUntilIdle() throws RemoteException {
- if (DEBUG) {
- Log.d(TAG, "waitUntilIdle called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot wait until idle, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- synchronized(mConfigureLock) {
- if (mConfiguring) {
- String err = "Cannot wait until idle, configuration change in progress.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
- }
- mLegacyDevice.waitUntilIdle();
- }
-
- @Override
- public long flush() {
- if (DEBUG) {
- Log.d(TAG, "flush called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot flush, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- synchronized(mConfigureLock) {
- if (mConfiguring) {
- String err = "Cannot flush, configuration change in progress.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
- }
- }
- return mLegacyDevice.flush();
- }
-
- public void prepare(int streamId) {
- if (DEBUG) {
- Log.d(TAG, "prepare called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot prepare stream, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- // LEGACY doesn't support actual prepare, just signal success right away
- mCameraCallbacks.onPrepared(streamId);
- }
-
- public void prepare2(int maxCount, int streamId) {
- // We don't support this in LEGACY mode.
- prepare(streamId);
- }
-
- public void tearDown(int streamId) {
- if (DEBUG) {
- Log.d(TAG, "tearDown called.");
- }
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot tear down stream, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- // LEGACY doesn't support actual teardown, so just a no-op
- }
-
- @Override
- public void setCameraAudioRestriction(int mode) {
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot set camera audio restriction, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- mLegacyDevice.setAudioRestriction(mode);
- }
-
- @Override
- public int getGlobalAudioRestriction() {
- if (mLegacyDevice.isClosed()) {
- String err = "Cannot set camera audio restriction, device has been closed.";
- Log.e(TAG, err);
- throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
- }
-
- return mLegacyDevice.getAudioRestriction();
- }
-
- @Override
- public ICameraOfflineSession switchToOffline(ICameraDeviceCallbacks cbs,
- int[] offlineOutputIds) {
- throw new UnsupportedOperationException("Legacy device does not support offline mode");
- }
-
- @Override
- public IBinder asBinder() {
- // This is solely intended to be used for in-process binding.
- return null;
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/CaptureCollector.java b/core/java/android/hardware/camera2/legacy/CaptureCollector.java
deleted file mode 100644
index 113927c4c4b2..000000000000
--- a/core/java/android/hardware/camera2/legacy/CaptureCollector.java
+++ /dev/null
@@ -1,673 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.legacy;
-
-import android.hardware.camera2.impl.CameraDeviceImpl;
-import android.util.Log;
-import android.util.MutableLong;
-import android.util.Pair;
-import android.view.Surface;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.TreeSet;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Collect timestamps and state for each {@link CaptureRequest} as it passes through
- * the Legacy camera pipeline.
- */
-public class CaptureCollector {
- private static final String TAG = "CaptureCollector";
-
- private static final boolean DEBUG = false;
-
- private static final int FLAG_RECEIVED_JPEG = 1;
- private static final int FLAG_RECEIVED_JPEG_TS = 2;
- private static final int FLAG_RECEIVED_PREVIEW = 4;
- private static final int FLAG_RECEIVED_PREVIEW_TS = 8;
- private static final int FLAG_RECEIVED_ALL_JPEG = FLAG_RECEIVED_JPEG | FLAG_RECEIVED_JPEG_TS;
- private static final int FLAG_RECEIVED_ALL_PREVIEW = FLAG_RECEIVED_PREVIEW |
- FLAG_RECEIVED_PREVIEW_TS;
-
- private static final int MAX_JPEGS_IN_FLIGHT = 1;
-
- private class CaptureHolder implements Comparable<CaptureHolder>{
- private final RequestHolder mRequest;
- private final LegacyRequest mLegacy;
- public final boolean needsJpeg;
- public final boolean needsPreview;
-
- private long mTimestamp = 0;
- private int mReceivedFlags = 0;
- private boolean mHasStarted = false;
- private boolean mFailedJpeg = false;
- private boolean mFailedPreview = false;
- private boolean mCompleted = false;
- private boolean mPreviewCompleted = false;
-
- public CaptureHolder(RequestHolder request, LegacyRequest legacyHolder) {
- mRequest = request;
- mLegacy = legacyHolder;
- needsJpeg = request.hasJpegTargets();
- needsPreview = request.hasPreviewTargets();
- }
-
- public boolean isPreviewCompleted() {
- return (mReceivedFlags & FLAG_RECEIVED_ALL_PREVIEW) == FLAG_RECEIVED_ALL_PREVIEW;
- }
-
- public boolean isJpegCompleted() {
- return (mReceivedFlags & FLAG_RECEIVED_ALL_JPEG) == FLAG_RECEIVED_ALL_JPEG;
- }
-
- public boolean isCompleted() {
- return (needsJpeg == isJpegCompleted()) && (needsPreview == isPreviewCompleted());
- }
-
- public void tryComplete() {
- if (!mPreviewCompleted && needsPreview && isPreviewCompleted()) {
- CaptureCollector.this.onPreviewCompleted();
- mPreviewCompleted = true;
- }
-
- if (isCompleted() && !mCompleted) {
- if (mFailedPreview || mFailedJpeg) {
- if (!mHasStarted) {
- // Send a request error if the capture has not yet started.
- mRequest.failRequest();
- CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_REQUEST);
- } else {
- // Send buffer dropped errors for each pending buffer if the request has
- // started.
- for (Surface targetSurface : mRequest.getRequest().getTargets() ) {
- try {
- if (mRequest.jpegType(targetSurface)) {
- if (mFailedJpeg) {
- CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
- /*result*/null,
- CameraDeviceImpl.CameraDeviceCallbacks.
- ERROR_CAMERA_BUFFER,
- targetSurface);
- }
- } else {
- // preview buffer
- if (mFailedPreview) {
- CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
- /*result*/null,
- CameraDeviceImpl.CameraDeviceCallbacks.
- ERROR_CAMERA_BUFFER,
- targetSurface);
- }
- }
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.e(TAG, "Unexpected exception when querying Surface: " + e);
- }
- }
- }
- }
- CaptureCollector.this.onRequestCompleted(CaptureHolder.this);
- mCompleted = true;
- }
- }
-
- public void setJpegTimestamp(long timestamp) {
- if (DEBUG) {
- Log.d(TAG, "setJpegTimestamp - called for request " + mRequest.getRequestId());
- }
- if (!needsJpeg) {
- throw new IllegalStateException(
- "setJpegTimestamp called for capture with no jpeg targets.");
- }
- if (isCompleted()) {
- throw new IllegalStateException(
- "setJpegTimestamp called on already completed request.");
- }
-
- mReceivedFlags |= FLAG_RECEIVED_JPEG_TS;
-
- if (mTimestamp == 0) {
- mTimestamp = timestamp;
- }
-
- if (!mHasStarted) {
- mHasStarted = true;
- CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
- CameraDeviceState.NO_CAPTURE_ERROR);
- }
-
- tryComplete();
- }
-
- public void setJpegProduced() {
- if (DEBUG) {
- Log.d(TAG, "setJpegProduced - called for request " + mRequest.getRequestId());
- }
- if (!needsJpeg) {
- throw new IllegalStateException(
- "setJpegProduced called for capture with no jpeg targets.");
- }
- if (isCompleted()) {
- throw new IllegalStateException(
- "setJpegProduced called on already completed request.");
- }
-
- mReceivedFlags |= FLAG_RECEIVED_JPEG;
- tryComplete();
- }
-
- public void setJpegFailed() {
- if (DEBUG) {
- Log.d(TAG, "setJpegFailed - called for request " + mRequest.getRequestId());
- }
- if (!needsJpeg || isJpegCompleted()) {
- return;
- }
- mFailedJpeg = true;
-
- mReceivedFlags |= FLAG_RECEIVED_JPEG;
- mReceivedFlags |= FLAG_RECEIVED_JPEG_TS;
- tryComplete();
- }
-
- public void setPreviewTimestamp(long timestamp) {
- if (DEBUG) {
- Log.d(TAG, "setPreviewTimestamp - called for request " + mRequest.getRequestId());
- }
- if (!needsPreview) {
- throw new IllegalStateException(
- "setPreviewTimestamp called for capture with no preview targets.");
- }
- if (isCompleted()) {
- throw new IllegalStateException(
- "setPreviewTimestamp called on already completed request.");
- }
-
- mReceivedFlags |= FLAG_RECEIVED_PREVIEW_TS;
-
- if (mTimestamp == 0) {
- mTimestamp = timestamp;
- }
-
- if (!needsJpeg) {
- if (!mHasStarted) {
- mHasStarted = true;
- CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
- CameraDeviceState.NO_CAPTURE_ERROR);
- }
- }
-
- tryComplete();
- }
-
- public void setPreviewProduced() {
- if (DEBUG) {
- Log.d(TAG, "setPreviewProduced - called for request " + mRequest.getRequestId());
- }
- if (!needsPreview) {
- throw new IllegalStateException(
- "setPreviewProduced called for capture with no preview targets.");
- }
- if (isCompleted()) {
- throw new IllegalStateException(
- "setPreviewProduced called on already completed request.");
- }
-
- mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
- tryComplete();
- }
-
- public void setPreviewFailed() {
- if (DEBUG) {
- Log.d(TAG, "setPreviewFailed - called for request " + mRequest.getRequestId());
- }
- if (!needsPreview || isPreviewCompleted()) {
- return;
- }
- mFailedPreview = true;
-
- mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
- mReceivedFlags |= FLAG_RECEIVED_PREVIEW_TS;
- tryComplete();
- }
-
- // Comparison and equals based on frame number.
- @Override
- public int compareTo(CaptureHolder captureHolder) {
- return (mRequest.getFrameNumber() > captureHolder.mRequest.getFrameNumber()) ? 1 :
- ((mRequest.getFrameNumber() == captureHolder.mRequest.getFrameNumber()) ? 0 :
- -1);
- }
-
- // Comparison and equals based on frame number.
- @Override
- public boolean equals(Object o) {
- return o instanceof CaptureHolder && compareTo((CaptureHolder) o) == 0;
- }
- }
-
- private final TreeSet<CaptureHolder> mActiveRequests;
- private final ArrayDeque<CaptureHolder> mJpegCaptureQueue;
- private final ArrayDeque<CaptureHolder> mJpegProduceQueue;
- private final ArrayDeque<CaptureHolder> mPreviewCaptureQueue;
- private final ArrayDeque<CaptureHolder> mPreviewProduceQueue;
- private final ArrayList<CaptureHolder> mCompletedRequests = new ArrayList<>();
-
- private final ReentrantLock mLock = new ReentrantLock();
- private final Condition mIsEmpty;
- private final Condition mPreviewsEmpty;
- private final Condition mNotFull;
- private final CameraDeviceState mDeviceState;
- private int mInFlight = 0;
- private int mInFlightPreviews = 0;
- private final int mMaxInFlight;
-
- /**
- * Create a new {@link CaptureCollector} that can modify the given {@link CameraDeviceState}.
- *
- * @param maxInFlight max allowed in-flight requests.
- * @param deviceState the {@link CameraDeviceState} to update as requests are processed.
- */
- public CaptureCollector(int maxInFlight, CameraDeviceState deviceState) {
- mMaxInFlight = maxInFlight;
- mJpegCaptureQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
- mJpegProduceQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
- mPreviewCaptureQueue = new ArrayDeque<>(mMaxInFlight);
- mPreviewProduceQueue = new ArrayDeque<>(mMaxInFlight);
- mActiveRequests = new TreeSet<>();
- mIsEmpty = mLock.newCondition();
- mNotFull = mLock.newCondition();
- mPreviewsEmpty = mLock.newCondition();
- mDeviceState = deviceState;
- }
-
- /**
- * Queue a new request.
- *
- * <p>
- * For requests that use the Camera1 API preview output stream, this will block if there are
- * already {@code maxInFlight} requests in progress (until at least one prior request has
- * completed). For requests that use the Camera1 API jpeg callbacks, this will block until
- * all prior requests have been completed to avoid stopping preview for
- * {@link android.hardware.Camera#takePicture} before prior preview requests have been
- * completed.
- * </p>
- * @param holder the {@link RequestHolder} for this request.
- * @param legacy the {@link LegacyRequest} for this request; this will not be mutated.
- * @param timeout a timeout to use for this call.
- * @param unit the units to use for the timeout.
- * @return {@code false} if this method timed out.
- * @throws InterruptedException if this thread is interrupted.
- */
- public boolean queueRequest(RequestHolder holder, LegacyRequest legacy, long timeout,
- TimeUnit unit)
- throws InterruptedException {
- CaptureHolder h = new CaptureHolder(holder, legacy);
- long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- if (DEBUG) {
- Log.d(TAG, "queueRequest for request " + holder.getRequestId() +
- " - " + mInFlight + " requests remain in flight.");
- }
-
- if (!(h.needsJpeg || h.needsPreview)) {
- throw new IllegalStateException("Request must target at least one output surface!");
- }
-
- if (h.needsJpeg) {
- // Wait for all current requests to finish before queueing jpeg.
- while (mInFlight > 0) {
- if (nanos <= 0) {
- return false;
- }
- nanos = mIsEmpty.awaitNanos(nanos);
- }
- mJpegCaptureQueue.add(h);
- mJpegProduceQueue.add(h);
- }
- if (h.needsPreview) {
- while (mInFlight >= mMaxInFlight) {
- if (nanos <= 0) {
- return false;
- }
- nanos = mNotFull.awaitNanos(nanos);
- }
- mPreviewCaptureQueue.add(h);
- mPreviewProduceQueue.add(h);
- mInFlightPreviews++;
- }
- mActiveRequests.add(h);
-
- mInFlight++;
- return true;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Wait all queued requests to complete.
- *
- * @param timeout a timeout to use for this call.
- * @param unit the units to use for the timeout.
- * @return {@code false} if this method timed out.
- * @throws InterruptedException if this thread is interrupted.
- */
- public boolean waitForEmpty(long timeout, TimeUnit unit) throws InterruptedException {
- long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- while (mInFlight > 0) {
- if (nanos <= 0) {
- return false;
- }
- nanos = mIsEmpty.awaitNanos(nanos);
- }
- return true;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Wait all queued requests that use the Camera1 API preview output to complete.
- *
- * @param timeout a timeout to use for this call.
- * @param unit the units to use for the timeout.
- * @return {@code false} if this method timed out.
- * @throws InterruptedException if this thread is interrupted.
- */
- public boolean waitForPreviewsEmpty(long timeout, TimeUnit unit) throws InterruptedException {
- long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- while (mInFlightPreviews > 0) {
- if (nanos <= 0) {
- return false;
- }
- nanos = mPreviewsEmpty.awaitNanos(nanos);
- }
- return true;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Wait for the specified request to be completed (all buffers available).
- *
- * <p>May not wait for the same request more than once, since a successful wait
- * will erase the history of that request.</p>
- *
- * @param holder the {@link RequestHolder} for this request.
- * @param timeout a timeout to use for this call.
- * @param unit the units to use for the timeout.
- * @param timestamp the timestamp of the request will be written out to here, in ns
- *
- * @return {@code false} if this method timed out.
- *
- * @throws InterruptedException if this thread is interrupted.
- */
- public boolean waitForRequestCompleted(RequestHolder holder, long timeout, TimeUnit unit,
- MutableLong timestamp)
- throws InterruptedException {
- long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- while (!removeRequestIfCompleted(holder, /*out*/timestamp)) {
- if (nanos <= 0) {
- return false;
- }
- nanos = mNotFull.awaitNanos(nanos);
- }
- return true;
- } finally {
- lock.unlock();
- }
- }
-
- private boolean removeRequestIfCompleted(RequestHolder holder, MutableLong timestamp) {
- int i = 0;
- for (CaptureHolder h : mCompletedRequests) {
- if (h.mRequest.equals(holder)) {
- timestamp.value = h.mTimestamp;
- mCompletedRequests.remove(i);
- return true;
- }
- i++;
- }
-
- return false;
- }
-
- /**
- * Called to alert the {@link CaptureCollector} that the jpeg capture has begun.
- *
- * @param timestamp the time of the jpeg capture.
- * @return the {@link RequestHolder} for the request associated with this capture.
- */
- public RequestHolder jpegCaptured(long timestamp) {
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- CaptureHolder h = mJpegCaptureQueue.poll();
- if (h == null) {
- Log.w(TAG, "jpegCaptured called with no jpeg request on queue!");
- return null;
- }
- h.setJpegTimestamp(timestamp);
- return h.mRequest;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Called to alert the {@link CaptureCollector} that the jpeg capture has completed.
- *
- * @return a pair containing the {@link RequestHolder} and the timestamp of the capture.
- */
- public Pair<RequestHolder, Long> jpegProduced() {
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- CaptureHolder h = mJpegProduceQueue.poll();
- if (h == null) {
- Log.w(TAG, "jpegProduced called with no jpeg request on queue!");
- return null;
- }
- h.setJpegProduced();
- return new Pair<>(h.mRequest, h.mTimestamp);
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Check if there are any pending capture requests that use the Camera1 API preview output.
- *
- * @return {@code true} if there are pending preview requests.
- */
- public boolean hasPendingPreviewCaptures() {
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- return !mPreviewCaptureQueue.isEmpty();
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Called to alert the {@link CaptureCollector} that the preview capture has begun.
- *
- * @param timestamp the time of the preview capture.
- * @return a pair containing the {@link RequestHolder} and the timestamp of the capture.
- */
- public Pair<RequestHolder, Long> previewCaptured(long timestamp) {
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- CaptureHolder h = mPreviewCaptureQueue.poll();
- if (h == null) {
- if (DEBUG) {
- Log.d(TAG, "previewCaptured called with no preview request on queue!");
- }
- return null;
- }
- h.setPreviewTimestamp(timestamp);
- return new Pair<>(h.mRequest, h.mTimestamp);
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Called to alert the {@link CaptureCollector} that the preview capture has completed.
- *
- * @return the {@link RequestHolder} for the request associated with this capture.
- */
- public RequestHolder previewProduced() {
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- CaptureHolder h = mPreviewProduceQueue.poll();
- if (h == null) {
- Log.w(TAG, "previewProduced called with no preview request on queue!");
- return null;
- }
- h.setPreviewProduced();
- return h.mRequest;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Called to alert the {@link CaptureCollector} that the next pending preview capture has failed.
- */
- public void failNextPreview() {
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- CaptureHolder h1 = mPreviewCaptureQueue.peek();
- CaptureHolder h2 = mPreviewProduceQueue.peek();
-
- // Find the request with the lowest frame number.
- CaptureHolder h = (h1 == null) ? h2 :
- ((h2 == null) ? h1 :
- ((h1.compareTo(h2) <= 0) ? h1 :
- h2));
-
- if (h != null) {
- mPreviewCaptureQueue.remove(h);
- mPreviewProduceQueue.remove(h);
- mActiveRequests.remove(h);
- h.setPreviewFailed();
- }
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Called to alert the {@link CaptureCollector} that the next pending jpeg capture has failed.
- */
- public void failNextJpeg() {
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- CaptureHolder h1 = mJpegCaptureQueue.peek();
- CaptureHolder h2 = mJpegProduceQueue.peek();
-
- // Find the request with the lowest frame number.
- CaptureHolder h = (h1 == null) ? h2 :
- ((h2 == null) ? h1 :
- ((h1.compareTo(h2) <= 0) ? h1 :
- h2));
-
- if (h != null) {
- mJpegCaptureQueue.remove(h);
- mJpegProduceQueue.remove(h);
- mActiveRequests.remove(h);
- h.setJpegFailed();
- }
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Called to alert the {@link CaptureCollector} all pending captures have failed.
- */
- public void failAll() {
- final ReentrantLock lock = this.mLock;
- lock.lock();
- try {
- CaptureHolder h;
- while ((h = mActiveRequests.pollFirst()) != null) {
- h.setPreviewFailed();
- h.setJpegFailed();
- }
- mPreviewCaptureQueue.clear();
- mPreviewProduceQueue.clear();
- mJpegCaptureQueue.clear();
- mJpegProduceQueue.clear();
- } finally {
- lock.unlock();
- }
- }
-
- private void onPreviewCompleted() {
- mInFlightPreviews--;
- if (mInFlightPreviews < 0) {
- throw new IllegalStateException(
- "More preview captures completed than requests queued.");
- }
- if (mInFlightPreviews == 0) {
- mPreviewsEmpty.signalAll();
- }
- }
-
- private void onRequestCompleted(CaptureHolder capture) {
- RequestHolder request = capture.mRequest;
-
- mInFlight--;
- if (DEBUG) {
- Log.d(TAG, "Completed request " + request.getRequestId() +
- ", " + mInFlight + " requests remain in flight.");
- }
- if (mInFlight < 0) {
- throw new IllegalStateException(
- "More captures completed than requests queued.");
- }
-
- mCompletedRequests.add(capture);
- mActiveRequests.remove(capture);
-
- mNotFull.signalAll();
- if (mInFlight == 0) {
- mIsEmpty.signalAll();
- }
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
deleted file mode 100644
index 152d82d5a6da..000000000000
--- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.graphics.SurfaceTexture;
-import android.hardware.camera2.impl.CameraDeviceImpl;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Size;
-import android.view.Surface;
-
-import java.util.Collection;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * GLThreadManager handles the thread used for rendering into the configured output surfaces.
- */
-public class GLThreadManager {
- private final String TAG;
- private static final boolean DEBUG = false;
-
- private static final int MSG_NEW_CONFIGURATION = 1;
- private static final int MSG_NEW_FRAME = 2;
- private static final int MSG_CLEANUP = 3;
- private static final int MSG_DROP_FRAMES = 4;
- private static final int MSG_ALLOW_FRAMES = 5;
-
- private CaptureCollector mCaptureCollector;
-
- private final CameraDeviceState mDeviceState;
-
- private final SurfaceTextureRenderer mTextureRenderer;
-
- private final RequestHandlerThread mGLHandlerThread;
-
- private final RequestThreadManager.FpsCounter mPrevCounter =
- new RequestThreadManager.FpsCounter("GL Preview Producer");
-
- /**
- * Container object for Configure messages.
- */
- private static class ConfigureHolder {
- public final ConditionVariable condition;
- public final Collection<Pair<Surface, Size>> surfaces;
- public final CaptureCollector collector;
-
- public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface,
- Size>> surfaces, CaptureCollector collector) {
- this.condition = condition;
- this.surfaces = surfaces;
- this.collector = collector;
- }
- }
-
- private final Handler.Callback mGLHandlerCb = new Handler.Callback() {
- private boolean mCleanup = false;
- private boolean mConfigured = false;
- private boolean mDroppingFrames = false;
-
- @SuppressWarnings("unchecked")
- @Override
- public boolean handleMessage(Message msg) {
- if (mCleanup) {
- return true;
- }
- try {
- switch (msg.what) {
- case MSG_NEW_CONFIGURATION:
- ConfigureHolder configure = (ConfigureHolder) msg.obj;
- mTextureRenderer.cleanupEGLContext();
- mTextureRenderer.configureSurfaces(configure.surfaces);
- mCaptureCollector = checkNotNull(configure.collector);
- configure.condition.open();
- mConfigured = true;
- break;
- case MSG_NEW_FRAME:
- if (mDroppingFrames) {
- Log.w(TAG, "Ignoring frame.");
- break;
- }
- if (DEBUG) {
- mPrevCounter.countAndLog();
- }
- if (!mConfigured) {
- Log.e(TAG, "Dropping frame, EGL context not configured!");
- }
- mTextureRenderer.drawIntoSurfaces(mCaptureCollector);
- break;
- case MSG_CLEANUP:
- mTextureRenderer.cleanupEGLContext();
- mCleanup = true;
- mConfigured = false;
- break;
- case MSG_DROP_FRAMES:
- mDroppingFrames = true;
- break;
- case MSG_ALLOW_FRAMES:
- mDroppingFrames = false;
- break;
- case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
- // OK: Ignore message.
- break;
- default:
- Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
- break;
- }
- } catch (Exception e) {
- Log.e(TAG, "Received exception on GL render thread: ", e);
- mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- }
- return true;
- }
- };
-
- /**
- * Create a new GL thread and renderer.
- *
- * @param cameraId the camera id for this thread.
- * @param facing direction the camera is facing.
- * @param state {@link CameraDeviceState} to use for error handling.
- */
- public GLThreadManager(int cameraId, int facing, CameraDeviceState state) {
- mTextureRenderer = new SurfaceTextureRenderer(facing);
- TAG = String.format("CameraDeviceGLThread-%d", cameraId);
- mGLHandlerThread = new RequestHandlerThread(TAG, mGLHandlerCb);
- mDeviceState = state;
- }
-
- /**
- * Start the thread.
- *
- * <p>
- * This must be called before queueing new frames.
- * </p>
- */
- public void start() {
- mGLHandlerThread.start();
- }
-
- /**
- * Wait until the thread has started.
- */
- public void waitUntilStarted() {
- mGLHandlerThread.waitUntilStarted();
- }
-
- /**
- * Quit the thread.
- *
- * <p>
- * No further methods can be called after this.
- * </p>
- */
- public void quit() {
- Handler handler = mGLHandlerThread.getHandler();
- handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
- mGLHandlerThread.quitSafely();
- try {
- mGLHandlerThread.join();
- } catch (InterruptedException e) {
- Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
- mGLHandlerThread.getName(), mGLHandlerThread.getId()));
- }
- }
-
- /**
- * Queue a new call to draw into the surfaces specified in the next available preview
- * request from the {@link CaptureCollector} passed to
- * {@link #setConfigurationAndWait(java.util.Collection, CaptureCollector)};
- */
- public void queueNewFrame() {
- Handler handler = mGLHandlerThread.getHandler();
-
- /**
- * Avoid queuing more than one new frame. If we are not consuming faster than frames
- * are produced, drop frames rather than allowing the queue to back up.
- */
- if (!handler.hasMessages(MSG_NEW_FRAME)) {
- handler.sendMessage(handler.obtainMessage(MSG_NEW_FRAME));
- } else {
- Log.e(TAG, "GLThread dropping frame. Not consuming frames quickly enough!");
- }
- }
-
- /**
- * Configure the GL renderer for the given set of output surfaces, and block until
- * this configuration has been applied.
- *
- * @param surfaces a collection of pairs of {@link android.view.Surface}s and their
- * corresponding sizes to configure.
- * @param collector a {@link CaptureCollector} to retrieve requests from.
- */
- public void setConfigurationAndWait(Collection<Pair<Surface, Size>> surfaces,
- CaptureCollector collector) {
- checkNotNull(collector, "collector must not be null");
- Handler handler = mGLHandlerThread.getHandler();
-
- final ConditionVariable condition = new ConditionVariable(/*closed*/false);
- ConfigureHolder configure = new ConfigureHolder(condition, surfaces, collector);
-
- Message m = handler.obtainMessage(MSG_NEW_CONFIGURATION, /*arg1*/0, /*arg2*/0, configure);
- handler.sendMessage(m);
-
- // Block until configuration applied.
- condition.block();
- }
-
- /**
- * Get the underlying surface to produce frames from.
- *
- * <p>
- * This returns the surface that is drawn into the set of surfaces passed in for each frame.
- * This method should only be called after a call to
- * {@link #setConfigurationAndWait(java.util.Collection)}. Calling this before the first call
- * to {@link #setConfigurationAndWait(java.util.Collection)}, after {@link #quit()}, or
- * concurrently to one of these calls may result in an invalid
- * {@link android.graphics.SurfaceTexture} being returned.
- * </p>
- *
- * @return an {@link android.graphics.SurfaceTexture} to draw to.
- */
- public SurfaceTexture getCurrentSurfaceTexture() {
- return mTextureRenderer.getSurfaceTexture();
- }
-
- /**
- * Ignore any subsequent calls to {@link #queueNewFrame(java.util.Collection)}.
- */
- public void ignoreNewFrames() {
- mGLHandlerThread.getHandler().sendEmptyMessage(MSG_DROP_FRAMES);
- }
-
- /**
- * Wait until no messages are queued.
- */
- public void waitUntilIdle() {
- mGLHandlerThread.waitUntilIdle();
- }
-
- /**
- * Re-enable drawing new frames after a call to {@link #ignoreNewFrames()}.
- */
- public void allowNewFrames() {
- mGLHandlerThread.getHandler().sendEmptyMessage(MSG_ALLOW_FRAMES);
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
deleted file mode 100644
index fdd578c419d8..000000000000
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ /dev/null
@@ -1,886 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.graphics.ImageFormat;
-import android.graphics.SurfaceTexture;
-import android.hardware.Camera;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.impl.CameraDeviceImpl;
-import android.hardware.camera2.impl.CaptureResultExtras;
-import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
-import android.hardware.camera2.ICameraDeviceCallbacks;
-import android.hardware.camera2.params.StreamConfigurationMap;
-import android.hardware.camera2.utils.ArrayUtils;
-import android.hardware.camera2.utils.SubmitInfo;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Size;
-import android.util.SparseArray;
-import android.view.Surface;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * This class emulates the functionality of a Camera2 device using a the old Camera class.
- *
- * <p>
- * There are two main components that are used to implement this:
- * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}).
- * - A message-queue based pipeline that manages an old Camera class, and executes capture and
- * configuration requests.
- * </p>
- */
-public class LegacyCameraDevice implements AutoCloseable {
- private final String TAG;
-
- private static final boolean DEBUG = false;
- private final int mCameraId;
- private final CameraCharacteristics mStaticCharacteristics;
- private final ICameraDeviceCallbacks mDeviceCallbacks;
- private final CameraDeviceState mDeviceState = new CameraDeviceState();
- private SparseArray<Surface> mConfiguredSurfaces;
- private boolean mClosed = false;
-
- private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
-
- private final HandlerThread mResultThread = new HandlerThread("ResultThread");
- private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
- private final Handler mCallbackHandler;
- private final Handler mResultHandler;
- private static final int ILLEGAL_VALUE = -1;
-
- // Keep up to date with values in hardware/libhardware/include/hardware/gralloc.h
- private static final int GRALLOC_USAGE_RENDERSCRIPT = 0x00100000;
- private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003;
- private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100;
- private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800;
- private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200;
- private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
-
- public static final int MAX_DIMEN_FOR_ROUNDING = 1920; // maximum allowed width for rounding
-
- // Keep up to date with values in system/core/include/system/window.h
- public static final int NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1;
-
- private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
- return getExtrasFromRequest(holder,
- /*errorCode*/CameraDeviceState.NO_CAPTURE_ERROR, /*errorArg*/null);
- }
-
- private CaptureResultExtras getExtrasFromRequest(RequestHolder holder,
- int errorCode, Object errorArg) {
- int errorStreamId = -1;
- if (errorCode == CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER) {
- Surface errorTarget = (Surface) errorArg;
- int indexOfTarget = mConfiguredSurfaces.indexOfValue(errorTarget);
- if (indexOfTarget < 0) {
- Log.e(TAG, "Buffer drop error reported for unknown Surface");
- } else {
- errorStreamId = mConfiguredSurfaces.keyAt(indexOfTarget);
- }
- }
- if (holder == null) {
- return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
- ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, null,
- ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
- }
- return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
- /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
- /*partialResultCount*/1, errorStreamId, null, holder.getFrameNumber(), -1, -1);
- }
-
- /**
- * Listener for the camera device state machine. Calls the appropriate
- * {@link ICameraDeviceCallbacks} for each state transition.
- */
- private final CameraDeviceState.CameraDeviceStateListener mStateListener =
- new CameraDeviceState.CameraDeviceStateListener() {
- @Override
- public void onError(final int errorCode, final Object errorArg, final RequestHolder holder) {
- if (DEBUG) {
- Log.d(TAG, "onError called, errorCode = " + errorCode + ", errorArg = " + errorArg);
- }
- switch (errorCode) {
- /*
- * Only be considered idle if we hit a fatal error
- * and no further requests can be processed.
- */
- case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED:
- case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE:
- case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: {
- mIdle.open();
-
- if (DEBUG) {
- Log.d(TAG, "onError - opening idle");
- }
- }
- }
-
- final CaptureResultExtras extras = getExtrasFromRequest(holder, errorCode, errorArg);
- mResultHandler.post(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(TAG, "doing onError callback for request " + holder.getRequestId() +
- ", with error code " + errorCode);
- }
- try {
- mDeviceCallbacks.onDeviceError(errorCode, extras);
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "Received remote exception during onCameraError callback: ", e);
- }
- }
- });
- }
-
- @Override
- public void onConfiguring() {
- // Do nothing
- if (DEBUG) {
- Log.d(TAG, "doing onConfiguring callback.");
- }
- }
-
- @Override
- public void onIdle() {
- if (DEBUG) {
- Log.d(TAG, "onIdle called");
- }
-
- mIdle.open();
-
- mResultHandler.post(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(TAG, "doing onIdle callback.");
- }
- try {
- mDeviceCallbacks.onDeviceIdle();
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "Received remote exception during onCameraIdle callback: ", e);
- }
- }
- });
- }
-
- @Override
- public void onBusy() {
- mIdle.close();
-
- if (DEBUG) {
- Log.d(TAG, "onBusy called");
- }
- }
-
- @Override
- public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
- final CaptureResultExtras extras = getExtrasFromRequest(holder);
-
- mResultHandler.post(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(TAG, "doing onCaptureStarted callback for request " +
- holder.getRequestId());
- }
- try {
- mDeviceCallbacks.onCaptureStarted(extras, timestamp);
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "Received remote exception during onCameraError callback: ", e);
- }
- }
- });
- }
-
- @Override
- public void onRequestQueueEmpty() {
- mResultHandler.post(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(TAG, "doing onRequestQueueEmpty callback");
- }
- try {
- mDeviceCallbacks.onRequestQueueEmpty();
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "Received remote exception during onRequestQueueEmpty callback: ",
- e);
- }
- }
- });
- }
-
- @Override
- public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
- final CaptureResultExtras extras = getExtrasFromRequest(holder);
-
- mResultHandler.post(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(TAG, "doing onCaptureResult callback for request " +
- holder.getRequestId());
- }
- try {
- mDeviceCallbacks.onResultReceived(result, extras,
- new PhysicalCaptureResultInfo[0]);
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "Received remote exception during onCameraError callback: ", e);
- }
- }
- });
- }
-
- @Override
- public void onRepeatingRequestError(final long lastFrameNumber,
- final int repeatingRequestId) {
- mResultHandler.post(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(TAG, "doing onRepeatingRequestError callback.");
- }
- try {
- mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber,
- repeatingRequestId);
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "Received remote exception during onRepeatingRequestError " +
- "callback: ", e);
- }
- }
- });
- }
- };
-
- private final RequestThreadManager mRequestThreadManager;
-
- /**
- * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
- * converted to this; YV12 and NV21 are the two currently supported formats.
- *
- * @param s the surface to check.
- * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
- * format.
- */
- static boolean needsConversion(Surface s) throws BufferQueueAbandonedException {
- int nativeType = detectSurfaceType(s);
- return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
- nativeType == ImageFormat.NV21;
- }
-
- /**
- * Create a new emulated camera device from a given Camera 1 API camera.
- *
- * <p>
- * The {@link Camera} provided to this constructor must already have been successfully opened,
- * and ownership of the provided camera is passed to this object. No further calls to the
- * camera methods should be made following this constructor.
- * </p>
- *
- * @param cameraId the id of the camera.
- * @param camera an open {@link Camera} device.
- * @param characteristics the static camera characteristics for this camera device
- * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
- */
- public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
- ICameraDeviceCallbacks callbacks) {
- mCameraId = cameraId;
- mDeviceCallbacks = callbacks;
- TAG = String.format("CameraDevice-%d-LE", mCameraId);
-
- mResultThread.start();
- mResultHandler = new Handler(mResultThread.getLooper());
- mCallbackHandlerThread.start();
- mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
- mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
- mStaticCharacteristics = characteristics;
- mRequestThreadManager =
- new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
- mRequestThreadManager.start();
- }
-
- /**
- * Configure the device with a set of output surfaces.
- *
- * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
- *
- * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
- *
- * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this
- * list; it must not be modified by the caller once it's passed in.
- * @return an error code for this binder operation, or {@link NO_ERROR}
- * on success.
- */
- public int configureOutputs(SparseArray<Surface> outputs) {
- return configureOutputs(outputs, /*validateSurfacesOnly*/false);
- }
-
- /**
- * Configure the device with a set of output surfaces.
- *
- * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
- *
- * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
- *
- * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this
- * list; it must not be modified by the caller once it's passed in.
- * @param validateSurfacesOnly If set it will only check whether the outputs are supported
- * and avoid any device configuration.
- * @return an error code for this binder operation, or {@link NO_ERROR}
- * on success.
- * @hide
- */
- public int configureOutputs(SparseArray<Surface> outputs, boolean validateSurfacesOnly) {
- List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
- if (outputs != null) {
- int count = outputs.size();
- for (int i = 0; i < count; i++) {
- Surface output = outputs.valueAt(i);
- if (output == null) {
- Log.e(TAG, "configureOutputs - null outputs are not allowed");
- return BAD_VALUE;
- }
- if (!output.isValid()) {
- Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed");
- return BAD_VALUE;
- }
- StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
- get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-
- // Validate surface size and format.
- try {
- Size s = getSurfaceSize(output);
- int surfaceType = detectSurfaceType(output);
-
- boolean flexibleConsumer = isFlexibleConsumer(output);
-
- Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
- if (sizes == null) {
- if (surfaceType == ImageFormat.PRIVATE) {
-
- // YUV_420_888 is always present in LEGACY for all
- // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
- // API (i.e. {@code #getOutputSizes} works here).
- sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
- } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
- sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
- }
- }
-
- if (!ArrayUtils.contains(sizes, s)) {
- if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
- sizedSurfaces.add(new Pair<>(output, s));
- } else {
- String reason = (sizes == null) ? "format is invalid." :
- ("size not in valid set: " + Arrays.toString(sizes));
- Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
- "0x%x is not valid, %s", s.getWidth(), s.getHeight(),
- surfaceType, reason));
- return BAD_VALUE;
- }
- } else {
- sizedSurfaces.add(new Pair<>(output, s));
- }
- // Lock down the size before configuration
- if (!validateSurfacesOnly) {
- setSurfaceDimens(output, s.getWidth(), s.getHeight());
- }
- } catch (BufferQueueAbandonedException e) {
- Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
- return BAD_VALUE;
- }
-
- }
- }
-
- if (validateSurfacesOnly) {
- return LegacyExceptionUtils.NO_ERROR;
- }
-
- boolean success = false;
- if (mDeviceState.setConfiguring()) {
- mRequestThreadManager.configure(sizedSurfaces);
- success = mDeviceState.setIdle();
- }
-
- if (success) {
- mConfiguredSurfaces = outputs;
- } else {
- return LegacyExceptionUtils.INVALID_OPERATION;
- }
- return LegacyExceptionUtils.NO_ERROR;
- }
-
- /**
- * Submit a burst of capture requests.
- *
- * @param requestList a list of capture requests to execute.
- * @param repeating {@code true} if this burst is repeating.
- * @return the submission info, including the new request id, and the last frame number, which
- * contains either the frame number of the last frame that will be returned for this request,
- * or the frame number of the last frame that will be returned for the current repeating
- * request if this burst is set to be repeating.
- */
- public SubmitInfo submitRequestList(CaptureRequest[] requestList, boolean repeating) {
- if (requestList == null || requestList.length == 0) {
- Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
- throw new ServiceSpecificException(BAD_VALUE,
- "submitRequestList - Empty/null requests are not allowed");
- }
-
- List<Long> surfaceIds;
-
- try {
- surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
- getSurfaceIds(mConfiguredSurfaces);
- } catch (BufferQueueAbandonedException e) {
- throw new ServiceSpecificException(BAD_VALUE,
- "submitRequestList - configured surface is abandoned.");
- }
-
- // Make sure that there all requests have at least 1 surface; all surfaces are non-null
- for (CaptureRequest request : requestList) {
- if (request.getTargets().isEmpty()) {
- Log.e(TAG, "submitRequestList - "
- + "Each request must have at least one Surface target");
- throw new ServiceSpecificException(BAD_VALUE,
- "submitRequestList - "
- + "Each request must have at least one Surface target");
- }
-
- for (Surface surface : request.getTargets()) {
- if (surface == null) {
- Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
- throw new ServiceSpecificException(BAD_VALUE,
- "submitRequestList - Null Surface targets are not allowed");
- } else if (mConfiguredSurfaces == null) {
- Log.e(TAG, "submitRequestList - must configure " +
- " device with valid surfaces before submitting requests");
- throw new ServiceSpecificException(INVALID_OPERATION,
- "submitRequestList - must configure " +
- " device with valid surfaces before submitting requests");
- } else if (!containsSurfaceId(surface, surfaceIds)) {
- Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
- throw new ServiceSpecificException(BAD_VALUE,
- "submitRequestList - cannot use a surface that wasn't configured");
- }
- }
- }
-
- // TODO: further validation of request here
- mIdle.close();
- return mRequestThreadManager.submitCaptureRequests(requestList, repeating);
- }
-
- /**
- * Submit a single capture request.
- *
- * @param request the capture request to execute.
- * @param repeating {@code true} if this request is repeating.
- * @return the submission info, including the new request id, and the last frame number, which
- * contains either the frame number of the last frame that will be returned for this request,
- * or the frame number of the last frame that will be returned for the current repeating
- * request if this burst is set to be repeating.
- */
- public SubmitInfo submitRequest(CaptureRequest request, boolean repeating) {
- CaptureRequest[] requestList = { request };
- return submitRequestList(requestList, repeating);
- }
-
- /**
- * Cancel the repeating request with the given request id.
- *
- * @param requestId the request id of the request to cancel.
- * @return the last frame number to be returned from the HAL for the given repeating request, or
- * {@code INVALID_FRAME} if none exists.
- */
- public long cancelRequest(int requestId) {
- return mRequestThreadManager.cancelRepeating(requestId);
- }
-
- /**
- * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
- */
- public void waitUntilIdle() {
- mIdle.block();
- }
-
- /**
- * Flush any pending requests.
- *
- * @return the last frame number.
- */
- public long flush() {
- long lastFrame = mRequestThreadManager.flush();
- waitUntilIdle();
- return lastFrame;
- }
-
- public void setAudioRestriction(int mode) {
- mRequestThreadManager.setAudioRestriction(mode);
- }
-
- public int getAudioRestriction() {
- return mRequestThreadManager.getAudioRestriction();
- }
-
- /**
- * Return {@code true} if the device has been closed.
- */
- public boolean isClosed() {
- return mClosed;
- }
-
- @Override
- public void close() {
- mRequestThreadManager.quit();
- mCallbackHandlerThread.quitSafely();
- mResultThread.quitSafely();
-
- try {
- mCallbackHandlerThread.join();
- } catch (InterruptedException e) {
- Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
- mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
- }
-
- try {
- mResultThread.join();
- } catch (InterruptedException e) {
- Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
- mResultThread.getName(), mResultThread.getId()));
- }
-
- mClosed = true;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- close();
- } catch (ServiceSpecificException e) {
- Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
- } finally {
- super.finalize();
- }
- }
-
- static long findEuclidDistSquare(Size a, Size b) {
- long d0 = a.getWidth() - b.getWidth();
- long d1 = a.getHeight() - b.getHeight();
- return d0 * d0 + d1 * d1;
- }
-
- // Keep up to date with rounding behavior in
- // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
- static Size findClosestSize(Size size, Size[] supportedSizes) {
- if (size == null || supportedSizes == null) {
- return null;
- }
- Size bestSize = null;
- for (Size s : supportedSizes) {
- if (s.equals(size)) {
- return size;
- } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
- LegacyCameraDevice.findEuclidDistSquare(size, s) <
- LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
- bestSize = s;
- }
- }
- return bestSize;
- }
-
- /**
- * Query the surface for its currently configured default buffer size.
- * @param surface a non-{@code null} {@code Surface}
- * @return the width and height of the surface
- *
- * @throws NullPointerException if the {@code surface} was {@code null}
- * @throws BufferQueueAbandonedException if the {@code surface} was invalid
- */
- public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
- checkNotNull(surface);
-
- int[] dimens = new int[2];
- LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
-
- return new Size(dimens[0], dimens[1]);
- }
-
- public static boolean isFlexibleConsumer(Surface output) {
- int usageFlags = detectSurfaceUsageFlags(output);
-
- // Keep up to date with allowed consumer types in
- // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
- int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
- int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
- GRALLOC_USAGE_HW_COMPOSER;
- boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
- (usageFlags & allowedFlags) != 0);
- return flexibleConsumer;
- }
-
- public static boolean isPreviewConsumer(Surface output) {
- int usageFlags = detectSurfaceUsageFlags(output);
- int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT |
- GRALLOC_USAGE_SW_READ_OFTEN;
- int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
- GRALLOC_USAGE_HW_RENDER;
- boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 &&
- (usageFlags & allowedFlags) != 0);
- int surfaceFormat = ImageFormat.UNKNOWN;
- try {
- surfaceFormat = detectSurfaceType(output);
- } catch(BufferQueueAbandonedException e) {
- throw new IllegalArgumentException("Surface was abandoned", e);
- }
-
- return previewConsumer;
- }
-
- public static boolean isVideoEncoderConsumer(Surface output) {
- int usageFlags = detectSurfaceUsageFlags(output);
- int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
- GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN;
- int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER;
- boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 &&
- (usageFlags & allowedFlags) != 0);
-
- int surfaceFormat = ImageFormat.UNKNOWN;
- try {
- surfaceFormat = detectSurfaceType(output);
- } catch(BufferQueueAbandonedException e) {
- throw new IllegalArgumentException("Surface was abandoned", e);
- }
-
- return videoEncoderConsumer;
- }
-
- /**
- * Query the surface for its currently configured usage flags
- */
- static int detectSurfaceUsageFlags(Surface surface) {
- checkNotNull(surface);
- return nativeDetectSurfaceUsageFlags(surface);
- }
-
- /**
- * Query the surface for its currently configured format
- */
- public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
- checkNotNull(surface);
- int surfaceType = nativeDetectSurfaceType(surface);
-
- // TODO: remove this override since the default format should be
- // ImageFormat.PRIVATE. b/9487482
- if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
- surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
- surfaceType = ImageFormat.PRIVATE;
- }
-
- return LegacyExceptionUtils.throwOnError(surfaceType);
- }
-
- /**
- * Query the surface for its currently configured dataspace
- */
- public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException {
- checkNotNull(surface);
- return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface));
- }
-
- static void connectSurface(Surface surface) throws BufferQueueAbandonedException {
- checkNotNull(surface);
-
- LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface));
- }
-
- static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException {
- if (surface == null) return;
-
- LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface));
- }
-
- static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
- int height, int pixelFormat)
- throws BufferQueueAbandonedException {
- checkNotNull(surface);
- checkNotNull(pixelBuffer);
- checkArgumentPositive(width, "width must be positive.");
- checkArgumentPositive(height, "height must be positive.");
-
- LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
- pixelFormat));
- }
-
- static void setSurfaceFormat(Surface surface, int pixelFormat)
- throws BufferQueueAbandonedException {
- checkNotNull(surface);
-
- LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
- }
-
- static void setSurfaceDimens(Surface surface, int width, int height)
- throws BufferQueueAbandonedException {
- checkNotNull(surface);
- checkArgumentPositive(width, "width must be positive.");
- checkArgumentPositive(height, "height must be positive.");
-
- LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
- }
-
- public static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
- checkNotNull(surface);
- try {
- return nativeGetSurfaceId(surface);
- } catch (IllegalArgumentException e) {
- throw new BufferQueueAbandonedException();
- }
- }
-
- static List<Long> getSurfaceIds(SparseArray<Surface> surfaces)
- throws BufferQueueAbandonedException {
- if (surfaces == null) {
- throw new NullPointerException("Null argument surfaces");
- }
- List<Long> surfaceIds = new ArrayList<>();
- int count = surfaces.size();
- for (int i = 0; i < count; i++) {
- long id = getSurfaceId(surfaces.valueAt(i));
- if (id == 0) {
- throw new IllegalStateException(
- "Configured surface had null native GraphicBufferProducer pointer!");
- }
- surfaceIds.add(id);
- }
- return surfaceIds;
- }
-
- static List<Long> getSurfaceIds(Collection<Surface> surfaces)
- throws BufferQueueAbandonedException {
- if (surfaces == null) {
- throw new NullPointerException("Null argument surfaces");
- }
- List<Long> surfaceIds = new ArrayList<>();
- for (Surface s : surfaces) {
- long id = getSurfaceId(s);
- if (id == 0) {
- throw new IllegalStateException(
- "Configured surface had null native GraphicBufferProducer pointer!");
- }
- surfaceIds.add(id);
- }
- return surfaceIds;
- }
-
- static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
- long id = 0;
- try {
- id = getSurfaceId(s);
- } catch (BufferQueueAbandonedException e) {
- // If surface is abandoned, return false.
- return false;
- }
- return ids.contains(id);
- }
-
- static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
- throws BufferQueueAbandonedException {
- checkNotNull(surface);
- LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
- sensorOrientation));
- }
-
- static Size getTextureSize(SurfaceTexture surfaceTexture)
- throws BufferQueueAbandonedException {
- checkNotNull(surfaceTexture);
-
- int[] dimens = new int[2];
- LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
- /*out*/dimens));
-
- return new Size(dimens[0], dimens[1]);
- }
-
- static void setNextTimestamp(Surface surface, long timestamp)
- throws BufferQueueAbandonedException {
- checkNotNull(surface);
- LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
- }
-
- static void setScalingMode(Surface surface, int mode)
- throws BufferQueueAbandonedException {
- checkNotNull(surface);
- LegacyExceptionUtils.throwOnError(nativeSetScalingMode(surface, mode));
- }
-
-
- private static native int nativeDetectSurfaceType(Surface surface);
-
- private static native int nativeDetectSurfaceDataspace(Surface surface);
-
- private static native int nativeDetectSurfaceDimens(Surface surface,
- /*out*/int[/*2*/] dimens);
-
- private static native int nativeConnectSurface(Surface surface);
-
- private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
- int height, int pixelFormat);
-
- private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
-
- private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
-
- private static native long nativeGetSurfaceId(Surface surface);
-
- private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
- int sensorOrientation);
-
- private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
- /*out*/int[/*2*/] dimens);
-
- private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
-
- private static native int nativeDetectSurfaceUsageFlags(Surface surface);
-
- private static native int nativeSetScalingMode(Surface surface, int scalingMode);
-
- private static native int nativeDisconnectSurface(Surface surface);
-
- static native int nativeGetJpegFooterSize();
-}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java b/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
deleted file mode 100644
index 55130c8f2839..000000000000
--- a/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.hardware.ICameraService;
-import android.os.ServiceSpecificException;
-import android.util.AndroidException;
-
-import static android.system.OsConstants.*;
-
-/**
- * Utility class containing exception handling used solely by the compatibility mode shim.
- */
-public class LegacyExceptionUtils {
- private static final String TAG = "LegacyExceptionUtils";
-
- public static final int NO_ERROR = 0;
- public static final int PERMISSION_DENIED = -EPERM;
- public static final int ALREADY_EXISTS = -EEXIST;
- public static final int BAD_VALUE = -EINVAL;
- public static final int DEAD_OBJECT = -ENOSYS;
- public static final int INVALID_OPERATION = -EPIPE;
- public static final int TIMED_OUT = -ETIMEDOUT;
-
- /**
- * Checked exception thrown when a BufferQueue has been abandoned by its consumer.
- */
- public static class BufferQueueAbandonedException extends AndroidException {
- public BufferQueueAbandonedException () {}
-
- public BufferQueueAbandonedException(String name) {
- super(name);
- }
-
- public BufferQueueAbandonedException(String name, Throwable cause) {
- super(name, cause);
- }
-
- public BufferQueueAbandonedException(Exception cause) {
- super(cause);
- }
- }
-
- /**
- * Throw error codes used by legacy device methods as exceptions.
- *
- * <p>Non-negative return values are passed through, negative return values are thrown as
- * exceptions.</p>
- *
- * @param errorFlag error to throw as an exception.
- * @throws {@link BufferQueueAbandonedException} for BAD_VALUE.
- * @throws {@link UnsupportedOperationException} for an unknown negative error code.
- * @return {@code errorFlag} if the value was non-negative, throws otherwise.
- */
- public static int throwOnError(int errorFlag) throws BufferQueueAbandonedException {
- if (errorFlag == NO_ERROR) {
- return NO_ERROR;
- } else if (errorFlag == BAD_VALUE) {
- throw new BufferQueueAbandonedException();
- }
-
- if (errorFlag < 0) {
- throw new UnsupportedOperationException("Unknown error " + errorFlag);
- }
- return errorFlag;
- }
-
- /**
- * Throw error codes returned by the camera service as exceptions.
- *
- * @param errorFlag error to throw as an exception.
- */
- public static void throwOnServiceError(int errorFlag) {
- int errorCode = ICameraService.ERROR_INVALID_OPERATION;
- String errorMsg;
-
- if (errorFlag >= NO_ERROR) {
- return;
- } else if (errorFlag == PERMISSION_DENIED) {
- errorCode = ICameraService.ERROR_PERMISSION_DENIED;
- errorMsg = "Lacking privileges to access camera service";
- } else if (errorFlag == ALREADY_EXISTS) {
- // This should be handled at the call site. Typically this isn't bad,
- // just means we tried to do an operation that already completed.
- return;
- } else if (errorFlag == BAD_VALUE) {
- errorCode = ICameraService.ERROR_ILLEGAL_ARGUMENT;
- errorMsg = "Bad argument passed to camera service";
- } else if (errorFlag == DEAD_OBJECT) {
- errorCode = ICameraService.ERROR_DISCONNECTED;
- errorMsg = "Camera service not available";
- } else if (errorFlag == TIMED_OUT) {
- errorCode = ICameraService.ERROR_INVALID_OPERATION;
- errorMsg = "Operation timed out in camera service";
- } else if (errorFlag == -EACCES) {
- errorCode = ICameraService.ERROR_DISABLED;
- errorMsg = "Camera disabled by policy";
- } else if (errorFlag == -EBUSY) {
- errorCode = ICameraService.ERROR_CAMERA_IN_USE;
- errorMsg = "Camera already in use";
- } else if (errorFlag == -EUSERS) {
- errorCode = ICameraService.ERROR_MAX_CAMERAS_IN_USE;
- errorMsg = "Maximum number of cameras in use";
- } else if (errorFlag == -ENODEV) {
- errorCode = ICameraService.ERROR_DISCONNECTED;
- errorMsg = "Camera device not available";
- } else if (errorFlag == -EOPNOTSUPP) {
- errorCode = ICameraService.ERROR_DEPRECATED_HAL;
- errorMsg = "Deprecated camera HAL does not support this";
- } else if (errorFlag == INVALID_OPERATION) {
- errorCode = ICameraService.ERROR_INVALID_OPERATION;
- errorMsg = "Illegal state encountered in camera service.";
- } else {
- errorCode = ICameraService.ERROR_INVALID_OPERATION;
- errorMsg = "Unknown camera device error " + errorFlag;
- }
-
- throw new ServiceSpecificException(errorCode, errorMsg);
- }
-
- private LegacyExceptionUtils() {
- throw new AssertionError();
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
deleted file mode 100644
index b3b4549426f0..000000000000
--- a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.graphics.Rect;
-import android.hardware.Camera;
-import android.hardware.Camera.FaceDetectionListener;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.params.Face;
-import android.hardware.camera2.utils.ListUtils;
-import android.hardware.camera2.utils.ParamsUtils;
-import android.util.Log;
-import android.util.Size;
-
-import com.android.internal.util.ArrayUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static android.hardware.camera2.CaptureRequest.*;
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Map legacy face detect callbacks into face detection results.
- */
-@SuppressWarnings("deprecation")
-public class LegacyFaceDetectMapper {
- private static String TAG = "LegacyFaceDetectMapper";
- private static final boolean DEBUG = false;
-
- private final Camera mCamera;
- /** Is the camera capable of face detection? */
- private final boolean mFaceDetectSupported;
- /** Is the camera is running face detection? */
- private boolean mFaceDetectEnabled = false;
- /** Did the last request say to use SCENE_MODE = FACE_PRIORITY? */
- private boolean mFaceDetectScenePriority = false;
- /** Did the last request enable the face detect mode to ON? */
- private boolean mFaceDetectReporting = false;
-
- /** Synchronize access to all fields */
- private final Object mLock = new Object();
- private Camera.Face[] mFaces;
- private Camera.Face[] mFacesPrev;
- /**
- * Instantiate a new face detect mapper.
- *
- * @param camera a non-{@code null} camera1 device
- * @param characteristics a non-{@code null} camera characteristics for that camera1
- *
- * @throws NullPointerException if any of the args were {@code null}
- */
- public LegacyFaceDetectMapper(Camera camera, CameraCharacteristics characteristics) {
- mCamera = checkNotNull(camera, "camera must not be null");
- checkNotNull(characteristics, "characteristics must not be null");
-
- mFaceDetectSupported = ArrayUtils.contains(
- characteristics.get(
- CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES),
- STATISTICS_FACE_DETECT_MODE_SIMPLE);
-
- if (!mFaceDetectSupported) {
- return;
- }
-
- mCamera.setFaceDetectionListener(new FaceDetectionListener() {
-
- @Override
- public void onFaceDetection(Camera.Face[] faces, Camera camera) {
- int lengthFaces = faces == null ? 0 : faces.length;
- synchronized (mLock) {
- if (mFaceDetectEnabled) {
- mFaces = faces;
- } else if (lengthFaces > 0) {
- // stopFaceDetectMode could race against the requests, print a debug log
- Log.d(TAG,
- "onFaceDetection - Ignored some incoming faces since" +
- "face detection was disabled");
- }
- }
-
- if (DEBUG) {
- Log.v(TAG, "onFaceDetection - read " + lengthFaces + " faces");
- }
- }
- });
- }
-
- /**
- * Process the face detect mode from the capture request into an api1 face detect toggle.
- *
- * <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped}
- * with the request.</p>
- *
- * <p>Callbacks are processed in the background, and the next call to {@link #mapResultTriggers}
- * will have the latest faces detected as reflected by the camera1 callbacks.</p>
- *
- * <p>None of the arguments will be mutated.</p>
- *
- * @param captureRequest a non-{@code null} request
- * @param parameters a non-{@code null} parameters corresponding to this request (read-only)
- */
- public void processFaceDetectMode(CaptureRequest captureRequest,
- Camera.Parameters parameters) {
- checkNotNull(captureRequest, "captureRequest must not be null");
-
- /*
- * statistics.faceDetectMode
- */
- int fdMode = ParamsUtils.getOrDefault(captureRequest, STATISTICS_FACE_DETECT_MODE,
- STATISTICS_FACE_DETECT_MODE_OFF);
-
- if (fdMode != STATISTICS_FACE_DETECT_MODE_OFF && !mFaceDetectSupported) {
- Log.w(TAG,
- "processFaceDetectMode - Ignoring statistics.faceDetectMode; " +
- "face detection is not available");
- return;
- }
-
- /*
- * control.sceneMode
- */
- int sceneMode = ParamsUtils.getOrDefault(captureRequest, CONTROL_SCENE_MODE,
- CONTROL_SCENE_MODE_DISABLED);
- if (sceneMode == CONTROL_SCENE_MODE_FACE_PRIORITY && !mFaceDetectSupported) {
- Log.w(TAG, "processFaceDetectMode - ignoring control.sceneMode == FACE_PRIORITY; " +
- "face detection is not available");
- return;
- }
-
- // Print some warnings out in case the values were wrong
- switch (fdMode) {
- case STATISTICS_FACE_DETECT_MODE_OFF:
- case STATISTICS_FACE_DETECT_MODE_SIMPLE:
- break;
- case STATISTICS_FACE_DETECT_MODE_FULL:
- Log.w(TAG,
- "processFaceDetectMode - statistics.faceDetectMode == FULL unsupported, " +
- "downgrading to SIMPLE");
- break;
- default:
- Log.w(TAG, "processFaceDetectMode - ignoring unknown statistics.faceDetectMode = "
- + fdMode);
- return;
- }
-
- boolean enableFaceDetect = (fdMode != STATISTICS_FACE_DETECT_MODE_OFF)
- || (sceneMode == CONTROL_SCENE_MODE_FACE_PRIORITY);
- synchronized (mLock) {
- // Enable/disable face detection if it's changed since last time
- if (enableFaceDetect != mFaceDetectEnabled) {
- if (enableFaceDetect) {
- mCamera.startFaceDetection();
-
- if (DEBUG) {
- Log.v(TAG, "processFaceDetectMode - start face detection");
- }
- } else {
- mCamera.stopFaceDetection();
-
- if (DEBUG) {
- Log.v(TAG, "processFaceDetectMode - stop face detection");
- }
-
- mFaces = null;
- }
-
- mFaceDetectEnabled = enableFaceDetect;
- mFaceDetectScenePriority = sceneMode == CONTROL_SCENE_MODE_FACE_PRIORITY;
- mFaceDetectReporting = fdMode != STATISTICS_FACE_DETECT_MODE_OFF;
- }
- }
- }
-
- /**
- * Update the {@code result} camera metadata map with the new value for the
- * {@code statistics.faces} and {@code statistics.faceDetectMode}.
- *
- * <p>Face detect callbacks are processed in the background, and each call to
- * {@link #mapResultFaces} will have the latest faces as reflected by the camera1 callbacks.</p>
- *
- * <p>If the scene mode was set to {@code FACE_PRIORITY} but face detection is disabled,
- * the camera will still run face detection in the background, but no faces will be reported
- * in the capture result.</p>
- *
- * @param result a non-{@code null} result
- * @param legacyRequest a non-{@code null} request (read-only)
- */
- public void mapResultFaces(CameraMetadataNative result, LegacyRequest legacyRequest) {
- checkNotNull(result, "result must not be null");
- checkNotNull(legacyRequest, "legacyRequest must not be null");
-
- Camera.Face[] faces, previousFaces;
- int fdMode;
- boolean fdScenePriority;
- synchronized (mLock) {
- fdMode = mFaceDetectReporting ?
- STATISTICS_FACE_DETECT_MODE_SIMPLE : STATISTICS_FACE_DETECT_MODE_OFF;
-
- if (mFaceDetectReporting) {
- faces = mFaces;
- } else {
- faces = null;
- }
-
- fdScenePriority = mFaceDetectScenePriority;
-
- previousFaces = mFacesPrev;
- mFacesPrev = faces;
- }
-
- CameraCharacteristics characteristics = legacyRequest.characteristics;
- CaptureRequest request = legacyRequest.captureRequest;
- Size previewSize = legacyRequest.previewSize;
- Camera.Parameters params = legacyRequest.parameters;
-
- Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
- request.get(CaptureRequest.SCALER_CROP_REGION),
- request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
- previewSize, params);
-
- List<Face> convertedFaces = new ArrayList<>();
- if (faces != null) {
- for (Camera.Face face : faces) {
- if (face != null) {
- convertedFaces.add(
- ParameterUtils.convertFaceFromLegacy(face, activeArray, zoomData));
- } else {
- Log.w(TAG, "mapResultFaces - read NULL face from camera1 device");
- }
- }
- }
-
- if (DEBUG && previousFaces != faces) { // Log only in verbose and IF the faces changed
- Log.v(TAG, "mapResultFaces - changed to " + ListUtils.listToString(convertedFaces));
- }
-
- result.set(CaptureResult.STATISTICS_FACES, convertedFaces.toArray(new Face[0]));
- result.set(CaptureResult.STATISTICS_FACE_DETECT_MODE, fdMode);
-
- // Override scene mode with FACE_PRIORITY if the request was using FACE_PRIORITY
- if (fdScenePriority) {
- result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_FACE_PRIORITY);
- }
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java
deleted file mode 100644
index d33c09eac85d..000000000000
--- a/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.hardware.Camera;
-import android.hardware.Camera.Parameters;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.utils.ParamsUtils;
-import android.util.Log;
-
-import java.util.Objects;
-
-import static android.hardware.camera2.CaptureRequest.*;
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Map capture request data into legacy focus state transitions.
- *
- * <p>This object will asynchronously process auto-focus changes, so no interaction
- * with it is necessary beyond reading the current state and updating with the latest trigger.</p>
- */
-@SuppressWarnings("deprecation")
-public class LegacyFocusStateMapper {
- private static String TAG = "LegacyFocusStateMapper";
- private static final boolean DEBUG = false;
-
- private final Camera mCamera;
-
- private int mAfStatePrevious = CONTROL_AF_STATE_INACTIVE;
- private String mAfModePrevious = null;
-
- /** Guard mAfRun and mAfState */
- private final Object mLock = new Object();
- /** Guard access with mLock */
- private int mAfRun = 0;
- /** Guard access with mLock */
- private int mAfState = CONTROL_AF_STATE_INACTIVE;
-
- /**
- * Instantiate a new focus state mapper.
- *
- * @param camera a non-{@code null} camera1 device
- *
- * @throws NullPointerException if any of the args were {@code null}
- */
- public LegacyFocusStateMapper(Camera camera) {
- mCamera = checkNotNull(camera, "camera must not be null");
- }
-
- /**
- * Process the AF triggers from the request as a camera1 autofocus routine.
- *
- * <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped}
- * with the request.</p>
- *
- * <p>Callbacks are processed in the background, and the next call to {@link #mapResultTriggers}
- * will have the latest AF state as reflected by the camera1 callbacks.</p>
- *
- * <p>None of the arguments will be mutated.</p>
- *
- * @param captureRequest a non-{@code null} request
- * @param parameters a non-{@code null} parameters corresponding to this request (read-only)
- */
- public void processRequestTriggers(CaptureRequest captureRequest,
- Camera.Parameters parameters) {
- checkNotNull(captureRequest, "captureRequest must not be null");
-
- /*
- * control.afTrigger
- */
- int afTrigger = ParamsUtils.getOrDefault(captureRequest, CONTROL_AF_TRIGGER,
- CONTROL_AF_TRIGGER_IDLE);
-
- final String afMode = parameters.getFocusMode();
-
- if (!Objects.equals(mAfModePrevious, afMode)) {
- if (DEBUG) {
- Log.v(TAG, "processRequestTriggers - AF mode switched from " + mAfModePrevious +
- " to " + afMode);
- }
-
- // Switching modes always goes back to INACTIVE; ignore callbacks from previous modes
-
- synchronized (mLock) {
- ++mAfRun;
- mAfState = CONTROL_AF_STATE_INACTIVE;
- }
- mCamera.cancelAutoFocus();
- }
-
- mAfModePrevious = afMode;
-
- // Passive AF Scanning
- {
- final int currentAfRun;
-
- synchronized (mLock) {
- currentAfRun = mAfRun;
- }
-
- Camera.AutoFocusMoveCallback afMoveCallback = new Camera.AutoFocusMoveCallback() {
- @Override
- public void onAutoFocusMoving(boolean start, Camera camera) {
- synchronized (mLock) {
- int latestAfRun = mAfRun;
-
- if (DEBUG) {
- Log.v(TAG,
- "onAutoFocusMoving - start " + start + " latest AF run " +
- latestAfRun + ", last AF run " + currentAfRun
- );
- }
-
- if (currentAfRun != latestAfRun) {
- Log.d(TAG,
- "onAutoFocusMoving - ignoring move callbacks from old af run"
- + currentAfRun
- );
- return;
- }
-
- int newAfState = start ?
- CONTROL_AF_STATE_PASSIVE_SCAN :
- CONTROL_AF_STATE_PASSIVE_FOCUSED;
- // We never send CONTROL_AF_STATE_PASSIVE_UNFOCUSED
-
- switch (afMode) {
- case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
- case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
- break;
- // This callback should never be sent in any other AF mode
- default:
- Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode "
- + afMode);
-
- }
-
- mAfState = newAfState;
- }
- }
- };
-
- // Only set move callback if we can call autofocus.
- switch (afMode) {
- case Parameters.FOCUS_MODE_AUTO:
- case Parameters.FOCUS_MODE_MACRO:
- case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
- case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
- mCamera.setAutoFocusMoveCallback(afMoveCallback);
- }
- }
-
-
- // AF Locking
- switch (afTrigger) {
- case CONTROL_AF_TRIGGER_START:
-
- int afStateAfterStart;
- switch (afMode) {
- case Parameters.FOCUS_MODE_AUTO:
- case Parameters.FOCUS_MODE_MACRO:
- afStateAfterStart = CONTROL_AF_STATE_ACTIVE_SCAN;
- break;
- case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
- case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
- afStateAfterStart = CONTROL_AF_STATE_PASSIVE_SCAN;
- break;
- default:
- // EDOF, INFINITY
- afStateAfterStart = CONTROL_AF_STATE_INACTIVE;
- }
-
- final int currentAfRun;
- synchronized (mLock) {
- currentAfRun = ++mAfRun;
- mAfState = afStateAfterStart;
- }
-
- if (DEBUG) {
- Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_START, " +
- "new AF run is " + currentAfRun);
- }
-
- // Avoid calling autofocus unless we are in a state that supports calling this.
- if (afStateAfterStart == CONTROL_AF_STATE_INACTIVE) {
- break;
- }
-
- mCamera.autoFocus(new Camera.AutoFocusCallback() {
- @Override
- public void onAutoFocus(boolean success, Camera camera) {
- synchronized (mLock) {
- int latestAfRun = mAfRun;
-
- if (DEBUG) {
- Log.v(TAG, "onAutoFocus - success " + success + " latest AF run " +
- latestAfRun + ", last AF run " + currentAfRun);
- }
-
- // Ignore old auto-focus results, since another trigger was requested
- if (latestAfRun != currentAfRun) {
- Log.d(TAG, String.format("onAutoFocus - ignoring AF callback " +
- "(old run %d, new run %d)", currentAfRun, latestAfRun));
-
- return;
- }
-
- int newAfState = success ?
- CONTROL_AF_STATE_FOCUSED_LOCKED :
- CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
-
- switch (afMode) {
- case Parameters.FOCUS_MODE_AUTO:
- case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
- case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
- case Parameters.FOCUS_MODE_MACRO:
- break;
- // This callback should never be sent in any other AF mode
- default:
- Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode "
- + afMode);
-
- }
-
- mAfState = newAfState;
- }
- }
- });
-
- break;
- case CONTROL_AF_TRIGGER_CANCEL:
- synchronized (mLock) {
- int updatedAfRun;
-
- synchronized (mLock) {
- updatedAfRun = ++mAfRun;
- mAfState = CONTROL_AF_STATE_INACTIVE;
- }
-
- mCamera.cancelAutoFocus();
-
- if (DEBUG) {
- Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_CANCEL, " +
- "new AF run is " + updatedAfRun);
- }
- }
-
- break;
- case CONTROL_AF_TRIGGER_IDLE:
- // No action necessary. The callbacks will handle transitions.
- break;
- default:
- Log.w(TAG, "processRequestTriggers - ignoring unknown control.afTrigger = "
- + afTrigger);
- }
- }
-
- /**
- * Update the {@code result} camera metadata map with the new value for the
- * {@code control.afState}.
- *
- * <p>AF callbacks are processed in the background, and each call to {@link #mapResultTriggers}
- * will have the latest AF state as reflected by the camera1 callbacks.</p>
- *
- * @param result a non-{@code null} result
- */
- public void mapResultTriggers(CameraMetadataNative result) {
- checkNotNull(result, "result must not be null");
-
- int newAfState;
- synchronized (mLock) {
- newAfState = mAfState;
- }
-
- if (DEBUG && newAfState != mAfStatePrevious) {
- Log.v(TAG, String.format("mapResultTriggers - afState changed from %s to %s",
- afStateToString(mAfStatePrevious), afStateToString(newAfState)));
- }
-
- result.set(CaptureResult.CONTROL_AF_STATE, newAfState);
-
- mAfStatePrevious = newAfState;
- }
-
- private static String afStateToString(int afState) {
- switch (afState) {
- case CONTROL_AF_STATE_ACTIVE_SCAN:
- return "ACTIVE_SCAN";
- case CONTROL_AF_STATE_FOCUSED_LOCKED:
- return "FOCUSED_LOCKED";
- case CONTROL_AF_STATE_INACTIVE:
- return "INACTIVE";
- case CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
- return "NOT_FOCUSED_LOCKED";
- case CONTROL_AF_STATE_PASSIVE_FOCUSED:
- return "PASSIVE_FOCUSED";
- case CONTROL_AF_STATE_PASSIVE_SCAN:
- return "PASSIVE_SCAN";
- case CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
- return "PASSIVE_UNFOCUSED";
- default :
- return "UNKNOWN(" + afState + ")";
- }
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
deleted file mode 100644
index 362ddfae67bf..000000000000
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ /dev/null
@@ -1,1532 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.graphics.ImageFormat;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.hardware.Camera;
-import android.hardware.Camera.CameraInfo;
-import android.hardware.Camera.Parameters;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.params.MeteringRectangle;
-import android.hardware.camera2.params.StreamConfiguration;
-import android.hardware.camera2.params.StreamConfigurationDuration;
-import android.hardware.camera2.utils.ArrayUtils;
-import android.hardware.camera2.utils.ListUtils;
-import android.hardware.camera2.utils.ParamsUtils;
-import android.util.Log;
-import android.util.Range;
-import android.util.Size;
-import android.util.SizeF;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import static com.android.internal.util.Preconditions.*;
-import static android.hardware.camera2.CameraCharacteristics.*;
-import static android.hardware.camera2.legacy.ParameterUtils.*;
-
-/**
- * Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the
- * camera characteristics.
- */
-@SuppressWarnings("deprecation")
-public class LegacyMetadataMapper {
- private static final String TAG = "LegacyMetadataMapper";
- private static final boolean DEBUG = false;
-
- private static final long NS_PER_MS = 1000000;
-
- // from graphics.h
- public static final int HAL_PIXEL_FORMAT_RGBA_8888 = PixelFormat.RGBA_8888;
- public static final int HAL_PIXEL_FORMAT_BGRA_8888 = 0x5;
- public static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
- public static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
-
- // for metadata
- private static final float LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS = 0.0f;
-
- private static final int REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_RAW = 0; // no raw support
- private static final int REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC = 3; // preview, video, cb
- private static final int REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC_STALL = 1; // 1 jpeg only
- private static final int REQUEST_MAX_NUM_INPUT_STREAMS_COUNT = 0; // no reprocessing
-
- /** Assume 3 HAL1 stages: Exposure, Read-out, Post-Processing */
- private static final int REQUEST_PIPELINE_MAX_DEPTH_HAL1 = 3;
- /** Assume 3 shim stages: Preview input, Split output, Format conversion for output */
- private static final int REQUEST_PIPELINE_MAX_DEPTH_OURS = 3;
- /* TODO: Update above maxDepth values once we do more performance measurements */
-
- // For approximating JPEG stall durations
- private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // 200 milliseconds
- private static final long APPROXIMATE_SENSOR_AREA_PX = (1 << 23); // 8 megapixels
- private static final long APPROXIMATE_JPEG_ENCODE_TIME_MS = 600; // 600 milliseconds
-
- static final int UNKNOWN_MODE = -1;
-
- // Maximum difference between a preview size aspect ratio and a jpeg size aspect ratio
- private static final float PREVIEW_ASPECT_RATIO_TOLERANCE = 0.01f;
-
- /*
- * Development hijinks: Lie about not supporting certain capabilities
- *
- * - Unblock some CTS tests from running whose main intent is not the metadata itself
- *
- * TODO: Remove these constants and strip out any code that previously relied on them
- * being set to true.
- */
- static final boolean LIE_ABOUT_AE_STATE = false;
- static final boolean LIE_ABOUT_AE_MAX_REGIONS = false;
- static final boolean LIE_ABOUT_AF = false;
- static final boolean LIE_ABOUT_AF_MAX_REGIONS = false;
- static final boolean LIE_ABOUT_AWB_STATE = false;
- static final boolean LIE_ABOUT_AWB = false;
-
-
- /**
- * Create characteristics for a legacy device by mapping the {@code parameters}
- * and {@code info}
- *
- * @param parameters A non-{@code null} parameters set
- * @param info Camera info with camera facing direction and angle of orientation
- * @param cameraId Current camera Id
- * @param displaySize Device display size
- *
- * @return static camera characteristics for a camera device
- *
- * @throws NullPointerException if any of the args were {@code null}
- */
- public static CameraCharacteristics createCharacteristics(Camera.Parameters parameters,
- CameraInfo info, int cameraId, Size displaySize) {
- checkNotNull(parameters, "parameters must not be null");
- checkNotNull(info, "info must not be null");
-
- String paramStr = parameters.flatten();
- android.hardware.CameraInfo outerInfo = new android.hardware.CameraInfo();
- outerInfo.info = info;
-
- return createCharacteristics(paramStr, outerInfo, cameraId, displaySize);
- }
-
- /**
- * Create characteristics for a legacy device by mapping the {@code parameters}
- * and {@code info}
- *
- * @param parameters A string parseable by {@link Camera.Parameters#unflatten}
- * @param info Camera info with camera facing direction and angle of orientation
- * @param cameraId Current camera id
- * @param displaySize Device display size
- * @return static camera characteristics for a camera device
- *
- * @throws NullPointerException if any of the args were {@code null}
- */
- public static CameraCharacteristics createCharacteristics(String parameters,
- android.hardware.CameraInfo info, int cameraId, Size displaySize) {
- checkNotNull(parameters, "parameters must not be null");
- checkNotNull(info, "info must not be null");
- checkNotNull(info.info, "info.info must not be null");
-
- CameraMetadataNative m = new CameraMetadataNative();
-
- mapCharacteristicsFromInfo(m, info.info);
-
- Camera.Parameters params = Camera.getEmptyParameters();
- params.unflatten(parameters);
- mapCharacteristicsFromParameters(m, params);
-
- if (DEBUG) {
- Log.v(TAG, "createCharacteristics metadata:");
- Log.v(TAG, "--------------------------------------------------- (start)");
- m.dumpToLog();
- Log.v(TAG, "--------------------------------------------------- (end)");
- }
-
- m.setCameraId(cameraId);
- m.setDisplaySize(displaySize);
-
- return new CameraCharacteristics(m);
- }
-
- private static void mapCharacteristicsFromInfo(CameraMetadataNative m, CameraInfo i) {
- m.set(LENS_FACING, i.facing == CameraInfo.CAMERA_FACING_BACK ?
- LENS_FACING_BACK : LENS_FACING_FRONT);
- m.set(SENSOR_ORIENTATION, i.orientation);
- }
-
- private static void mapCharacteristicsFromParameters(CameraMetadataNative m,
- Camera.Parameters p) {
-
- /*
- * colorCorrection.*
- */
- m.set(COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
- new int[] { COLOR_CORRECTION_ABERRATION_MODE_FAST,
- COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY });
- /*
- * control.ae*
- */
- mapControlAe(m, p);
- /*
- * control.af*
- */
- mapControlAf(m, p);
- /*
- * control.awb*
- */
- mapControlAwb(m, p);
- /*
- * control.*
- * - Anything that doesn't have a set of related fields
- */
- mapControlOther(m, p);
- /*
- * lens.*
- */
- mapLens(m, p);
- /*
- * flash.*
- */
- mapFlash(m, p);
- /*
- * jpeg.*
- */
- mapJpeg(m, p);
-
- /*
- * noiseReduction.*
- */
- m.set(NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
- new int[] { NOISE_REDUCTION_MODE_FAST,
- NOISE_REDUCTION_MODE_HIGH_QUALITY});
-
- /*
- * scaler.*
- */
- mapScaler(m, p);
-
- /*
- * sensor.*
- */
- mapSensor(m, p);
-
- /*
- * statistics.*
- */
- mapStatistics(m, p);
-
- /*
- * sync.*
- */
- mapSync(m, p);
-
- /*
- * info.supportedHardwareLevel
- */
- m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);
-
- /*
- * scaler.availableStream*, scaler.available*Durations, sensor.info.maxFrameDuration
- */
- mapScalerStreamConfigs(m, p);
-
- // Order matters below: Put this last so that we can read the metadata set previously
-
- /*
- * request.*
- */
- mapRequest(m, p);
-
- }
-
- private static void mapScalerStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
-
- ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>();
- /*
- * Implementation-defined (preview, recording, etc) -> use camera1 preview sizes
- * YUV_420_888 cpu callbacks -> use camera1 preview sizes
- * Other preview callbacks (CPU) -> use camera1 preview sizes
- * JPEG still capture -> use camera1 still capture sizes
- *
- * Use platform-internal format constants here, since StreamConfigurationMap does the
- * remapping to public format constants.
- */
- List<Camera.Size> previewSizes = p.getSupportedPreviewSizes();
- List<Camera.Size> jpegSizes = p.getSupportedPictureSizes();
- /*
- * Work-around for b/17589233:
- * - Some HALs's largest preview size aspect ratio does not match the largest JPEG size AR
- * - This causes a large amount of problems with focus/metering because it's relative to
- * preview, making the difference between the JPEG and preview viewport inaccessible
- * - This boils down to metering or focusing areas being "arbitrarily" cropped
- * in the capture result.
- * - Work-around the HAL limitations by removing all of the largest preview sizes
- * until we get one with the same aspect ratio as the jpeg size.
- */
- {
- SizeAreaComparator areaComparator = new SizeAreaComparator();
-
- // Sort preview to min->max
- Collections.sort(previewSizes, areaComparator);
-
- Camera.Size maxJpegSize = SizeAreaComparator.findLargestByArea(jpegSizes);
- float jpegAspectRatio = maxJpegSize.width * 1.0f / maxJpegSize.height;
-
- if (DEBUG) {
- Log.v(TAG, String.format("mapScalerStreamConfigs - largest JPEG area %dx%d, AR=%f",
- maxJpegSize.width, maxJpegSize.height, jpegAspectRatio));
- }
-
- // Now remove preview sizes from the end (largest->smallest) until aspect ratio matches
- while (!previewSizes.isEmpty()) {
- int index = previewSizes.size() - 1; // max is always at the end
- Camera.Size size = previewSizes.get(index);
-
- float previewAspectRatio = size.width * 1.0f / size.height;
-
- if (Math.abs(jpegAspectRatio - previewAspectRatio) >=
- PREVIEW_ASPECT_RATIO_TOLERANCE) {
- previewSizes.remove(index); // Assume removing from end is O(1)
-
- if (DEBUG) {
- Log.v(TAG, String.format(
- "mapScalerStreamConfigs - removed preview size %dx%d, AR=%f "
- + "was not the same",
- size.width, size.height, previewAspectRatio));
- }
- } else {
- break;
- }
- }
-
- if (previewSizes.isEmpty()) {
- // Fall-back to the original faulty behavior, but at least work
- Log.w(TAG, "mapScalerStreamConfigs - failed to find any preview size matching " +
- "JPEG aspect ratio " + jpegAspectRatio);
- previewSizes = p.getSupportedPreviewSizes();
- }
-
- // Sort again, this time in descending order max->min
- Collections.sort(previewSizes, Collections.reverseOrder(areaComparator));
- }
-
- appendStreamConfig(availableStreamConfigs,
- HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, previewSizes);
- appendStreamConfig(availableStreamConfigs,
- ImageFormat.YUV_420_888, previewSizes);
- for (int format : p.getSupportedPreviewFormats()) {
- if (ImageFormat.isPublicFormat(format) && format != ImageFormat.NV21) {
- appendStreamConfig(availableStreamConfigs, format, previewSizes);
- } else if (DEBUG) {
- /*
- * Do not add any formats unknown to us
- * (since it would fail runtime checks in StreamConfigurationMap)
- */
- Log.v(TAG,
- String.format("mapStreamConfigs - Skipping format %x", format));
- }
- }
-
- appendStreamConfig(availableStreamConfigs,
- HAL_PIXEL_FORMAT_BLOB, p.getSupportedPictureSizes());
- /*
- * scaler.availableStreamConfigurations
- */
- m.set(SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
- availableStreamConfigs.toArray(new StreamConfiguration[0]));
-
- /*
- * scaler.availableMinFrameDurations
- */
- // No frame durations available
- m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[0]);
-
- StreamConfigurationDuration[] jpegStalls =
- new StreamConfigurationDuration[jpegSizes.size()];
- int i = 0;
- long longestStallDuration = -1;
- for (Camera.Size s : jpegSizes) {
- long stallDuration = calculateJpegStallDuration(s);
- jpegStalls[i++] = new StreamConfigurationDuration(HAL_PIXEL_FORMAT_BLOB, s.width,
- s.height, stallDuration);
- if (longestStallDuration < stallDuration) {
- longestStallDuration = stallDuration;
- }
- }
- /*
- * scaler.availableStallDurations
- */
- // Set stall durations for jpeg, other formats use default stall duration
- m.set(SCALER_AVAILABLE_STALL_DURATIONS, jpegStalls);
-
- /*
- * sensor.info.maxFrameDuration
- */
- m.set(SENSOR_INFO_MAX_FRAME_DURATION, longestStallDuration);
- }
-
- @SuppressWarnings({"unchecked"})
- private static void mapControlAe(CameraMetadataNative m, Camera.Parameters p) {
- /*
- * control.aeAvailableAntiBandingModes
- */
- List<String> antiBandingModes = p.getSupportedAntibanding();
- if (antiBandingModes != null && antiBandingModes.size() > 0) { // antibanding is optional
- int[] modes = new int[antiBandingModes.size()];
- int j = 0;
- for (String mode : antiBandingModes) {
- int convertedMode = convertAntiBandingMode(mode);
- if (DEBUG && convertedMode == -1) {
- Log.v(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
- " not supported, skipping...");
- } else {
- modes[j++] = convertedMode;
- }
- }
- m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, Arrays.copyOf(modes, j));
- } else {
- m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, new int[0]);
- }
-
- /*
- * control.aeAvailableTargetFpsRanges
- */
- {
- List<int[]> fpsRanges = p.getSupportedPreviewFpsRange();
- if (fpsRanges == null) {
- throw new AssertionError("Supported FPS ranges cannot be null.");
- }
- int rangesSize = fpsRanges.size();
- if (rangesSize <= 0) {
- throw new AssertionError("At least one FPS range must be supported.");
- }
- Range<Integer>[] ranges = new Range[rangesSize];
- int i = 0;
- for (int[] r : fpsRanges) {
- ranges[i++] = Range.create(
- (int) Math.floor(r[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.0),
- (int) Math.ceil(r[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.0));
- }
- m.set(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, ranges);
- }
-
- /*
- * control.aeAvailableModes
- */
- {
- List<String> flashModes = p.getSupportedFlashModes();
-
- String[] flashModeStrings = new String[] {
- Camera.Parameters.FLASH_MODE_OFF,
- Camera.Parameters.FLASH_MODE_AUTO,
- Camera.Parameters.FLASH_MODE_ON,
- Camera.Parameters.FLASH_MODE_RED_EYE,
- // Map these manually
- Camera.Parameters.FLASH_MODE_TORCH,
- };
- int[] flashModeInts = new int[] {
- CONTROL_AE_MODE_ON,
- CONTROL_AE_MODE_ON_AUTO_FLASH,
- CONTROL_AE_MODE_ON_ALWAYS_FLASH,
- CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE
- };
- int[] aeAvail = ArrayUtils.convertStringListToIntArray(
- flashModes, flashModeStrings, flashModeInts);
-
- // No flash control -> AE is always on
- if (aeAvail == null || aeAvail.length == 0) {
- aeAvail = new int[] {
- CONTROL_AE_MODE_ON
- };
- }
-
- // Note that AE_MODE_OFF is never available.
- m.set(CONTROL_AE_AVAILABLE_MODES, aeAvail);
- }
-
- /*
- * control.aeCompensationRanges
- */
- {
- int min = p.getMinExposureCompensation();
- int max = p.getMaxExposureCompensation();
-
- m.set(CONTROL_AE_COMPENSATION_RANGE, Range.create(min, max));
- }
-
- /*
- * control.aeCompensationStep
- */
- {
- float step = p.getExposureCompensationStep();
-
- m.set(CONTROL_AE_COMPENSATION_STEP, ParamsUtils.createRational(step));
- }
-
- /*
- * control.aeLockAvailable
- */
- {
- boolean aeLockAvailable = p.isAutoExposureLockSupported();
-
- m.set(CONTROL_AE_LOCK_AVAILABLE, aeLockAvailable);
- }
- }
-
-
- @SuppressWarnings({"unchecked"})
- private static void mapControlAf(CameraMetadataNative m, Camera.Parameters p) {
- /*
- * control.afAvailableModes
- */
- {
- List<String> focusModes = p.getSupportedFocusModes();
-
- String[] focusModeStrings = new String[] {
- Camera.Parameters.FOCUS_MODE_AUTO,
- Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
- Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
- Camera.Parameters.FOCUS_MODE_EDOF,
- Camera.Parameters.FOCUS_MODE_INFINITY,
- Camera.Parameters.FOCUS_MODE_MACRO,
- Camera.Parameters.FOCUS_MODE_FIXED,
- };
-
- int[] focusModeInts = new int[] {
- CONTROL_AF_MODE_AUTO,
- CONTROL_AF_MODE_CONTINUOUS_PICTURE,
- CONTROL_AF_MODE_CONTINUOUS_VIDEO,
- CONTROL_AF_MODE_EDOF,
- CONTROL_AF_MODE_OFF,
- CONTROL_AF_MODE_MACRO,
- CONTROL_AF_MODE_OFF
- };
-
- List<Integer> afAvail = ArrayUtils.convertStringListToIntList(
- focusModes, focusModeStrings, focusModeInts);
-
- // No AF modes supported? That's unpossible!
- if (afAvail == null || afAvail.size() == 0) {
- Log.w(TAG, "No AF modes supported (HAL bug); defaulting to AF_MODE_OFF only");
- afAvail = new ArrayList<Integer>(/*capacity*/1);
- afAvail.add(CONTROL_AF_MODE_OFF);
- }
-
- m.set(CONTROL_AF_AVAILABLE_MODES, ArrayUtils.toIntArray(afAvail));
-
- if (DEBUG) {
- Log.v(TAG, "mapControlAf - control.afAvailableModes set to " +
- ListUtils.listToString(afAvail));
- }
- }
- }
-
- private static void mapControlAwb(CameraMetadataNative m, Camera.Parameters p) {
- /*
- * control.awbAvailableModes
- */
-
- {
- List<String> wbModes = p.getSupportedWhiteBalance();
-
- String[] wbModeStrings = new String[] {
- Camera.Parameters.WHITE_BALANCE_AUTO ,
- Camera.Parameters.WHITE_BALANCE_INCANDESCENT ,
- Camera.Parameters.WHITE_BALANCE_FLUORESCENT ,
- Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT ,
- Camera.Parameters.WHITE_BALANCE_DAYLIGHT ,
- Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT ,
- Camera.Parameters.WHITE_BALANCE_TWILIGHT ,
- Camera.Parameters.WHITE_BALANCE_SHADE ,
- };
-
- int[] wbModeInts = new int[] {
- CONTROL_AWB_MODE_AUTO,
- CONTROL_AWB_MODE_INCANDESCENT ,
- CONTROL_AWB_MODE_FLUORESCENT ,
- CONTROL_AWB_MODE_WARM_FLUORESCENT ,
- CONTROL_AWB_MODE_DAYLIGHT ,
- CONTROL_AWB_MODE_CLOUDY_DAYLIGHT ,
- CONTROL_AWB_MODE_TWILIGHT ,
- CONTROL_AWB_MODE_SHADE ,
- // Note that CONTROL_AWB_MODE_OFF is unsupported
- };
-
- List<Integer> awbAvail = ArrayUtils.convertStringListToIntList(
- wbModes, wbModeStrings, wbModeInts);
-
- // No AWB modes supported? That's unpossible!
- if (awbAvail == null || awbAvail.size() == 0) {
- Log.w(TAG, "No AWB modes supported (HAL bug); defaulting to AWB_MODE_AUTO only");
- awbAvail = new ArrayList<Integer>(/*capacity*/1);
- awbAvail.add(CONTROL_AWB_MODE_AUTO);
- }
-
- m.set(CONTROL_AWB_AVAILABLE_MODES, ArrayUtils.toIntArray(awbAvail));
-
- if (DEBUG) {
- Log.v(TAG, "mapControlAwb - control.awbAvailableModes set to " +
- ListUtils.listToString(awbAvail));
- }
-
-
- /*
- * control.awbLockAvailable
- */
- {
- boolean awbLockAvailable = p.isAutoWhiteBalanceLockSupported();
-
- m.set(CONTROL_AWB_LOCK_AVAILABLE, awbLockAvailable);
- }
- }
- }
-
- private static void mapControlOther(CameraMetadataNative m, Camera.Parameters p) {
- /*
- * android.control.availableVideoStabilizationModes
- */
- {
- int stabModes[] = p.isVideoStabilizationSupported() ?
- new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF,
- CONTROL_VIDEO_STABILIZATION_MODE_ON } :
- new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF };
-
- m.set(CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, stabModes);
- }
-
- /*
- * android.control.maxRegions
- */
- final int AE = 0, AWB = 1, AF = 2;
-
- int[] maxRegions = new int[3];
- maxRegions[AE] = p.getMaxNumMeteringAreas();
- maxRegions[AWB] = 0; // AWB regions not supported in API1
- maxRegions[AF] = p.getMaxNumFocusAreas();
-
- if (LIE_ABOUT_AE_MAX_REGIONS) {
- maxRegions[AE] = 0;
- }
- if (LIE_ABOUT_AF_MAX_REGIONS) {
- maxRegions[AF] = 0;
- }
-
- m.set(CONTROL_MAX_REGIONS, maxRegions);
-
- /*
- * android.control.availableEffects
- */
- List<String> effectModes = p.getSupportedColorEffects();
- int[] supportedEffectModes = (effectModes == null) ? new int[0] :
- ArrayUtils.convertStringListToIntArray(effectModes, sLegacyEffectMode,
- sEffectModes);
- m.set(CONTROL_AVAILABLE_EFFECTS, supportedEffectModes);
-
- /*
- * android.control.availableSceneModes
- */
- int maxNumDetectedFaces = p.getMaxNumDetectedFaces();
- List<String> sceneModes = p.getSupportedSceneModes();
- List<Integer> supportedSceneModes =
- ArrayUtils.convertStringListToIntList(sceneModes, sLegacySceneModes, sSceneModes);
-
- // Special case where the only scene mode listed is AUTO => no scene mode
- if (sceneModes != null && sceneModes.size() == 1 &&
- sceneModes.get(0).equals(Parameters.SCENE_MODE_AUTO)) {
- supportedSceneModes = null;
- }
-
- boolean sceneModeSupported = true;
- if (supportedSceneModes == null && maxNumDetectedFaces == 0) {
- sceneModeSupported = false;
- }
-
- if (sceneModeSupported) {
- if (supportedSceneModes == null) {
- supportedSceneModes = new ArrayList<Integer>();
- }
- if (maxNumDetectedFaces > 0) { // always supports FACE_PRIORITY when face detecting
- supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY);
- }
- // Remove all DISABLED occurrences
- if (supportedSceneModes.contains(CONTROL_SCENE_MODE_DISABLED)) {
- while(supportedSceneModes.remove(new Integer(CONTROL_SCENE_MODE_DISABLED))) {}
- }
- m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes));
- } else {
- m.set(CONTROL_AVAILABLE_SCENE_MODES, new int[] {CONTROL_SCENE_MODE_DISABLED});
- }
-
- /*
- * android.control.availableModes
- */
- m.set(CONTROL_AVAILABLE_MODES, sceneModeSupported ?
- new int[] { CONTROL_MODE_AUTO, CONTROL_MODE_USE_SCENE_MODE } :
- new int[] { CONTROL_MODE_AUTO });
- }
-
- private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
- /*
- * We can tell if the lens is fixed focus;
- * but if it's not, we can't tell the minimum focus distance, so leave it null then.
- */
- if (DEBUG) {
- Log.v(TAG, "mapLens - focus-mode='" + p.getFocusMode() + "'");
- }
-
- if (Camera.Parameters.FOCUS_MODE_FIXED.equals(p.getFocusMode())) {
- /*
- * lens.info.minimumFocusDistance
- */
- m.set(LENS_INFO_MINIMUM_FOCUS_DISTANCE, LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS);
-
- if (DEBUG) {
- Log.v(TAG, "mapLens - lens.info.minimumFocusDistance = 0");
- }
- } else {
- if (DEBUG) {
- Log.v(TAG, "mapLens - lens.info.minimumFocusDistance is unknown");
- }
- }
-
- float[] focalLengths = new float[] { p.getFocalLength() };
- m.set(LENS_INFO_AVAILABLE_FOCAL_LENGTHS, focalLengths);
- }
-
- private static void mapFlash(CameraMetadataNative m, Camera.Parameters p) {
- boolean flashAvailable = false;
- List<String> supportedFlashModes = p.getSupportedFlashModes();
-
- if (supportedFlashModes != null) {
- // If only 'OFF' is available, we don't really have flash support
- flashAvailable = !ListUtils.listElementsEqualTo(
- supportedFlashModes, Camera.Parameters.FLASH_MODE_OFF);
- }
-
- /*
- * flash.info.available
- */
- m.set(FLASH_INFO_AVAILABLE, flashAvailable);
- }
-
- private static void mapJpeg(CameraMetadataNative m, Camera.Parameters p) {
- List<Camera.Size> thumbnailSizes = p.getSupportedJpegThumbnailSizes();
-
- if (thumbnailSizes != null) {
- Size[] sizes = convertSizeListToArray(thumbnailSizes);
- Arrays.sort(sizes, new android.hardware.camera2.utils.SizeAreaComparator());
- m.set(JPEG_AVAILABLE_THUMBNAIL_SIZES, sizes);
- }
- }
-
- private static void mapRequest(CameraMetadataNative m, Parameters p) {
- /*
- * request.availableCapabilities
- */
- int[] capabilities = { REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE };
- m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
-
- /*
- * request.availableCharacteristicsKeys
- */
- {
- // TODO: check if the underlying key is supported before listing a key as available
-
- // Note: We only list public keys. Native HALs should list ALL keys regardless of visibility.
-
- Key<?> availableKeys[] = new Key<?>[] {
- CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES ,
- CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES ,
- CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES ,
- CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES ,
- CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE ,
- CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP ,
- CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE ,
- CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES ,
- CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS ,
- CameraCharacteristics.CONTROL_AVAILABLE_MODES ,
- CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES ,
- CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES ,
- CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES ,
- CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE ,
- CameraCharacteristics.CONTROL_MAX_REGIONS ,
- CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE ,
- CameraCharacteristics.FLASH_INFO_AVAILABLE ,
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL ,
- CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES ,
- CameraCharacteristics.LENS_FACING ,
- CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS ,
- CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES ,
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES ,
- CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS ,
- CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT ,
- CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH ,
- CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM ,
-// CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP ,
- CameraCharacteristics.SCALER_CROPPING_TYPE ,
- CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES ,
- CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE ,
- CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE ,
- CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE ,
- CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE ,
- CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE ,
- CameraCharacteristics.SENSOR_ORIENTATION ,
- CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES ,
- CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT ,
- CameraCharacteristics.SYNC_MAX_LATENCY ,
- };
- List<Key<?>> characteristicsKeys = new ArrayList<>(Arrays.asList(availableKeys));
-
- /*
- * Add the conditional keys
- */
- if (m.get(LENS_INFO_MINIMUM_FOCUS_DISTANCE) != null) {
- characteristicsKeys.add(LENS_INFO_MINIMUM_FOCUS_DISTANCE);
- }
-
- m.set(REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
- getTagsForKeys(characteristicsKeys.toArray(new Key<?>[0])));
- }
-
- /*
- * request.availableRequestKeys
- */
- {
- CaptureRequest.Key<?> defaultAvailableKeys[] = new CaptureRequest.Key<?>[] {
- CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE,
- CaptureRequest.CONTROL_AE_ANTIBANDING_MODE,
- CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
- CaptureRequest.CONTROL_AE_LOCK,
- CaptureRequest.CONTROL_AE_MODE,
- CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
- CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_TRIGGER,
- CaptureRequest.CONTROL_AWB_LOCK,
- CaptureRequest.CONTROL_AWB_MODE,
- CaptureRequest.CONTROL_CAPTURE_INTENT,
- CaptureRequest.CONTROL_EFFECT_MODE,
- CaptureRequest.CONTROL_MODE,
- CaptureRequest.CONTROL_SCENE_MODE,
- CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
- CaptureRequest.CONTROL_ZOOM_RATIO,
- CaptureRequest.FLASH_MODE,
- CaptureRequest.JPEG_GPS_COORDINATES,
- CaptureRequest.JPEG_GPS_PROCESSING_METHOD,
- CaptureRequest.JPEG_GPS_TIMESTAMP,
- CaptureRequest.JPEG_ORIENTATION,
- CaptureRequest.JPEG_QUALITY,
- CaptureRequest.JPEG_THUMBNAIL_QUALITY,
- CaptureRequest.JPEG_THUMBNAIL_SIZE,
- CaptureRequest.LENS_FOCAL_LENGTH,
- CaptureRequest.NOISE_REDUCTION_MODE,
- CaptureRequest.SCALER_CROP_REGION,
- CaptureRequest.STATISTICS_FACE_DETECT_MODE,
- };
- ArrayList<CaptureRequest.Key<?>> availableKeys =
- new ArrayList<CaptureRequest.Key<?>>(Arrays.asList(defaultAvailableKeys));
-
- if (p.getMaxNumMeteringAreas() > 0) {
- availableKeys.add(CaptureRequest.CONTROL_AE_REGIONS);
- }
- if (p.getMaxNumFocusAreas() > 0) {
- availableKeys.add(CaptureRequest.CONTROL_AF_REGIONS);
- }
-
- CaptureRequest.Key<?> availableRequestKeys[] =
- new CaptureRequest.Key<?>[availableKeys.size()];
- availableKeys.toArray(availableRequestKeys);
- m.set(REQUEST_AVAILABLE_REQUEST_KEYS, getTagsForKeys(availableRequestKeys));
- }
-
- /*
- * request.availableResultKeys
- */
- {
- CaptureResult.Key<?> defaultAvailableKeys[] = new CaptureResult.Key<?>[] {
- CaptureResult.COLOR_CORRECTION_ABERRATION_MODE ,
- CaptureResult.CONTROL_AE_ANTIBANDING_MODE ,
- CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION ,
- CaptureResult.CONTROL_AE_LOCK ,
- CaptureResult.CONTROL_AE_MODE ,
- CaptureResult.CONTROL_AF_MODE ,
- CaptureResult.CONTROL_AF_STATE ,
- CaptureResult.CONTROL_AWB_MODE ,
- CaptureResult.CONTROL_AWB_LOCK ,
- CaptureResult.CONTROL_MODE ,
- CaptureResult.CONTROL_ZOOM_RATIO ,
- CaptureResult.FLASH_MODE ,
- CaptureResult.JPEG_GPS_COORDINATES ,
- CaptureResult.JPEG_GPS_PROCESSING_METHOD ,
- CaptureResult.JPEG_GPS_TIMESTAMP ,
- CaptureResult.JPEG_ORIENTATION ,
- CaptureResult.JPEG_QUALITY ,
- CaptureResult.JPEG_THUMBNAIL_QUALITY ,
- CaptureResult.LENS_FOCAL_LENGTH ,
- CaptureResult.NOISE_REDUCTION_MODE ,
- CaptureResult.REQUEST_PIPELINE_DEPTH ,
- CaptureResult.SCALER_CROP_REGION ,
- CaptureResult.SENSOR_TIMESTAMP ,
- CaptureResult.STATISTICS_FACE_DETECT_MODE ,
-// CaptureResult.STATISTICS_FACES ,
- };
- List<CaptureResult.Key<?>> availableKeys =
- new ArrayList<CaptureResult.Key<?>>(Arrays.asList(defaultAvailableKeys));
-
- if (p.getMaxNumMeteringAreas() > 0) {
- availableKeys.add(CaptureResult.CONTROL_AE_REGIONS);
- }
- if (p.getMaxNumFocusAreas() > 0) {
- availableKeys.add(CaptureResult.CONTROL_AF_REGIONS);
- }
-
- CaptureResult.Key<?> availableResultKeys[] =
- new CaptureResult.Key<?>[availableKeys.size()];
- availableKeys.toArray(availableResultKeys);
- m.set(REQUEST_AVAILABLE_RESULT_KEYS, getTagsForKeys(availableResultKeys));
- }
-
- /*
- * request.maxNumOutputStreams
- */
- int[] outputStreams = {
- /* RAW */
- REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_RAW,
- /* Processed & Not-Stalling */
- REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC,
- /* Processed & Stalling */
- REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC_STALL,
- };
- m.set(REQUEST_MAX_NUM_OUTPUT_STREAMS, outputStreams);
-
- /*
- * request.maxNumInputStreams
- */
- m.set(REQUEST_MAX_NUM_INPUT_STREAMS, REQUEST_MAX_NUM_INPUT_STREAMS_COUNT);
-
- /*
- * request.partialResultCount
- */
- m.set(REQUEST_PARTIAL_RESULT_COUNT, 1); // No partial results supported
-
- /*
- * request.pipelineMaxDepth
- */
- m.set(REQUEST_PIPELINE_MAX_DEPTH,
- (byte)(REQUEST_PIPELINE_MAX_DEPTH_HAL1 + REQUEST_PIPELINE_MAX_DEPTH_OURS));
- }
-
- private static void mapScaler(CameraMetadataNative m, Parameters p) {
- /*
- * control.zoomRatioRange
- */
- Range<Float> zoomRatioRange = new Range<Float>(1.0f, ParameterUtils.getMaxZoomRatio(p));
- m.set(CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange);
-
- /*
- * scaler.availableMaxDigitalZoom
- */
- m.set(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, ParameterUtils.getMaxZoomRatio(p));
-
- /*
- * scaler.croppingType = CENTER_ONLY
- */
- m.set(SCALER_CROPPING_TYPE, SCALER_CROPPING_TYPE_CENTER_ONLY);
- }
-
- private static void mapSensor(CameraMetadataNative m, Parameters p) {
- // Use the largest jpeg size (by area) for both active array and pixel array
- Size largestJpegSize = getLargestSupportedJpegSizeByArea(p);
- /*
- * sensor.info.activeArraySize, and preCorrectionActiveArraySize
- */
- {
- Rect activeArrayRect = ParamsUtils.createRect(largestJpegSize);
- m.set(SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArrayRect);
- m.set(SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, activeArrayRect);
- }
-
- /*
- * sensor.availableTestPatternModes
- */
- {
- // Only "OFF" test pattern mode is available
- m.set(SENSOR_AVAILABLE_TEST_PATTERN_MODES, new int[] { SENSOR_TEST_PATTERN_MODE_OFF });
- }
-
- /*
- * sensor.info.pixelArraySize
- */
- m.set(SENSOR_INFO_PIXEL_ARRAY_SIZE, largestJpegSize);
-
- /*
- * sensor.info.physicalSize
- */
- {
- /*
- * Assume focal length is at infinity focus and that the lens is rectilinear.
- */
- float focalLength = p.getFocalLength(); // in mm
- double angleHor = p.getHorizontalViewAngle() * Math.PI / 180; // to radians
- double angleVer = p.getVerticalViewAngle() * Math.PI / 180; // to radians
-
- float height = (float)Math.abs(2 * focalLength * Math.tan(angleVer / 2));
- float width = (float)Math.abs(2 * focalLength * Math.tan(angleHor / 2));
-
- m.set(SENSOR_INFO_PHYSICAL_SIZE, new SizeF(width, height)); // in mm
- }
-
- /*
- * sensor.info.timestampSource
- */
- {
- m.set(SENSOR_INFO_TIMESTAMP_SOURCE, SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN);
- }
- }
-
- private static void mapStatistics(CameraMetadataNative m, Parameters p) {
- /*
- * statistics.info.availableFaceDetectModes
- */
- int[] fdModes;
-
- if (p.getMaxNumDetectedFaces() > 0) {
- fdModes = new int[] {
- STATISTICS_FACE_DETECT_MODE_OFF,
- STATISTICS_FACE_DETECT_MODE_SIMPLE
- // FULL is never-listed, since we have no way to query it statically
- };
- } else {
- fdModes = new int[] {
- STATISTICS_FACE_DETECT_MODE_OFF
- };
- }
- m.set(STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, fdModes);
-
- /*
- * statistics.info.maxFaceCount
- */
- m.set(STATISTICS_INFO_MAX_FACE_COUNT, p.getMaxNumDetectedFaces());
- }
-
- private static void mapSync(CameraMetadataNative m, Parameters p) {
- /*
- * sync.maxLatency
- */
- m.set(SYNC_MAX_LATENCY, SYNC_MAX_LATENCY_UNKNOWN);
- }
-
- private static void appendStreamConfig(
- ArrayList<StreamConfiguration> configs, int format, List<Camera.Size> sizes) {
- for (Camera.Size size : sizes) {
- StreamConfiguration config =
- new StreamConfiguration(format, size.width, size.height, /*input*/false);
- configs.add(config);
- }
- }
-
- private final static String[] sLegacySceneModes = {
- Parameters.SCENE_MODE_AUTO,
- Parameters.SCENE_MODE_ACTION,
- Parameters.SCENE_MODE_PORTRAIT,
- Parameters.SCENE_MODE_LANDSCAPE,
- Parameters.SCENE_MODE_NIGHT,
- Parameters.SCENE_MODE_NIGHT_PORTRAIT,
- Parameters.SCENE_MODE_THEATRE,
- Parameters.SCENE_MODE_BEACH,
- Parameters.SCENE_MODE_SNOW,
- Parameters.SCENE_MODE_SUNSET,
- Parameters.SCENE_MODE_STEADYPHOTO,
- Parameters.SCENE_MODE_FIREWORKS,
- Parameters.SCENE_MODE_SPORTS,
- Parameters.SCENE_MODE_PARTY,
- Parameters.SCENE_MODE_CANDLELIGHT,
- Parameters.SCENE_MODE_BARCODE,
- Parameters.SCENE_MODE_HDR,
- };
-
- private final static int[] sSceneModes = {
- CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED,
- CameraCharacteristics.CONTROL_SCENE_MODE_ACTION,
- CameraCharacteristics.CONTROL_SCENE_MODE_PORTRAIT,
- CameraCharacteristics.CONTROL_SCENE_MODE_LANDSCAPE,
- CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT,
- CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT_PORTRAIT,
- CameraCharacteristics.CONTROL_SCENE_MODE_THEATRE,
- CameraCharacteristics.CONTROL_SCENE_MODE_BEACH,
- CameraCharacteristics.CONTROL_SCENE_MODE_SNOW,
- CameraCharacteristics.CONTROL_SCENE_MODE_SUNSET,
- CameraCharacteristics.CONTROL_SCENE_MODE_STEADYPHOTO,
- CameraCharacteristics.CONTROL_SCENE_MODE_FIREWORKS,
- CameraCharacteristics.CONTROL_SCENE_MODE_SPORTS,
- CameraCharacteristics.CONTROL_SCENE_MODE_PARTY,
- CameraCharacteristics.CONTROL_SCENE_MODE_CANDLELIGHT,
- CameraCharacteristics.CONTROL_SCENE_MODE_BARCODE,
- CameraCharacteristics.CONTROL_SCENE_MODE_HDR,
- };
-
- static int convertSceneModeFromLegacy(String mode) {
- if (mode == null) {
- return CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED;
- }
- int index = ArrayUtils.getArrayIndex(sLegacySceneModes, mode);
- if (index < 0) {
- return UNKNOWN_MODE;
- }
- return sSceneModes[index];
- }
-
- static String convertSceneModeToLegacy(int mode) {
- if (mode == CONTROL_SCENE_MODE_FACE_PRIORITY) {
- // OK: Let LegacyFaceDetectMapper handle turning face detection on/off
- return Parameters.SCENE_MODE_AUTO;
- }
-
- int index = ArrayUtils.getArrayIndex(sSceneModes, mode);
- if (index < 0) {
- return null;
- }
- return sLegacySceneModes[index];
- }
-
- private final static String[] sLegacyEffectMode = {
- Parameters.EFFECT_NONE,
- Parameters.EFFECT_MONO,
- Parameters.EFFECT_NEGATIVE,
- Parameters.EFFECT_SOLARIZE,
- Parameters.EFFECT_SEPIA,
- Parameters.EFFECT_POSTERIZE,
- Parameters.EFFECT_WHITEBOARD,
- Parameters.EFFECT_BLACKBOARD,
- Parameters.EFFECT_AQUA,
- };
-
- private final static int[] sEffectModes = {
- CameraCharacteristics.CONTROL_EFFECT_MODE_OFF,
- CameraCharacteristics.CONTROL_EFFECT_MODE_MONO,
- CameraCharacteristics.CONTROL_EFFECT_MODE_NEGATIVE,
- CameraCharacteristics.CONTROL_EFFECT_MODE_SOLARIZE,
- CameraCharacteristics.CONTROL_EFFECT_MODE_SEPIA,
- CameraCharacteristics.CONTROL_EFFECT_MODE_POSTERIZE,
- CameraCharacteristics.CONTROL_EFFECT_MODE_WHITEBOARD,
- CameraCharacteristics.CONTROL_EFFECT_MODE_BLACKBOARD,
- CameraCharacteristics.CONTROL_EFFECT_MODE_AQUA,
- };
-
- static int convertEffectModeFromLegacy(String mode) {
- if (mode == null) {
- return CameraCharacteristics.CONTROL_EFFECT_MODE_OFF;
- }
- int index = ArrayUtils.getArrayIndex(sLegacyEffectMode, mode);
- if (index < 0) {
- return UNKNOWN_MODE;
- }
- return sEffectModes[index];
- }
-
- static String convertEffectModeToLegacy(int mode) {
- int index = ArrayUtils.getArrayIndex(sEffectModes, mode);
- if (index < 0) {
- return null;
- }
- return sLegacyEffectMode[index];
- }
-
- /**
- * Convert the ae antibanding mode from api1 into api2.
- *
- * @param mode the api1 mode, {@code null} is allowed and will return {@code -1}.
- *
- * @return The api2 value, or {@code -1} by default if conversion failed
- */
- private static int convertAntiBandingMode(String mode) {
- if (mode == null) {
- return -1;
- }
-
- switch (mode) {
- case Camera.Parameters.ANTIBANDING_OFF: {
- return CONTROL_AE_ANTIBANDING_MODE_OFF;
- }
- case Camera.Parameters.ANTIBANDING_50HZ: {
- return CONTROL_AE_ANTIBANDING_MODE_50HZ;
- }
- case Camera.Parameters.ANTIBANDING_60HZ: {
- return CONTROL_AE_ANTIBANDING_MODE_60HZ;
- }
- case Camera.Parameters.ANTIBANDING_AUTO: {
- return CONTROL_AE_ANTIBANDING_MODE_AUTO;
- }
- default: {
- Log.w(TAG, "convertAntiBandingMode - Unknown antibanding mode " + mode);
- return -1;
- }
- }
- }
-
- /**
- * Convert the ae antibanding mode from api1 into api2.
- *
- * @param mode the api1 mode, {@code null} is allowed and will return {@code MODE_OFF}.
- *
- * @return The api2 value, or {@code MODE_OFF} by default if conversion failed
- */
- static int convertAntiBandingModeOrDefault(String mode) {
- int antiBandingMode = convertAntiBandingMode(mode);
- if (antiBandingMode == -1) {
- return CONTROL_AE_ANTIBANDING_MODE_OFF;
- }
-
- return antiBandingMode;
- }
-
- private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
- int[] legacyFps = new int[2];
- legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
- legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
- return legacyFps;
- }
-
- /**
- * Return the stall duration for a given output jpeg size in nanoseconds.
- *
- * <p>An 8mp image is chosen to have a stall duration of 0.8 seconds.</p>
- */
- private static long calculateJpegStallDuration(Camera.Size size) {
- long baseDuration = APPROXIMATE_CAPTURE_DELAY_MS * NS_PER_MS; // 200ms for capture
- long area = size.width * (long) size.height;
- long stallPerArea = APPROXIMATE_JPEG_ENCODE_TIME_MS * NS_PER_MS /
- APPROXIMATE_SENSOR_AREA_PX; // 600ms stall for 8mp
- return baseDuration + area * stallPerArea;
- }
-
- /**
- * Set the legacy parameters using the {@link LegacyRequest legacy request}.
- *
- * <p>The legacy request's parameters are changed as a side effect of calling this
- * method.</p>
- *
- * @param request a non-{@code null} legacy request
- */
- public static void convertRequestMetadata(LegacyRequest request) {
- LegacyRequestMapper.convertRequestMetadata(request);
- }
-
- private static final int[] sAllowedTemplates = {
- CameraDevice.TEMPLATE_PREVIEW,
- CameraDevice.TEMPLATE_STILL_CAPTURE,
- CameraDevice.TEMPLATE_RECORD,
- // Disallowed templates in legacy mode:
- // CameraDevice.TEMPLATE_VIDEO_SNAPSHOT,
- // CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
- // CameraDevice.TEMPLATE_MANUAL
- };
-
- /**
- * Create a request template
- *
- * @param c a non-{@code null} camera characteristics for this camera
- * @param templateId a non-negative template ID
- *
- * @return a non-{@code null} request template
- *
- * @throws IllegalArgumentException if {@code templateId} was invalid
- *
- * @see android.hardware.camera2.CameraDevice#TEMPLATE_MANUAL
- */
- public static CameraMetadataNative createRequestTemplate(
- CameraCharacteristics c, int templateId) {
- if (!ArrayUtils.contains(sAllowedTemplates, templateId)) {
- throw new IllegalArgumentException("templateId out of range");
- }
-
- CameraMetadataNative m = new CameraMetadataNative();
-
- /*
- * NOTE: If adding new code here and it needs to query the static info,
- * query the camera characteristics, so we can reuse this for api2 code later
- * to create our own templates in the framework
- */
-
- /*
- * control.*
- */
-
- // control.awbMode
- m.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_AUTO);
- // AWB is always unconditionally available in API1 devices
-
- // control.aeAntibandingMode
- m.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE, CONTROL_AE_ANTIBANDING_MODE_AUTO);
-
- // control.aeExposureCompensation
- m.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0);
-
- // control.aeLock
- m.set(CaptureRequest.CONTROL_AE_LOCK, false);
-
- // control.aePrecaptureTrigger
- m.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
-
- // control.afTrigger
- m.set(CaptureRequest.CONTROL_AF_TRIGGER, CONTROL_AF_TRIGGER_IDLE);
-
- // control.awbMode
- m.set(CaptureRequest.CONTROL_AWB_MODE, CONTROL_AWB_MODE_AUTO);
-
- // control.awbLock
- m.set(CaptureRequest.CONTROL_AWB_LOCK, false);
-
- // control.aeRegions, control.awbRegions, control.afRegions
- {
- Rect activeArray = c.get(SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- MeteringRectangle[] activeRegions = new MeteringRectangle[] {
- new MeteringRectangle(/*x*/0, /*y*/0, /*width*/activeArray.width() - 1,
- /*height*/activeArray.height() - 1,/*weight*/0)};
- m.set(CaptureRequest.CONTROL_AE_REGIONS, activeRegions);
- m.set(CaptureRequest.CONTROL_AWB_REGIONS, activeRegions);
- m.set(CaptureRequest.CONTROL_AF_REGIONS, activeRegions);
- }
-
- // control.captureIntent
- {
- int captureIntent;
- switch (templateId) {
- case CameraDevice.TEMPLATE_PREVIEW:
- captureIntent = CONTROL_CAPTURE_INTENT_PREVIEW;
- break;
- case CameraDevice.TEMPLATE_STILL_CAPTURE:
- captureIntent = CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
- break;
- case CameraDevice.TEMPLATE_RECORD:
- captureIntent = CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
- break;
- default:
- // Can't get anything else since it's guarded by the IAE check
- throw new AssertionError("Impossible; keep in sync with sAllowedTemplates");
- }
- m.set(CaptureRequest.CONTROL_CAPTURE_INTENT, captureIntent);
- }
-
- // control.aeMode
- m.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
- // AE is always unconditionally available in API1 devices
-
- // control.mode
- m.set(CaptureRequest.CONTROL_MODE, CONTROL_MODE_AUTO);
-
- // control.afMode
- {
- Float minimumFocusDistance = c.get(LENS_INFO_MINIMUM_FOCUS_DISTANCE);
-
- int afMode;
- if (minimumFocusDistance != null &&
- minimumFocusDistance == LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS) {
- // Cannot control auto-focus with fixed-focus cameras
- afMode = CameraMetadata.CONTROL_AF_MODE_OFF;
- } else {
- // If a minimum focus distance is reported; the camera must have AF
- afMode = CameraMetadata.CONTROL_AF_MODE_AUTO;
-
- if (templateId == CameraDevice.TEMPLATE_RECORD ||
- templateId == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
- if (ArrayUtils.contains(c.get(CONTROL_AF_AVAILABLE_MODES),
- CONTROL_AF_MODE_CONTINUOUS_VIDEO)) {
- afMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO;
- }
- } else if (templateId == CameraDevice.TEMPLATE_PREVIEW ||
- templateId == CameraDevice.TEMPLATE_STILL_CAPTURE) {
- if (ArrayUtils.contains(c.get(CONTROL_AF_AVAILABLE_MODES),
- CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
- afMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
- }
- }
- }
-
- if (DEBUG) {
- Log.v(TAG, "createRequestTemplate (templateId=" + templateId + ")," +
- " afMode=" + afMode + ", minimumFocusDistance=" + minimumFocusDistance);
- }
-
- m.set(CaptureRequest.CONTROL_AF_MODE, afMode);
- }
-
- {
- // control.aeTargetFpsRange
- Range<Integer>[] availableFpsRange = c.
- get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
-
- // Pick FPS range with highest max value, tiebreak on higher min value
- Range<Integer> bestRange = availableFpsRange[0];
- for (Range<Integer> r : availableFpsRange) {
- if (bestRange.getUpper() < r.getUpper()) {
- bestRange = r;
- } else if (bestRange.getUpper() == r.getUpper() &&
- bestRange.getLower() < r.getLower()) {
- bestRange = r;
- }
- }
- m.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, bestRange);
- }
-
- // control.sceneMode -- DISABLED is always available
- m.set(CaptureRequest.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
-
- // control.zoomRatio -- 1.0
- m.set(CaptureRequest.CONTROL_ZOOM_RATIO, 1.0f);
-
- /*
- * statistics.*
- */
-
- // statistics.faceDetectMode
- m.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, STATISTICS_FACE_DETECT_MODE_OFF);
-
- /*
- * flash.*
- */
-
- // flash.mode
- m.set(CaptureRequest.FLASH_MODE, FLASH_MODE_OFF);
-
- /*
- * noiseReduction.*
- */
- if (templateId == CameraDevice.TEMPLATE_STILL_CAPTURE) {
- m.set(CaptureRequest.NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_HIGH_QUALITY);
- } else {
- m.set(CaptureRequest.NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_FAST);
- }
-
- /*
- * colorCorrection.*
- */
- if (templateId == CameraDevice.TEMPLATE_STILL_CAPTURE) {
- m.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE,
- COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY);
- } else {
- m.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE,
- COLOR_CORRECTION_ABERRATION_MODE_FAST);
- }
-
- /*
- * lens.*
- */
-
- // lens.focalLength
- m.set(CaptureRequest.LENS_FOCAL_LENGTH,
- c.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]);
-
- /*
- * jpeg.*
- */
-
- // jpeg.thumbnailSize - set smallest non-zero size if possible
- Size[] sizes = c.get(CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES);
- m.set(CaptureRequest.JPEG_THUMBNAIL_SIZE, (sizes.length > 1) ? sizes[1] : sizes[0]);
-
- // TODO: map other request template values
- return m;
- }
-
- private static int[] getTagsForKeys(Key<?>[] keys) {
- int[] tags = new int[keys.length];
-
- for (int i = 0; i < keys.length; ++i) {
- tags[i] = keys[i].getNativeKey().getTag();
- }
-
- return tags;
- }
-
- private static int[] getTagsForKeys(CaptureRequest.Key<?>[] keys) {
- int[] tags = new int[keys.length];
-
- for (int i = 0; i < keys.length; ++i) {
- tags[i] = keys[i].getNativeKey().getTag();
- }
-
- return tags;
- }
-
- private static int[] getTagsForKeys(CaptureResult.Key<?>[] keys) {
- int[] tags = new int[keys.length];
-
- for (int i = 0; i < keys.length; ++i) {
- tags[i] = keys[i].getNativeKey().getTag();
- }
-
- return tags;
- }
-
- /**
- * Convert the requested AF mode into its equivalent supported parameter.
- *
- * @param mode {@code CONTROL_AF_MODE}
- * @param supportedFocusModes list of camera1's supported focus modes
- * @return the stringified af mode, or {@code null} if its not supported
- */
- static String convertAfModeToLegacy(int mode, List<String> supportedFocusModes) {
- if (supportedFocusModes == null || supportedFocusModes.isEmpty()) {
- Log.w(TAG, "No focus modes supported; API1 bug");
- return null;
- }
-
- String param = null;
- switch (mode) {
- case CONTROL_AF_MODE_AUTO:
- param = Parameters.FOCUS_MODE_AUTO;
- break;
- case CONTROL_AF_MODE_CONTINUOUS_PICTURE:
- param = Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
- break;
- case CONTROL_AF_MODE_CONTINUOUS_VIDEO:
- param = Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
- break;
- case CONTROL_AF_MODE_EDOF:
- param = Parameters.FOCUS_MODE_EDOF;
- break;
- case CONTROL_AF_MODE_MACRO:
- param = Parameters.FOCUS_MODE_MACRO;
- break;
- case CONTROL_AF_MODE_OFF:
- if (supportedFocusModes.contains(Parameters.FOCUS_MODE_FIXED)) {
- param = Parameters.FOCUS_MODE_FIXED;
- } else {
- param = Parameters.FOCUS_MODE_INFINITY;
- }
- }
-
- if (!supportedFocusModes.contains(param)) {
- // Weed out bad user input by setting to the first arbitrary focus mode
- String defaultMode = supportedFocusModes.get(0);
- Log.w(TAG,
- String.format(
- "convertAfModeToLegacy - ignoring unsupported mode %d, " +
- "defaulting to %s", mode, defaultMode));
- param = defaultMode;
- }
-
- return param;
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequest.java b/core/java/android/hardware/camera2/legacy/LegacyRequest.java
deleted file mode 100644
index f13ac5c881e0..000000000000
--- a/core/java/android/hardware/camera2/legacy/LegacyRequest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.hardware.Camera;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
-import android.util.Size;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Hold important data necessary to build the camera1 parameters up from a capture request.
- */
-public class LegacyRequest {
- /** Immutable characteristics for the camera corresponding to this request */
- public final CameraCharacteristics characteristics;
- /** Immutable capture request, as requested by the user */
- public final CaptureRequest captureRequest;
- /** Immutable api1 preview buffer size at the time of the request */
- public final Size previewSize;
- /** <em>Mutable</em> camera parameters */
- public final Camera.Parameters parameters;
-
- /**
- * Create a new legacy request; the parameters are copied.
- *
- * @param characteristics immutable static camera characteristics for this camera
- * @param captureRequest immutable user-defined capture request
- * @param previewSize immutable internal preview size used for {@link Camera#setPreviewSurface}
- * @param parameters the initial camera1 parameter state; (copied) can be mutated
- */
- public LegacyRequest(CameraCharacteristics characteristics, CaptureRequest captureRequest,
- Size previewSize, Camera.Parameters parameters) {
- this.characteristics = checkNotNull(characteristics, "characteristics must not be null");
- this.captureRequest = checkNotNull(captureRequest, "captureRequest must not be null");
- this.previewSize = checkNotNull(previewSize, "previewSize must not be null");
- checkNotNull(parameters, "parameters must not be null");
-
- this.parameters = Camera.getParametersCopy(parameters);
- }
-
- /**
- * Update the current parameters in-place to be a copy of the new parameters.
- *
- * @param parameters non-{@code null} parameters for api1 camera
- */
- public void setParameters(Camera.Parameters parameters) {
- checkNotNull(parameters, "parameters must not be null");
-
- this.parameters.copyFrom(parameters);
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
deleted file mode 100644
index 3a46379477e9..000000000000
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ /dev/null
@@ -1,688 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.graphics.Rect;
-import android.hardware.Camera;
-import android.hardware.Camera.Parameters;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.params.MeteringRectangle;
-import android.hardware.camera2.utils.ListUtils;
-import android.hardware.camera2.utils.ParamsUtils;
-import android.location.Location;
-import android.util.Log;
-import android.util.Range;
-import android.util.Size;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-import static android.hardware.camera2.CaptureRequest.*;
-
-/**
- * Provide legacy-specific implementations of camera2 CaptureRequest for legacy devices.
- */
-@SuppressWarnings("deprecation")
-public class LegacyRequestMapper {
- private static final String TAG = "LegacyRequestMapper";
- private static final boolean DEBUG = false;
-
- /** Default quality for android.jpeg.quality, android.jpeg.thumbnailQuality */
- private static final byte DEFAULT_JPEG_QUALITY = 85;
-
- /**
- * Set the legacy parameters using the {@link LegacyRequest legacy request}.
- *
- * <p>The legacy request's parameters are changed as a side effect of calling this
- * method.</p>
- *
- * @param legacyRequest a non-{@code null} legacy request
- */
- public static void convertRequestMetadata(LegacyRequest legacyRequest) {
- CameraCharacteristics characteristics = legacyRequest.characteristics;
- CaptureRequest request = legacyRequest.captureRequest;
- Size previewSize = legacyRequest.previewSize;
- Camera.Parameters params = legacyRequest.parameters;
-
- Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-
- /*
- * scaler.cropRegion
- */
- ParameterUtils.ZoomData zoomData;
- {
- zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
- request.get(SCALER_CROP_REGION),
- request.get(CONTROL_ZOOM_RATIO),
- previewSize,
- params);
-
- if (params.isZoomSupported()) {
- params.setZoom(zoomData.zoomIndex);
- } else if (DEBUG) {
- Log.v(TAG, "convertRequestToMetadata - zoom is not supported");
- }
- }
-
- /*
- * colorCorrection.*
- */
- // colorCorrection.aberrationMode
- {
- int aberrationMode = ParamsUtils.getOrDefault(request,
- COLOR_CORRECTION_ABERRATION_MODE,
- /*defaultValue*/COLOR_CORRECTION_ABERRATION_MODE_FAST);
-
- if (aberrationMode != COLOR_CORRECTION_ABERRATION_MODE_FAST &&
- aberrationMode != COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY) {
- Log.w(TAG, "convertRequestToMetadata - Ignoring unsupported " +
- "colorCorrection.aberrationMode = " + aberrationMode);
- }
- }
-
- /*
- * control.ae*
- */
- // control.aeAntibandingMode
- {
- String legacyMode;
- Integer antiBandingMode = request.get(CONTROL_AE_ANTIBANDING_MODE);
- if (antiBandingMode != null) {
- legacyMode = convertAeAntiBandingModeToLegacy(antiBandingMode);
- } else {
- legacyMode = ListUtils.listSelectFirstFrom(params.getSupportedAntibanding(),
- new String[] {
- Parameters.ANTIBANDING_AUTO,
- Parameters.ANTIBANDING_OFF,
- Parameters.ANTIBANDING_50HZ,
- Parameters.ANTIBANDING_60HZ,
- });
- }
-
- if (legacyMode != null) {
- params.setAntibanding(legacyMode);
- }
- }
-
- /*
- * control.aeRegions, afRegions
- */
- {
- // aeRegions
- {
- // Use aeRegions if available, fall back to using awbRegions if present
- MeteringRectangle[] aeRegions = request.get(CONTROL_AE_REGIONS);
- if (request.get(CONTROL_AWB_REGIONS) != null) {
- Log.w(TAG, "convertRequestMetadata - control.awbRegions setting is not " +
- "supported, ignoring value");
- }
- int maxNumMeteringAreas = params.getMaxNumMeteringAreas();
- List<Camera.Area> meteringAreaList = convertMeteringRegionsToLegacy(
- activeArray, zoomData, aeRegions, maxNumMeteringAreas,
- /*regionName*/"AE");
-
- // WAR: for b/17252693, some devices can't handle params.setFocusAreas(null).
- if (maxNumMeteringAreas > 0) {
- params.setMeteringAreas(meteringAreaList);
- }
- }
-
- // afRegions
- {
- MeteringRectangle[] afRegions = request.get(CONTROL_AF_REGIONS);
- int maxNumFocusAreas = params.getMaxNumFocusAreas();
- List<Camera.Area> focusAreaList = convertMeteringRegionsToLegacy(
- activeArray, zoomData, afRegions, maxNumFocusAreas,
- /*regionName*/"AF");
-
- // WAR: for b/17252693, some devices can't handle params.setFocusAreas(null).
- if (maxNumFocusAreas > 0) {
- params.setFocusAreas(focusAreaList);
- }
- }
- }
-
- // control.aeTargetFpsRange
- Range<Integer> aeFpsRange = request.get(CONTROL_AE_TARGET_FPS_RANGE);
- if (aeFpsRange != null) {
- int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
-
- int[] rangeToApply = null;
- for(int[] range : params.getSupportedPreviewFpsRange()) {
- // Round range up/down to integer FPS value
- int intRangeLow = (int) Math.floor(range[0] / 1000.0) * 1000;
- int intRangeHigh = (int) Math.ceil(range[1] / 1000.0) * 1000;
- if (legacyFps[0] == intRangeLow && legacyFps[1] == intRangeHigh) {
- rangeToApply = range;
- break;
- }
- }
- if (rangeToApply != null) {
- params.setPreviewFpsRange(rangeToApply[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
- rangeToApply[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
- } else {
- Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]");
- }
- }
-
- /*
- * control
- */
-
- // control.aeExposureCompensation
- {
- Range<Integer> compensationRange =
- characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
- int compensation = ParamsUtils.getOrDefault(request,
- CONTROL_AE_EXPOSURE_COMPENSATION,
- /*defaultValue*/0);
-
- if (!compensationRange.contains(compensation)) {
- Log.w(TAG,
- "convertRequestMetadata - control.aeExposureCompensation " +
- "is out of range, ignoring value");
- compensation = 0;
- }
-
- params.setExposureCompensation(compensation);
- }
-
- // control.aeLock
- {
- Boolean aeLock = getIfSupported(request, CONTROL_AE_LOCK, /*defaultValue*/false,
- params.isAutoExposureLockSupported(),
- /*allowedValue*/false);
-
- if (aeLock != null) {
- params.setAutoExposureLock(aeLock);
- }
-
- if (DEBUG) {
- Log.v(TAG, "convertRequestToMetadata - control.aeLock set to " + aeLock);
- }
-
- // TODO: Don't add control.aeLock to availableRequestKeys if it's not supported
- }
-
- // control.aeMode, flash.mode
- mapAeAndFlashMode(request, /*out*/params);
-
- // control.afMode
- {
- int afMode = ParamsUtils.getOrDefault(request, CONTROL_AF_MODE,
- /*defaultValue*/CONTROL_AF_MODE_OFF);
- String focusMode = LegacyMetadataMapper.convertAfModeToLegacy(afMode,
- params.getSupportedFocusModes());
-
- if (focusMode != null) {
- params.setFocusMode(focusMode);
- }
-
- if (DEBUG) {
- Log.v(TAG, "convertRequestToMetadata - control.afMode "
- + afMode + " mapped to " + focusMode);
- }
- }
-
- // control.awbMode
- {
- Integer awbMode = getIfSupported(request, CONTROL_AWB_MODE,
- /*defaultValue*/CONTROL_AWB_MODE_AUTO,
- params.getSupportedWhiteBalance() != null,
- /*allowedValue*/CONTROL_AWB_MODE_AUTO);
-
- String whiteBalanceMode = null;
- if (awbMode != null) { // null iff AWB is not supported by camera1 api
- whiteBalanceMode = convertAwbModeToLegacy(awbMode);
- params.setWhiteBalance(whiteBalanceMode);
- }
-
- if (DEBUG) {
- Log.v(TAG, "convertRequestToMetadata - control.awbMode "
- + awbMode + " mapped to " + whiteBalanceMode);
- }
- }
-
- // control.awbLock
- {
- Boolean awbLock = getIfSupported(request, CONTROL_AWB_LOCK, /*defaultValue*/false,
- params.isAutoWhiteBalanceLockSupported(),
- /*allowedValue*/false);
-
- if (awbLock != null) {
- params.setAutoWhiteBalanceLock(awbLock);
- }
-
- // TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
- }
-
- // control.captureIntent
- {
- int captureIntent = ParamsUtils.getOrDefault(request,
- CONTROL_CAPTURE_INTENT,
- /*defaultValue*/CONTROL_CAPTURE_INTENT_PREVIEW);
-
- captureIntent = filterSupportedCaptureIntent(captureIntent);
-
- params.setRecordingHint(
- captureIntent == CONTROL_CAPTURE_INTENT_VIDEO_RECORD ||
- captureIntent == CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT);
- }
-
- // control.videoStabilizationMode
- {
- Integer stabMode = getIfSupported(request, CONTROL_VIDEO_STABILIZATION_MODE,
- /*defaultValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF,
- params.isVideoStabilizationSupported(),
- /*allowedValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF);
-
- if (stabMode != null) {
- params.setVideoStabilization(stabMode == CONTROL_VIDEO_STABILIZATION_MODE_ON);
- }
- }
-
- // lens.focusDistance
- {
- boolean infinityFocusSupported =
- ListUtils.listContains(params.getSupportedFocusModes(),
- Parameters.FOCUS_MODE_INFINITY);
- Float focusDistance = getIfSupported(request, LENS_FOCUS_DISTANCE,
- /*defaultValue*/0f, infinityFocusSupported, /*allowedValue*/0f);
-
- if (focusDistance == null || focusDistance != 0f) {
- Log.w(TAG,
- "convertRequestToMetadata - Ignoring android.lens.focusDistance "
- + infinityFocusSupported + ", only 0.0f is supported");
- }
- }
-
- // control.sceneMode, control.mode
- {
- // TODO: Map FACE_PRIORITY scene mode to face detection.
-
- if (params.getSupportedSceneModes() != null) {
- int controlMode = ParamsUtils.getOrDefault(request, CONTROL_MODE,
- /*defaultValue*/CONTROL_MODE_AUTO);
- String modeToSet;
- switch (controlMode) {
- case CONTROL_MODE_USE_SCENE_MODE: {
- int sceneMode = ParamsUtils.getOrDefault(request, CONTROL_SCENE_MODE,
- /*defaultValue*/CONTROL_SCENE_MODE_DISABLED);
- String legacySceneMode = LegacyMetadataMapper.
- convertSceneModeToLegacy(sceneMode);
- if (legacySceneMode != null) {
- modeToSet = legacySceneMode;
- } else {
- modeToSet = Parameters.SCENE_MODE_AUTO;
- Log.w(TAG, "Skipping unknown requested scene mode: " + sceneMode);
- }
- break;
- }
- case CONTROL_MODE_AUTO: {
- modeToSet = Parameters.SCENE_MODE_AUTO;
- break;
- }
- default: {
- Log.w(TAG, "Control mode " + controlMode +
- " is unsupported, defaulting to AUTO");
- modeToSet = Parameters.SCENE_MODE_AUTO;
- }
- }
- params.setSceneMode(modeToSet);
- }
- }
-
- // control.effectMode
- {
- if (params.getSupportedColorEffects() != null) {
- int effectMode = ParamsUtils.getOrDefault(request, CONTROL_EFFECT_MODE,
- /*defaultValue*/CONTROL_EFFECT_MODE_OFF);
- String legacyEffectMode = LegacyMetadataMapper.convertEffectModeToLegacy(effectMode);
- if (legacyEffectMode != null) {
- params.setColorEffect(legacyEffectMode);
- } else {
- params.setColorEffect(Parameters.EFFECT_NONE);
- Log.w(TAG, "Skipping unknown requested effect mode: " + effectMode);
- }
- }
- }
-
- /*
- * sensor
- */
-
- // sensor.testPattern
- {
- int testPatternMode = ParamsUtils.getOrDefault(request, SENSOR_TEST_PATTERN_MODE,
- /*defaultValue*/SENSOR_TEST_PATTERN_MODE_OFF);
- if (testPatternMode != SENSOR_TEST_PATTERN_MODE_OFF) {
- Log.w(TAG, "convertRequestToMetadata - ignoring sensor.testPatternMode "
- + testPatternMode + "; only OFF is supported");
- }
- }
-
- /*
- * jpeg.*
- */
-
- // jpeg.gpsLocation
- {
- Location location = request.get(JPEG_GPS_LOCATION);
- if (location != null) {
- if (checkForCompleteGpsData(location)) {
- params.setGpsAltitude(location.getAltitude());
- params.setGpsLatitude(location.getLatitude());
- params.setGpsLongitude(location.getLongitude());
- params.setGpsProcessingMethod(location.getProvider().toUpperCase());
- params.setGpsTimestamp(location.getTime());
- } else {
- Log.w(TAG, "Incomplete GPS parameters provided in location " + location);
- }
- } else {
- params.removeGpsData();
- }
- }
-
- // jpeg.orientation
- {
- Integer orientation = request.get(CaptureRequest.JPEG_ORIENTATION);
- params.setRotation(ParamsUtils.getOrDefault(request, JPEG_ORIENTATION,
- (orientation == null) ? 0 : orientation));
- }
-
- // jpeg.quality
- {
- params.setJpegQuality(0xFF & ParamsUtils.getOrDefault(request, JPEG_QUALITY,
- DEFAULT_JPEG_QUALITY));
- }
-
- // jpeg.thumbnailQuality
- {
- params.setJpegThumbnailQuality(0xFF & ParamsUtils.getOrDefault(request,
- JPEG_THUMBNAIL_QUALITY, DEFAULT_JPEG_QUALITY));
- }
-
- // jpeg.thumbnailSize
- {
- List<Camera.Size> sizes = params.getSupportedJpegThumbnailSizes();
-
- if (sizes != null && sizes.size() > 0) {
- Size s = request.get(JPEG_THUMBNAIL_SIZE);
- boolean invalidSize = (s == null) ? false : !ParameterUtils.containsSize(sizes,
- s.getWidth(), s.getHeight());
- if (invalidSize) {
- Log.w(TAG, "Invalid JPEG thumbnail size set " + s + ", skipping thumbnail...");
- }
- if (s == null || invalidSize) {
- // (0,0) = "no thumbnail" in Camera API 1
- params.setJpegThumbnailSize(/*width*/0, /*height*/0);
- } else {
- params.setJpegThumbnailSize(s.getWidth(), s.getHeight());
- }
- }
- }
-
- /*
- * noiseReduction.*
- */
- // noiseReduction.mode
- {
- int mode = ParamsUtils.getOrDefault(request,
- NOISE_REDUCTION_MODE,
- /*defaultValue*/NOISE_REDUCTION_MODE_FAST);
-
- if (mode != NOISE_REDUCTION_MODE_FAST &&
- mode != NOISE_REDUCTION_MODE_HIGH_QUALITY) {
- Log.w(TAG, "convertRequestToMetadata - Ignoring unsupported " +
- "noiseReduction.mode = " + mode);
- }
- }
- }
-
- private static boolean checkForCompleteGpsData(Location location) {
- return location != null && location.getProvider() != null && location.getTime() != 0;
- }
-
- static int filterSupportedCaptureIntent(int captureIntent) {
- switch (captureIntent) {
- case CONTROL_CAPTURE_INTENT_CUSTOM:
- case CONTROL_CAPTURE_INTENT_PREVIEW:
- case CONTROL_CAPTURE_INTENT_STILL_CAPTURE:
- case CONTROL_CAPTURE_INTENT_VIDEO_RECORD:
- case CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT:
- break;
- case CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG:
- case CONTROL_CAPTURE_INTENT_MANUAL:
- captureIntent = CONTROL_CAPTURE_INTENT_PREVIEW;
- Log.w(TAG, "Unsupported control.captureIntent value " + captureIntent
- + "; default to PREVIEW");
- default:
- captureIntent = CONTROL_CAPTURE_INTENT_PREVIEW;
- Log.w(TAG, "Unknown control.captureIntent value " + captureIntent
- + "; default to PREVIEW");
- }
-
- return captureIntent;
- }
-
- private static List<Camera.Area> convertMeteringRegionsToLegacy(
- Rect activeArray, ParameterUtils.ZoomData zoomData,
- MeteringRectangle[] meteringRegions, int maxNumMeteringAreas, String regionName) {
- if (meteringRegions == null || maxNumMeteringAreas <= 0) {
- if (maxNumMeteringAreas > 0) {
- return Arrays.asList(ParameterUtils.CAMERA_AREA_DEFAULT);
- } else {
- return null;
- }
- }
-
- // Add all non-zero weight regions to the list
- List<MeteringRectangle> meteringRectangleList = new ArrayList<>();
- for (MeteringRectangle rect : meteringRegions) {
- if (rect.getMeteringWeight() != MeteringRectangle.METERING_WEIGHT_DONT_CARE) {
- meteringRectangleList.add(rect);
- }
- }
-
- if (meteringRectangleList.size() == 0) {
- Log.w(TAG, "Only received metering rectangles with weight 0.");
- return Arrays.asList(ParameterUtils.CAMERA_AREA_DEFAULT);
- }
-
- // Ignore any regions beyond our maximum supported count
- int countMeteringAreas =
- Math.min(maxNumMeteringAreas, meteringRectangleList.size());
- List<Camera.Area> meteringAreaList = new ArrayList<>(countMeteringAreas);
-
- for (int i = 0; i < countMeteringAreas; ++i) {
- MeteringRectangle rect = meteringRectangleList.get(i);
-
- ParameterUtils.MeteringData meteringData =
- ParameterUtils.convertMeteringRectangleToLegacy(activeArray, rect, zoomData);
- meteringAreaList.add(meteringData.meteringArea);
- }
-
- if (maxNumMeteringAreas < meteringRectangleList.size()) {
- Log.w(TAG,
- "convertMeteringRegionsToLegacy - Too many requested " + regionName +
- " regions, ignoring all beyond the first " + maxNumMeteringAreas);
- }
-
- if (DEBUG) {
- Log.v(TAG, "convertMeteringRegionsToLegacy - " + regionName + " areas = "
- + ParameterUtils.stringFromAreaList(meteringAreaList));
- }
-
- return meteringAreaList;
- }
-
- private static void mapAeAndFlashMode(CaptureRequest r, /*out*/Parameters p) {
- int flashMode = ParamsUtils.getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
- int aeMode = ParamsUtils.getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
-
- List<String> supportedFlashModes = p.getSupportedFlashModes();
-
- String flashModeSetting = null;
-
- // Flash is OFF by default, on cameras that support flash
- if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_OFF)) {
- flashModeSetting = Parameters.FLASH_MODE_OFF;
- }
-
- /*
- * Map all of the control.aeMode* enums, but ignore AE_MODE_OFF since we never support it
- */
-
- // Ignore flash.mode controls unless aeMode == ON
- if (aeMode == CONTROL_AE_MODE_ON) {
- if (flashMode == FLASH_MODE_TORCH) {
- if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_TORCH)) {
- flashModeSetting = Parameters.FLASH_MODE_TORCH;
- } else {
- Log.w(TAG, "mapAeAndFlashMode - Ignore flash.mode == TORCH;" +
- "camera does not support it");
- }
- } else if (flashMode == FLASH_MODE_SINGLE) {
- if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
- flashModeSetting = Parameters.FLASH_MODE_ON;
- } else {
- Log.w(TAG, "mapAeAndFlashMode - Ignore flash.mode == SINGLE;" +
- "camera does not support it");
- }
- } else {
- // Use the default FLASH_MODE_OFF
- }
- } else if (aeMode == CONTROL_AE_MODE_ON_ALWAYS_FLASH) {
- if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
- flashModeSetting = Parameters.FLASH_MODE_ON;
- } else {
- Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_ALWAYS_FLASH;" +
- "camera does not support it");
- }
- } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH) {
- if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_AUTO)) {
- flashModeSetting = Parameters.FLASH_MODE_AUTO;
- } else {
- Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_AUTO_FLASH;" +
- "camera does not support it");
- }
- } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
- if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_RED_EYE)) {
- flashModeSetting = Parameters.FLASH_MODE_RED_EYE;
- } else {
- Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_AUTO_FLASH_REDEYE;"
- + "camera does not support it");
- }
- } else {
- // Default to aeMode == ON, flash = OFF
- }
-
- if (flashModeSetting != null) {
- p.setFlashMode(flashModeSetting);
- }
-
- if (DEBUG) {
- Log.v(TAG,
- "mapAeAndFlashMode - set flash.mode (api1) to " + flashModeSetting
- + ", requested (api2) " + flashMode
- + ", supported (api1) " + ListUtils.listToString(supportedFlashModes));
- }
- }
-
- /**
- * Returns null if the anti-banding mode enum is not supported.
- */
- private static String convertAeAntiBandingModeToLegacy(int mode) {
- switch (mode) {
- case CONTROL_AE_ANTIBANDING_MODE_OFF: {
- return Parameters.ANTIBANDING_OFF;
- }
- case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
- return Parameters.ANTIBANDING_50HZ;
- }
- case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
- return Parameters.ANTIBANDING_60HZ;
- }
- case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
- return Parameters.ANTIBANDING_AUTO;
- }
- default: {
- return null;
- }
- }
- }
-
- private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
- int[] legacyFps = new int[2];
- legacyFps[Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower() * 1000;
- legacyFps[Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper() * 1000;
- return legacyFps;
- }
-
- private static String convertAwbModeToLegacy(int mode) {
- switch (mode) {
- case CONTROL_AWB_MODE_AUTO:
- return Camera.Parameters.WHITE_BALANCE_AUTO;
- case CONTROL_AWB_MODE_INCANDESCENT:
- return Camera.Parameters.WHITE_BALANCE_INCANDESCENT;
- case CONTROL_AWB_MODE_FLUORESCENT:
- return Camera.Parameters.WHITE_BALANCE_FLUORESCENT;
- case CONTROL_AWB_MODE_WARM_FLUORESCENT:
- return Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT;
- case CONTROL_AWB_MODE_DAYLIGHT:
- return Camera.Parameters.WHITE_BALANCE_DAYLIGHT;
- case CONTROL_AWB_MODE_CLOUDY_DAYLIGHT:
- return Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT;
- case CONTROL_AWB_MODE_TWILIGHT:
- return Camera.Parameters.WHITE_BALANCE_TWILIGHT;
- case CONTROL_AWB_MODE_SHADE:
- return Parameters.WHITE_BALANCE_SHADE;
- default:
- Log.w(TAG, "convertAwbModeToLegacy - unrecognized control.awbMode" + mode);
- return Camera.Parameters.WHITE_BALANCE_AUTO;
- }
- }
-
-
- /**
- * Return {@code null} if the value is not supported, otherwise return the retrieved key's
- * value from the request (or the default value if it wasn't set).
- *
- * <p>If the fetched value in the request is equivalent to {@code allowedValue},
- * then omit the warning (e.g. turning off AF lock on a camera
- * that always has the AF lock turned off is a silent no-op), but still return {@code null}.</p>
- *
- * <p>Logs a warning to logcat if the key is not supported by api1 camera device.</p.
- */
- private static <T> T getIfSupported(
- CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue, boolean isSupported,
- T allowedValue) {
- T val = ParamsUtils.getOrDefault(r, key, defaultValue);
-
- if (!isSupported) {
- if (!Objects.equals(val, allowedValue)) {
- Log.w(TAG, key.getName() + " is not supported; ignoring requested value " + val);
- }
- return null;
- }
-
- return val;
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
deleted file mode 100644
index 09edf74f0d4c..000000000000
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.graphics.Rect;
-import android.hardware.Camera;
-import android.hardware.Camera.Parameters;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.legacy.ParameterUtils.WeightedRectangle;
-import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
-import android.hardware.camera2.params.MeteringRectangle;
-import android.hardware.camera2.utils.ListUtils;
-import android.hardware.camera2.utils.ParamsUtils;
-import android.util.Log;
-import android.util.Size;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static android.hardware.camera2.CaptureResult.*;
-
-/**
- * Provide legacy-specific implementations of camera2 CaptureResult for legacy devices.
- */
-@SuppressWarnings("deprecation")
-public class LegacyResultMapper {
- private static final String TAG = "LegacyResultMapper";
- private static final boolean DEBUG = false;
-
- private LegacyRequest mCachedRequest = null;
- private CameraMetadataNative mCachedResult = null;
-
- /**
- * Generate capture result metadata from the legacy camera request.
- *
- * <p>This method caches and reuses the result from the previous call to this method if
- * the {@code parameters} of the subsequent {@link LegacyRequest} passed to this method
- * have not changed.</p>
- *
- * @param legacyRequest a non-{@code null} legacy request containing the latest parameters
- * @param timestamp the timestamp to use for this result in nanoseconds.
- *
- * @return {@link CameraMetadataNative} object containing result metadata.
- */
- public CameraMetadataNative cachedConvertResultMetadata(
- LegacyRequest legacyRequest, long timestamp) {
- CameraMetadataNative result;
- boolean cached;
-
- /*
- * Attempt to look up the result from the cache if the parameters haven't changed
- */
- if (mCachedRequest != null &&
- legacyRequest.parameters.same(mCachedRequest.parameters) &&
- legacyRequest.captureRequest.equals(mCachedRequest.captureRequest)) {
- result = new CameraMetadataNative(mCachedResult);
- cached = true;
- } else {
- result = convertResultMetadata(legacyRequest);
- cached = false;
-
- // Always cache a *copy* of the metadata result,
- // since api2's client side takes ownership of it after it receives a result
- mCachedRequest = legacyRequest;
- mCachedResult = new CameraMetadataNative(result);
- }
-
- /*
- * Unconditionally set fields that change in every single frame
- */
- {
- // sensor.timestamp
- result.set(SENSOR_TIMESTAMP, timestamp);
- }
-
- if (DEBUG) {
- Log.v(TAG, "cachedConvertResultMetadata - cached? " + cached +
- " timestamp = " + timestamp);
-
- Log.v(TAG, "----- beginning of result dump ------");
- result.dumpToLog();
- Log.v(TAG, "----- end of result dump ------");
- }
-
- return result;
- }
-
- /**
- * Generate capture result metadata from the legacy camera request.
- *
- * @param legacyRequest a non-{@code null} legacy request containing the latest parameters
- * @return a {@link CameraMetadataNative} object containing result metadata.
- */
- private static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest) {
- CameraCharacteristics characteristics = legacyRequest.characteristics;
- CaptureRequest request = legacyRequest.captureRequest;
- Size previewSize = legacyRequest.previewSize;
- Camera.Parameters params = legacyRequest.parameters;
-
- CameraMetadataNative result = new CameraMetadataNative();
-
- Rect activeArraySize = characteristics.get(
- CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArraySize,
- request.get(CaptureRequest.SCALER_CROP_REGION),
- request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
- previewSize, params);
-
- /*
- * colorCorrection
- */
- // colorCorrection.aberrationMode
- {
- result.set(COLOR_CORRECTION_ABERRATION_MODE,
- request.get(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE));
- }
-
- /*
- * control
- */
-
- /*
- * control.ae*
- */
- mapAe(result, characteristics, request, activeArraySize, zoomData, /*out*/params);
-
- /*
- * control.af*
- */
- mapAf(result, activeArraySize, zoomData, /*out*/params);
-
- /*
- * control.awb*
- */
- mapAwb(result, /*out*/params);
-
- /*
- * control.captureIntent
- */
- {
- int captureIntent = ParamsUtils.getOrDefault(request,
- CaptureRequest.CONTROL_CAPTURE_INTENT,
- /*defaultValue*/CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
-
- captureIntent = LegacyRequestMapper.filterSupportedCaptureIntent(captureIntent);
-
- result.set(CONTROL_CAPTURE_INTENT, captureIntent);
- }
-
- /*
- * control.mode
- */
- {
- int controlMode = ParamsUtils.getOrDefault(request, CaptureRequest.CONTROL_MODE,
- CONTROL_MODE_AUTO);
- if (controlMode == CaptureResult.CONTROL_MODE_USE_SCENE_MODE) {
- result.set(CONTROL_MODE, CONTROL_MODE_USE_SCENE_MODE);
- } else {
- result.set(CONTROL_MODE, CONTROL_MODE_AUTO);
- }
- }
-
- /*
- * control.sceneMode
- */
- {
- String legacySceneMode = params.getSceneMode();
- int mode = LegacyMetadataMapper.convertSceneModeFromLegacy(legacySceneMode);
- if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
- result.set(CaptureResult.CONTROL_SCENE_MODE, mode);
- // In case of SCENE_MODE == FACE_PRIORITY, LegacyFaceDetectMapper will override
- // the result to say SCENE_MODE == FACE_PRIORITY.
- } else {
- Log.w(TAG, "Unknown scene mode " + legacySceneMode +
- " returned by camera HAL, setting to disabled.");
- result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
- }
- }
-
- /*
- * control.effectMode
- */
- {
- String legacyEffectMode = params.getColorEffect();
- int mode = LegacyMetadataMapper.convertEffectModeFromLegacy(legacyEffectMode);
- if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
- result.set(CaptureResult.CONTROL_EFFECT_MODE, mode);
- } else {
- Log.w(TAG, "Unknown effect mode " + legacyEffectMode +
- " returned by camera HAL, setting to off.");
- result.set(CaptureResult.CONTROL_EFFECT_MODE, CONTROL_EFFECT_MODE_OFF);
- }
- }
-
- // control.videoStabilizationMode
- {
- int stabMode =
- (params.isVideoStabilizationSupported() && params.getVideoStabilization()) ?
- CONTROL_VIDEO_STABILIZATION_MODE_ON :
- CONTROL_VIDEO_STABILIZATION_MODE_OFF;
- result.set(CONTROL_VIDEO_STABILIZATION_MODE, stabMode);
- }
-
- /*
- * flash
- */
- {
- // flash.mode, flash.state mapped in mapAeAndFlashMode
- }
-
- /*
- * lens
- */
- // lens.focusDistance
- {
- if (Parameters.FOCUS_MODE_INFINITY.equals(params.getFocusMode())) {
- result.set(CaptureResult.LENS_FOCUS_DISTANCE, 0.0f);
- }
- }
-
- // lens.focalLength
- result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
-
- /*
- * request
- */
- // request.pipelineDepth
- result.set(REQUEST_PIPELINE_DEPTH,
- characteristics.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
-
- /*
- * scaler
- */
- mapScaler(result, zoomData, /*out*/params);
-
- /*
- * sensor
- */
- // sensor.timestamp varies every frame; mapping is done in #cachedConvertResultMetadata
- {
- // Unconditionally no test patterns
- result.set(SENSOR_TEST_PATTERN_MODE, SENSOR_TEST_PATTERN_MODE_OFF);
- }
-
- /*
- * jpeg
- */
- // jpeg.gpsLocation
- result.set(JPEG_GPS_LOCATION, request.get(CaptureRequest.JPEG_GPS_LOCATION));
-
- // jpeg.orientation
- result.set(JPEG_ORIENTATION, request.get(CaptureRequest.JPEG_ORIENTATION));
-
- // jpeg.quality
- result.set(JPEG_QUALITY, (byte) params.getJpegQuality());
-
- // jpeg.thumbnailQuality
- result.set(JPEG_THUMBNAIL_QUALITY, (byte) params.getJpegThumbnailQuality());
-
- // jpeg.thumbnailSize
- Camera.Size s = params.getJpegThumbnailSize();
- if (s != null) {
- result.set(JPEG_THUMBNAIL_SIZE, ParameterUtils.convertSize(s));
- } else {
- Log.w(TAG, "Null thumbnail size received from parameters.");
- }
-
- /*
- * noiseReduction.*
- */
- // noiseReduction.mode
- result.set(NOISE_REDUCTION_MODE, request.get(CaptureRequest.NOISE_REDUCTION_MODE));
-
- return result;
- }
-
- private static void mapAe(CameraMetadataNative m,
- CameraCharacteristics characteristics,
- CaptureRequest request, Rect activeArray, ZoomData zoomData, /*out*/Parameters p) {
- // control.aeAntiBandingMode
- {
- int antiBandingMode = LegacyMetadataMapper.convertAntiBandingModeOrDefault(
- p.getAntibanding());
- m.set(CONTROL_AE_ANTIBANDING_MODE, antiBandingMode);
- }
-
- // control.aeExposureCompensation
- {
- m.set(CONTROL_AE_EXPOSURE_COMPENSATION, p.getExposureCompensation());
- }
-
- // control.aeLock
- {
- boolean lock = p.isAutoExposureLockSupported() ? p.getAutoExposureLock() : false;
- m.set(CONTROL_AE_LOCK, lock);
- if (DEBUG) {
- Log.v(TAG,
- "mapAe - android.control.aeLock = " + lock +
- ", supported = " + p.isAutoExposureLockSupported());
- }
-
- Boolean requestLock = request.get(CaptureRequest.CONTROL_AE_LOCK);
- if (requestLock != null && requestLock != lock) {
- Log.w(TAG,
- "mapAe - android.control.aeLock was requested to " + requestLock +
- " but resulted in " + lock);
- }
- }
-
- // control.aeMode, flash.mode, flash.state
- mapAeAndFlashMode(m, characteristics, p);
-
- // control.aeState
- if (LegacyMetadataMapper.LIE_ABOUT_AE_STATE) {
- // Lie to pass CTS temporarily.
- // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves
- m.set(CONTROL_AE_STATE, CONTROL_AE_STATE_CONVERGED);
- }
-
- // control.aeRegions
- if (p.getMaxNumMeteringAreas() > 0) {
- if (DEBUG) {
- String meteringAreas = p.get("metering-areas");
- Log.v(TAG, "mapAe - parameter dump; metering-areas: " + meteringAreas);
- }
-
- MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray,
- zoomData, p.getMeteringAreas(), "AE");
-
- m.set(CONTROL_AE_REGIONS, meteringRectArray);
- }
-
- }
-
- private static void mapAf(CameraMetadataNative m,
- Rect activeArray, ZoomData zoomData, Camera.Parameters p) {
- // control.afMode
- m.set(CaptureResult.CONTROL_AF_MODE, convertLegacyAfMode(p.getFocusMode()));
-
- // control.afRegions
- if (p.getMaxNumFocusAreas() > 0) {
- if (DEBUG) {
- String focusAreas = p.get("focus-areas");
- Log.v(TAG, "mapAe - parameter dump; focus-areas: " + focusAreas);
- }
-
- MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray,
- zoomData, p.getFocusAreas(), "AF");
-
- m.set(CONTROL_AF_REGIONS, meteringRectArray);
- }
- }
-
- private static void mapAwb(CameraMetadataNative m, Camera.Parameters p) {
- // control.awbLock
- {
- boolean lock = p.isAutoWhiteBalanceLockSupported() ?
- p.getAutoWhiteBalanceLock() : false;
- m.set(CONTROL_AWB_LOCK, lock);
- }
-
- // control.awbMode
- {
- int awbMode = convertLegacyAwbMode(p.getWhiteBalance());
- m.set(CONTROL_AWB_MODE, awbMode);
- }
- }
-
- private static MeteringRectangle[] getMeteringRectangles(Rect activeArray, ZoomData zoomData,
- List<Camera.Area> meteringAreaList, String regionName) {
- List<MeteringRectangle> meteringRectList = new ArrayList<>();
- if (meteringAreaList != null) {
- for (Camera.Area area : meteringAreaList) {
- WeightedRectangle rect =
- ParameterUtils.convertCameraAreaToActiveArrayRectangle(
- activeArray, zoomData, area);
-
- meteringRectList.add(rect.toMetering());
- }
- }
-
- if (DEBUG) {
- Log.v(TAG,
- "Metering rectangles for " + regionName + ": "
- + ListUtils.listToString(meteringRectList));
- }
-
- return meteringRectList.toArray(new MeteringRectangle[0]);
- }
-
- /** Map results for control.aeMode, flash.mode, flash.state */
- private static void mapAeAndFlashMode(CameraMetadataNative m,
- CameraCharacteristics characteristics, Parameters p) {
- // Default: AE mode on but flash never fires
- int flashMode = FLASH_MODE_OFF;
- // If there is no flash on this camera, the state is always unavailable
- // , otherwise it's only known for TORCH/SINGLE modes
- Integer flashState = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)
- ? null : FLASH_STATE_UNAVAILABLE;
- int aeMode = CONTROL_AE_MODE_ON;
-
- String flashModeSetting = p.getFlashMode();
-
- if (flashModeSetting != null) {
- switch (flashModeSetting) {
- case Parameters.FLASH_MODE_OFF:
- break; // ok, using default
- case Parameters.FLASH_MODE_AUTO:
- aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH;
- break;
- case Parameters.FLASH_MODE_ON:
- // flashMode = SINGLE + aeMode = ON is indistinguishable from ON_ALWAYS_FLASH
- flashMode = FLASH_MODE_SINGLE;
- aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH;
- flashState = FLASH_STATE_FIRED;
- break;
- case Parameters.FLASH_MODE_RED_EYE:
- aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
- break;
- case Parameters.FLASH_MODE_TORCH:
- flashMode = FLASH_MODE_TORCH;
- flashState = FLASH_STATE_FIRED;
- break;
- default:
- Log.w(TAG,
- "mapAeAndFlashMode - Ignoring unknown flash mode " + p.getFlashMode());
- }
- }
-
- // flash.state
- m.set(FLASH_STATE, flashState);
- // flash.mode
- m.set(FLASH_MODE, flashMode);
- // control.aeMode
- m.set(CONTROL_AE_MODE, aeMode);
- }
-
- private static int convertLegacyAfMode(String mode) {
- if (mode == null) {
- Log.w(TAG, "convertLegacyAfMode - no AF mode, default to OFF");
- return CONTROL_AF_MODE_OFF;
- }
-
- switch (mode) {
- case Parameters.FOCUS_MODE_AUTO:
- return CONTROL_AF_MODE_AUTO;
- case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
- return CONTROL_AF_MODE_CONTINUOUS_PICTURE;
- case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
- return CONTROL_AF_MODE_CONTINUOUS_VIDEO;
- case Parameters.FOCUS_MODE_EDOF:
- return CONTROL_AF_MODE_EDOF;
- case Parameters.FOCUS_MODE_MACRO:
- return CONTROL_AF_MODE_MACRO;
- case Parameters.FOCUS_MODE_FIXED:
- return CONTROL_AF_MODE_OFF;
- case Parameters.FOCUS_MODE_INFINITY:
- return CONTROL_AF_MODE_OFF;
- default:
- Log.w(TAG, "convertLegacyAfMode - unknown mode " + mode + " , ignoring");
- return CONTROL_AF_MODE_OFF;
- }
- }
-
- private static int convertLegacyAwbMode(String mode) {
- if (mode == null) {
- // OK: camera1 api may not support changing WB modes; assume AUTO
- return CONTROL_AWB_MODE_AUTO;
- }
-
- switch (mode) {
- case Camera.Parameters.WHITE_BALANCE_AUTO:
- return CONTROL_AWB_MODE_AUTO;
- case Camera.Parameters.WHITE_BALANCE_INCANDESCENT:
- return CONTROL_AWB_MODE_INCANDESCENT;
- case Camera.Parameters.WHITE_BALANCE_FLUORESCENT:
- return CONTROL_AWB_MODE_FLUORESCENT;
- case Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT:
- return CONTROL_AWB_MODE_WARM_FLUORESCENT;
- case Camera.Parameters.WHITE_BALANCE_DAYLIGHT:
- return CONTROL_AWB_MODE_DAYLIGHT;
- case Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT:
- return CONTROL_AWB_MODE_CLOUDY_DAYLIGHT;
- case Camera.Parameters.WHITE_BALANCE_TWILIGHT:
- return CONTROL_AWB_MODE_TWILIGHT;
- case Camera.Parameters.WHITE_BALANCE_SHADE:
- return CONTROL_AWB_MODE_SHADE;
- default:
- Log.w(TAG, "convertAwbMode - unrecognized WB mode " + mode);
- return CONTROL_AWB_MODE_AUTO;
- }
- }
-
- /** Map results for scaler.* */
- private static void mapScaler(CameraMetadataNative m,
- ZoomData zoomData,
- /*out*/Parameters p) {
- /*
- * scaler.cropRegion
- */
- {
- m.set(SCALER_CROP_REGION, zoomData.reportedCrop);
- }
-
- /*
- * control.zoomRatio
- */
- {
- m.set(CONTROL_ZOOM_RATIO, zoomData.reportedZoomRatio);
- }
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
deleted file mode 100644
index eb435989e9a0..000000000000
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ /dev/null
@@ -1,1099 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.hardware.Camera;
-import android.hardware.Camera.Area;
-import android.hardware.camera2.params.Face;
-import android.hardware.camera2.params.MeteringRectangle;
-import android.hardware.camera2.utils.ListUtils;
-import android.hardware.camera2.utils.ParamsUtils;
-import android.hardware.camera2.utils.SizeAreaComparator;
-import android.util.Size;
-import android.util.SizeF;
-
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Various utilities for dealing with camera API1 parameters.
- */
-@SuppressWarnings("deprecation")
-public class ParameterUtils {
- /** Upper/left minimal point of a normalized rectangle */
- public static final int NORMALIZED_RECTANGLE_MIN = -1000;
- /** Lower/right maximal point of a normalized rectangle */
- public static final int NORMALIZED_RECTANGLE_MAX = 1000;
- /** The default normalized rectangle spans the entire size of the preview viewport */
- public static final Rect NORMALIZED_RECTANGLE_DEFAULT = new Rect(
- NORMALIZED_RECTANGLE_MIN,
- NORMALIZED_RECTANGLE_MIN,
- NORMALIZED_RECTANGLE_MAX,
- NORMALIZED_RECTANGLE_MAX);
- /** The default normalized area uses the default normalized rectangle with a weight=1 */
- public static final Camera.Area CAMERA_AREA_DEFAULT =
- new Camera.Area(new Rect(NORMALIZED_RECTANGLE_DEFAULT),
- /*weight*/1);
- /** Empty rectangle {@code 0x0+0,0} */
- public static final Rect RECTANGLE_EMPTY =
- new Rect(/*left*/0, /*top*/0, /*right*/0, /*bottom*/0);
-
- private static final double ASPECT_RATIO_TOLERANCE = 0.05f;
-
- /**
- * Calculate effective/reported zoom data from a user-specified crop region.
- */
- public static class ZoomData {
- /** Zoom index used by {@link Camera.Parameters#setZoom} */
- public final int zoomIndex;
- /** Effective crop-region given the zoom index, coordinates relative to active-array */
- public final Rect previewCrop;
- /** Reported crop-region given the zoom index, coordinates relative to active-array */
- public final Rect reportedCrop;
- /** Reported zoom ratio given the zoom index */
- public final float reportedZoomRatio;
-
- public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop,
- float reportedZoomRatio) {
- this.zoomIndex = zoomIndex;
- this.previewCrop = previewCrop;
- this.reportedCrop = reportedCrop;
- this.reportedZoomRatio = reportedZoomRatio;
- }
- }
-
- /**
- * Calculate effective/reported metering data from a user-specified metering region.
- */
- public static class MeteringData {
- /**
- * The metering area scaled to the range of [-1000, 1000].
- * <p>Values outside of this range are clipped to be within the range.</p>
- */
- public final Camera.Area meteringArea;
- /**
- * Effective preview metering region, coordinates relative to active-array.
- *
- * <p>Clipped to fit inside of the (effective) preview crop region.</p>
- */
- public final Rect previewMetering;
- /**
- * Reported metering region, coordinates relative to active-array.
- *
- * <p>Clipped to fit inside of the (reported) resulting crop region.</p>
- */
- public final Rect reportedMetering;
-
- public MeteringData(Area meteringArea, Rect previewMetering, Rect reportedMetering) {
- this.meteringArea = meteringArea;
- this.previewMetering = previewMetering;
- this.reportedMetering = reportedMetering;
- }
- }
-
- /**
- * A weighted rectangle is an arbitrary rectangle (the coordinate system is unknown) with an
- * arbitrary weight.
- *
- * <p>The user of this class must know what the coordinate system ahead of time; it's
- * then possible to convert to a more concrete type such as a metering rectangle or a face.
- * </p>
- *
- * <p>When converting to a more concrete type, out-of-range values are clipped; this prevents
- * possible illegal argument exceptions being thrown at runtime.</p>
- */
- public static class WeightedRectangle {
- /** Arbitrary rectangle (the range is user-defined); never {@code null}. */
- public final Rect rect;
- /** Arbitrary weight (the range is user-defined). */
- public final int weight;
-
- /**
- * Create a new weighted-rectangle from a non-{@code null} rectangle; the {@code weight}
- * can be unbounded.
- */
- public WeightedRectangle(Rect rect, int weight) {
- this.rect = checkNotNull(rect, "rect must not be null");
- this.weight = weight;
- }
-
- /**
- * Convert to a metering rectangle, clipping any of the values to stay within range.
- *
- * <p>If values are clipped, a warning is printed to logcat.</p>
- *
- * @return a new metering rectangle
- */
- public MeteringRectangle toMetering() {
- int weight = clip(this.weight,
- MeteringRectangle.METERING_WEIGHT_MIN,
- MeteringRectangle.METERING_WEIGHT_MAX,
- rect,
- "weight");
-
- int x = clipLower(rect.left, /*lo*/0, rect, "left");
- int y = clipLower(rect.top, /*lo*/0, rect, "top");
- int w = clipLower(rect.width(), /*lo*/0, rect, "width");
- int h = clipLower(rect.height(), /*lo*/0, rect, "height");
-
- return new MeteringRectangle(x, y, w, h, weight);
- }
-
- /**
- * Convert to a face; the rect is considered to be the bounds, and the weight
- * is considered to be the score.
- *
- * <p>If the score is out of range of {@value Face#SCORE_MIN}, {@value Face#SCORE_MAX},
- * the score is clipped first and a warning is printed to logcat.</p>
- *
- * <p>If the id is negative, the id is changed to 0 and a warning is printed to
- * logcat.</p>
- *
- * <p>All other parameters are passed-through as-is.</p>
- *
- * @return a new face with the optional features set
- */
- public Face toFace(
- int id, Point leftEyePosition, Point rightEyePosition, Point mouthPosition) {
- int idSafe = clipLower(id, /*lo*/0, rect, "id");
- int score = clip(weight,
- Face.SCORE_MIN,
- Face.SCORE_MAX,
- rect,
- "score");
-
- return new Face(rect, score, idSafe, leftEyePosition, rightEyePosition, mouthPosition);
- }
-
- /**
- * Convert to a face; the rect is considered to be the bounds, and the weight
- * is considered to be the score.
- *
- * <p>If the score is out of range of {@value Face#SCORE_MIN}, {@value Face#SCORE_MAX},
- * the score is clipped first and a warning is printed to logcat.</p>
- *
- * <p>All other parameters are passed-through as-is.</p>
- *
- * @return a new face without the optional features
- */
- public Face toFace() {
- int score = clip(weight,
- Face.SCORE_MIN,
- Face.SCORE_MAX,
- rect,
- "score");
-
- return new Face(rect, score);
- }
-
- private static int clipLower(int value, int lo, Rect rect, String name) {
- return clip(value, lo, /*hi*/Integer.MAX_VALUE, rect, name);
- }
-
- private static int clip(int value, int lo, int hi, Rect rect, String name) {
- if (value < lo) {
- Log.w(TAG, "toMetering - Rectangle " + rect + " "
- + name + " too small, clip to " + lo);
- value = lo;
- } else if (value > hi) {
- Log.w(TAG, "toMetering - Rectangle " + rect + " "
- + name + " too small, clip to " + hi);
- value = hi;
- }
-
- return value;
- }
- }
-
- private static final String TAG = "ParameterUtils";
- private static final boolean DEBUG = false;
-
- /** getZoomRatios stores zoom ratios in 1/100 increments, e.x. a zoom of 3.2 is 320 */
- private static final int ZOOM_RATIO_MULTIPLIER = 100;
-
- /**
- * Convert a camera API1 size into a util size
- */
- public static Size convertSize(Camera.Size size) {
- checkNotNull(size, "size must not be null");
-
- return new Size(size.width, size.height);
- }
-
- /**
- * Convert a camera API1 list of sizes into a util list of sizes
- */
- public static List<Size> convertSizeList(List<Camera.Size> sizeList) {
- checkNotNull(sizeList, "sizeList must not be null");
-
- List<Size> sizes = new ArrayList<>(sizeList.size());
- for (Camera.Size s : sizeList) {
- sizes.add(new Size(s.width, s.height));
- }
- return sizes;
- }
-
- /**
- * Convert a camera API1 list of sizes into an array of sizes
- */
- public static Size[] convertSizeListToArray(List<Camera.Size> sizeList) {
- checkNotNull(sizeList, "sizeList must not be null");
-
- Size[] array = new Size[sizeList.size()];
- int ctr = 0;
- for (Camera.Size s : sizeList) {
- array[ctr++] = new Size(s.width, s.height);
- }
- return array;
- }
-
- /**
- * Check if the camera API1 list of sizes contains a size with the given dimens.
- */
- public static boolean containsSize(List<Camera.Size> sizeList, int width, int height) {
- checkNotNull(sizeList, "sizeList must not be null");
- for (Camera.Size s : sizeList) {
- if (s.height == height && s.width == width) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns the largest supported picture size, as compared by its area.
- */
- public static Size getLargestSupportedJpegSizeByArea(Camera.Parameters params) {
- checkNotNull(params, "params must not be null");
-
- List<Size> supportedJpegSizes = convertSizeList(params.getSupportedPictureSizes());
- return SizeAreaComparator.findLargestByArea(supportedJpegSizes);
- }
-
- /**
- * Convert a camera area into a human-readable string.
- */
- public static String stringFromArea(Camera.Area area) {
- if (area == null) {
- return null;
- } else {
- StringBuilder sb = new StringBuilder();
- Rect r = area.rect;
-
- sb.setLength(0);
- sb.append("(["); sb.append(r.left); sb.append(',');
- sb.append(r.top); sb.append("]["); sb.append(r.right);
- sb.append(','); sb.append(r.bottom); sb.append(']');
-
- sb.append(',');
- sb.append(area.weight);
- sb.append(')');
-
- return sb.toString();
- }
- }
-
- /**
- * Convert a camera area list into a human-readable string
- * @param areaList a list of areas (null is ok)
- */
- public static String stringFromAreaList(List<Camera.Area> areaList) {
- StringBuilder sb = new StringBuilder();
-
- if (areaList == null) {
- return null;
- }
-
- int i = 0;
- for (Camera.Area area : areaList) {
- if (area == null) {
- sb.append("null");
- } else {
- sb.append(stringFromArea(area));
- }
-
- if (i != areaList.size() - 1) {
- sb.append(", ");
- }
-
- i++;
- }
-
- return sb.toString();
- }
-
- /**
- * Calculate the closest zoom index for the user-requested crop region by rounding
- * up to the closest (largest or equal) possible zoom crop.
- *
- * <p>If the requested crop region exceeds the size of the active array, it is
- * shrunk to fit inside of the active array first.</p>
- *
- * <p>Since all api1 camera devices only support a discrete set of zooms, we have
- * to translate the per-pixel-granularity requested crop region into a per-zoom-index
- * granularity.</p>
- *
- * <p>Furthermore, since the zoom index and zoom levels also depends on the field-of-view
- * of the preview, the current preview {@code streamSize} is also used.</p>
- *
- * <p>The calculated crop regions are then written to in-place to {@code reportedCropRegion}
- * and {@code previewCropRegion}, in coordinates relative to the active array.</p>
- *
- * @param params non-{@code null} camera api1 parameters
- * @param activeArray active array dimensions, in sensor space
- * @param streamSize stream size dimensions, in pixels
- * @param cropRegion user-specified crop region, in active array coordinates
- * @param reportedCropRegion (out parameter) what the result for {@code cropRegion} looks like
- * @param previewCropRegion (out parameter) what the visual preview crop is
- * @return
- * the zoom index inclusively between 0 and {@code Parameters#getMaxZoom},
- * where 0 means the camera is not zoomed
- *
- * @throws NullPointerException if any of the args were {@code null}
- */
- public static int getClosestAvailableZoomCrop(
- Camera.Parameters params, Rect activeArray,
- Size streamSize, Rect cropRegion,
- /*out*/
- Rect reportedCropRegion,
- Rect previewCropRegion) {
- checkNotNull(params, "params must not be null");
- checkNotNull(activeArray, "activeArray must not be null");
- checkNotNull(streamSize, "streamSize must not be null");
- checkNotNull(reportedCropRegion, "reportedCropRegion must not be null");
- checkNotNull(previewCropRegion, "previewCropRegion must not be null");
-
- Rect actualCrop = new Rect(cropRegion);
-
- /*
- * Shrink requested crop region to fit inside of the active array size
- */
- if (!actualCrop.intersect(activeArray)) {
- Log.w(TAG, "getClosestAvailableZoomCrop - Crop region out of range; " +
- "setting to active array size");
- actualCrop.set(activeArray);
- }
-
- Rect previewCrop = getPreviewCropRectangleUnzoomed(activeArray, streamSize);
-
- // Make the user-requested crop region the same aspect ratio as the preview stream size
- Rect cropRegionAsPreview =
- shrinkToSameAspectRatioCentered(previewCrop, actualCrop);
-
- if (DEBUG) {
- Log.v(TAG, "getClosestAvailableZoomCrop - actualCrop = " + actualCrop);
- Log.v(TAG,
- "getClosestAvailableZoomCrop - previewCrop = " + previewCrop);
- Log.v(TAG,
- "getClosestAvailableZoomCrop - cropRegionAsPreview = " + cropRegionAsPreview);
- }
-
- /*
- * Iterate all available zoom rectangles and find the closest zoom index
- */
- Rect bestReportedCropRegion = null;
- Rect bestPreviewCropRegion = null;
- int bestZoomIndex = -1;
-
- List<Rect> availableReportedCropRegions =
- getAvailableZoomCropRectangles(params, activeArray);
- List<Rect> availablePreviewCropRegions =
- getAvailablePreviewZoomCropRectangles(params, activeArray, streamSize);
-
- if (DEBUG) {
- Log.v(TAG,
- "getClosestAvailableZoomCrop - availableReportedCropRegions = " +
- ListUtils.listToString(availableReportedCropRegions));
- Log.v(TAG,
- "getClosestAvailableZoomCrop - availablePreviewCropRegions = " +
- ListUtils.listToString(availablePreviewCropRegions));
- }
-
- if (availableReportedCropRegions.size() != availablePreviewCropRegions.size()) {
- throw new AssertionError("available reported/preview crop region size mismatch");
- }
-
- for (int i = 0; i < availableReportedCropRegions.size(); ++i) {
- Rect currentPreviewCropRegion = availablePreviewCropRegions.get(i);
- Rect currentReportedCropRegion = availableReportedCropRegions.get(i);
-
- boolean isBest;
- if (bestZoomIndex == -1) {
- isBest = true;
- } else if (currentPreviewCropRegion.width() >= cropRegionAsPreview.width() &&
- currentPreviewCropRegion.height() >= cropRegionAsPreview.height()) {
- isBest = true;
- } else {
- isBest = false;
- }
-
- // Sizes are sorted largest-to-smallest, so once the available crop is too small,
- // we the rest are too small. Furthermore, this is the final best crop,
- // since its the largest crop that still fits the requested crop
- if (isBest) {
- bestPreviewCropRegion = currentPreviewCropRegion;
- bestReportedCropRegion = currentReportedCropRegion;
- bestZoomIndex = i;
- } else {
- break;
- }
- }
-
- if (bestZoomIndex == -1) {
- // Even in the worst case, we should always at least return 0 here
- throw new AssertionError("Should've found at least one valid zoom index");
- }
-
- // Write the rectangles in-place
- reportedCropRegion.set(bestReportedCropRegion);
- previewCropRegion.set(bestPreviewCropRegion);
-
- return bestZoomIndex;
- }
-
- /**
- * Calculate the effective crop rectangle for this preview viewport;
- * assumes the preview is centered to the sensor and scaled to fit across one of the dimensions
- * without skewing.
- *
- * <p>The preview size must be a subset of the active array size; the resulting
- * rectangle will also be a subset of the active array rectangle.</p>
- *
- * <p>The unzoomed crop rectangle is calculated only.</p>
- *
- * @param activeArray active array dimensions, in sensor space
- * @param previewSize size of the preview buffer render target, in pixels (not in sensor space)
- * @return a rectangle which serves as the preview stream's effective crop region (unzoomed),
- * in sensor space
- *
- * @throws NullPointerException
- * if any of the args were {@code null}
- * @throws IllegalArgumentException
- * if {@code previewSize} is wider or taller than {@code activeArray}
- */
- private static Rect getPreviewCropRectangleUnzoomed(Rect activeArray, Size previewSize) {
- if (previewSize.getWidth() > activeArray.width()) {
- throw new IllegalArgumentException("previewSize must not be wider than activeArray");
- } else if (previewSize.getHeight() > activeArray.height()) {
- throw new IllegalArgumentException("previewSize must not be taller than activeArray");
- }
-
- float aspectRatioArray = activeArray.width() * 1.0f / activeArray.height();
- float aspectRatioPreview = previewSize.getWidth() * 1.0f / previewSize.getHeight();
-
- float cropH, cropW;
- if (Math.abs(aspectRatioPreview - aspectRatioArray) < ASPECT_RATIO_TOLERANCE) {
- cropH = activeArray.height();
- cropW = activeArray.width();
- } else if (aspectRatioPreview < aspectRatioArray) {
- // The new width must be smaller than the height, so scale the width by AR
- cropH = activeArray.height();
- cropW = cropH * aspectRatioPreview;
- } else {
- // The new height must be smaller (or equal) than the width, so scale the height by AR
- cropW = activeArray.width();
- cropH = cropW / aspectRatioPreview;
- }
-
- Matrix translateMatrix = new Matrix();
- RectF cropRect = new RectF(/*left*/0, /*top*/0, cropW, cropH);
-
- // Now center the crop rectangle so its center is in the center of the active array
- translateMatrix.setTranslate(activeArray.exactCenterX(), activeArray.exactCenterY());
- translateMatrix.postTranslate(-cropRect.centerX(), -cropRect.centerY());
-
- translateMatrix.mapRect(/*inout*/cropRect);
-
- // Round the rect corners towards the nearest integer values
- return ParamsUtils.createRect(cropRect);
- }
-
- /**
- * Shrink the {@code shrinkTarget} rectangle to snugly fit inside of {@code reference};
- * the aspect ratio of {@code shrinkTarget} will change to be the same aspect ratio as
- * {@code reference}.
- *
- * <p>At most a single dimension will scale (down). Both dimensions will never be scaled.</p>
- *
- * @param reference the rectangle whose aspect ratio will be used as the new aspect ratio
- * @param shrinkTarget the rectangle which will be scaled down to have a new aspect ratio
- *
- * @return a new rectangle, a subset of {@code shrinkTarget},
- * whose aspect ratio will match that of {@code reference}
- */
- private static Rect shrinkToSameAspectRatioCentered(Rect reference, Rect shrinkTarget) {
- float aspectRatioReference = reference.width() * 1.0f / reference.height();
- float aspectRatioShrinkTarget = shrinkTarget.width() * 1.0f / shrinkTarget.height();
-
- float cropH, cropW;
- if (aspectRatioShrinkTarget < aspectRatioReference) {
- // The new width must be smaller than the height, so scale the width by AR
- cropH = reference.height();
- cropW = cropH * aspectRatioShrinkTarget;
- } else {
- // The new height must be smaller (or equal) than the width, so scale the height by AR
- cropW = reference.width();
- cropH = cropW / aspectRatioShrinkTarget;
- }
-
- Matrix translateMatrix = new Matrix();
- RectF shrunkRect = new RectF(shrinkTarget);
-
- // Scale the rectangle down, but keep its center in the same place as before
- translateMatrix.setScale(cropW / reference.width(), cropH / reference.height(),
- shrinkTarget.exactCenterX(), shrinkTarget.exactCenterY());
-
- translateMatrix.mapRect(/*inout*/shrunkRect);
-
- return ParamsUtils.createRect(shrunkRect);
- }
-
- /**
- * Get the available 'crop' (zoom) rectangles for this camera that will be reported
- * via a {@code CaptureResult} when a zoom is requested.
- *
- * <p>These crops ignores the underlying preview buffer size, and will always be reported
- * the same values regardless of what configuration of outputs is used.</p>
- *
- * <p>When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size,
- * where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).</p>
- *
- * <p>Each crop rectangle is changed to have the same aspect ratio as {@code streamSize},
- * by shrinking the rectangle if necessary.</p>
- *
- * <p>To get the reported crop region when applying a zoom to the sensor, use {@code streamSize}
- * = {@code activeArray size}.</p>
- *
- * @param params non-{@code null} camera api1 parameters
- * @param activeArray active array dimensions, in sensor space
- * @param streamSize stream size dimensions, in pixels
- *
- * @return a list of available zoom rectangles, sorted from least zoomed to most zoomed
- */
- public static List<Rect> getAvailableZoomCropRectangles(
- Camera.Parameters params, Rect activeArray) {
- checkNotNull(params, "params must not be null");
- checkNotNull(activeArray, "activeArray must not be null");
-
- return getAvailableCropRectangles(params, activeArray, ParamsUtils.createSize(activeArray));
- }
-
- /**
- * Get the available 'crop' (zoom) rectangles for this camera.
- *
- * <p>This is the effective (real) crop that is applied by the camera api1 device
- * when projecting the zoom onto the intermediate preview buffer. Use this when
- * deciding which zoom ratio to apply.</p>
- *
- * <p>When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size,
- * where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).</p>
- *
- * <p>Each crop rectangle is changed to have the same aspect ratio as {@code streamSize},
- * by shrinking the rectangle if necessary.</p>
- *
- * <p>To get the reported crop region when applying a zoom to the sensor, use {@code streamSize}
- * = {@code activeArray size}.</p>
- *
- * @param params non-{@code null} camera api1 parameters
- * @param activeArray active array dimensions, in sensor space
- * @param streamSize stream size dimensions, in pixels
- *
- * @return a list of available zoom rectangles, sorted from least zoomed to most zoomed
- */
- public static List<Rect> getAvailablePreviewZoomCropRectangles(Camera.Parameters params,
- Rect activeArray, Size previewSize) {
- checkNotNull(params, "params must not be null");
- checkNotNull(activeArray, "activeArray must not be null");
- checkNotNull(previewSize, "previewSize must not be null");
-
- return getAvailableCropRectangles(params, activeArray, previewSize);
- }
-
- /**
- * Get the available 'crop' (zoom) rectangles for this camera.
- *
- * <p>When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size,
- * where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).</p>
- *
- * <p>Each crop rectangle is changed to have the same aspect ratio as {@code streamSize},
- * by shrinking the rectangle if necessary.</p>
- *
- * <p>To get the reported crop region when applying a zoom to the sensor, use {@code streamSize}
- * = {@code activeArray size}.</p>
- *
- * @param params non-{@code null} camera api1 parameters
- * @param activeArray active array dimensions, in sensor space
- * @param streamSize stream size dimensions, in pixels
- *
- * @return a list of available zoom rectangles, sorted from least zoomed to most zoomed
- */
- private static List<Rect> getAvailableCropRectangles(Camera.Parameters params,
- Rect activeArray, Size streamSize) {
- checkNotNull(params, "params must not be null");
- checkNotNull(activeArray, "activeArray must not be null");
- checkNotNull(streamSize, "streamSize must not be null");
-
- // TODO: change all uses of Rect activeArray to Size activeArray,
- // since we want the crop to be active-array relative, not pixel-array relative
-
- Rect unzoomedStreamCrop = getPreviewCropRectangleUnzoomed(activeArray, streamSize);
-
- if (!params.isZoomSupported()) {
- // Trivial case: No zoom -> only support the full size as the crop region
- return new ArrayList<>(Arrays.asList(unzoomedStreamCrop));
- }
-
- List<Rect> zoomCropRectangles = new ArrayList<>(params.getMaxZoom() + 1);
- Matrix scaleMatrix = new Matrix();
- RectF scaledRect = new RectF();
-
- for (int zoom : params.getZoomRatios()) {
- float shrinkRatio = ZOOM_RATIO_MULTIPLIER * 1.0f / zoom; // normalize to 1.0 and smaller
-
- // set scaledRect to unzoomedStreamCrop
- ParamsUtils.convertRectF(unzoomedStreamCrop, /*out*/scaledRect);
-
- scaleMatrix.setScale(
- shrinkRatio, shrinkRatio,
- activeArray.exactCenterX(),
- activeArray.exactCenterY());
-
- scaleMatrix.mapRect(scaledRect);
-
- Rect intRect = ParamsUtils.createRect(scaledRect);
-
- // Round the rect corners towards the nearest integer values
- zoomCropRectangles.add(intRect);
- }
-
- return zoomCropRectangles;
- }
-
- /**
- * Get the largest possible zoom ratio (normalized to {@code 1.0f} and higher)
- * that the camera can support.
- *
- * <p>If the camera does not support zoom, it always returns {@code 1.0f}.</p>
- *
- * @param params non-{@code null} camera api1 parameters
- * @return normalized max zoom ratio, at least {@code 1.0f}
- */
- public static float getMaxZoomRatio(Camera.Parameters params) {
- if (!params.isZoomSupported()) {
- return 1.0f; // no zoom
- }
-
- List<Integer> zoomRatios = params.getZoomRatios(); // sorted smallest->largest
- int zoom = zoomRatios.get(zoomRatios.size() - 1); // largest zoom ratio
- float zoomRatio = zoom * 1.0f / ZOOM_RATIO_MULTIPLIER; // normalize to 1.0 and smaller
-
- return zoomRatio;
- }
-
- /**
- * Returns the component-wise zoom ratio (each greater or equal than {@code 1.0});
- * largest values means more zoom.
- *
- * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
- * @param cropSize size of the crop/zoom
- *
- * @return {@link SizeF} with width/height being the component-wise zoom ratio
- *
- * @throws NullPointerException if any of the args were {@code null}
- * @throws IllegalArgumentException if any component of {@code cropSize} was {@code 0}
- */
- private static SizeF getZoomRatio(Size activeArraySize, Size cropSize) {
- checkNotNull(activeArraySize, "activeArraySize must not be null");
- checkNotNull(cropSize, "cropSize must not be null");
- checkArgumentPositive(cropSize.getWidth(), "cropSize.width must be positive");
- checkArgumentPositive(cropSize.getHeight(), "cropSize.height must be positive");
-
- float zoomRatioWidth = activeArraySize.getWidth() * 1.0f / cropSize.getWidth();
- float zoomRatioHeight = activeArraySize.getHeight() * 1.0f / cropSize.getHeight();
-
- return new SizeF(zoomRatioWidth, zoomRatioHeight);
- }
-
- /**
- * Convert the user-specified crop region/zoom into zoom data; which can be used
- * to set the parameters to a specific zoom index, or to report back to the user what
- * the actual zoom was, or for other calculations requiring the current preview crop region.
- *
- * <p>None of the parameters are mutated.<p>
- *
- * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
- * @param cropRegion the user-specified crop region
- * @param zoomRatio the user-specified zoom ratio
- * @param previewSize the current preview size (in pixels)
- * @param params the current camera parameters (not mutated)
- *
- * @return the zoom index, and the effective/reported crop regions (relative to active array)
- */
- public static ZoomData convertToLegacyZoom(Rect activeArraySize, Rect
- cropRegion, Float zoomRatio, Size previewSize, Camera.Parameters params) {
- final float FLOAT_EQUAL_THRESHOLD = 0.0001f;
- if (zoomRatio != null &&
- Math.abs(1.0f - zoomRatio) > FLOAT_EQUAL_THRESHOLD) {
- // User uses CONTROL_ZOOM_RATIO to control zoom
- return convertZoomRatio(activeArraySize, zoomRatio, previewSize, params);
- }
-
- return convertScalerCropRegion(activeArraySize, cropRegion, previewSize, params);
- }
-
- /**
- * Convert the user-specified zoom ratio into zoom data; which can be used
- * to set the parameters to a specific zoom index, or to report back to the user what the
- * actual zoom was, or for other calculations requiring the current preview crop region.
- *
- * <p>None of the parameters are mutated.</p>
- *
- * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
- * @param zoomRatio the current zoom ratio
- * @param previewSize the current preview size (in pixels)
- * @param params the current camera parameters (not mutated)
- *
- * @return the zoom index, and the effective/reported crop regions (relative to active array)
- */
- public static ZoomData convertZoomRatio(Rect activeArraySize, float zoomRatio,
- Size previewSize, Camera.Parameters params) {
- if (DEBUG) {
- Log.v(TAG, "convertZoomRatio - user zoom ratio was " + zoomRatio);
- }
-
- List<Rect> availableReportedCropRegions =
- getAvailableZoomCropRectangles(params, activeArraySize);
- List<Rect> availablePreviewCropRegions =
- getAvailablePreviewZoomCropRectangles(params, activeArraySize, previewSize);
- if (availableReportedCropRegions.size() != availablePreviewCropRegions.size()) {
- throw new AssertionError("available reported/preview crop region size mismatch");
- }
-
- // Find the best matched legacy zoom ratio for the requested camera2 zoom ratio.
- int bestZoomIndex = 0;
- Rect reportedCropRegion = new Rect(availableReportedCropRegions.get(0));
- Rect previewCropRegion = new Rect(availablePreviewCropRegions.get(0));
- float reportedZoomRatio = 1.0f;
- if (params.isZoomSupported()) {
- List<Integer> zoomRatios = params.getZoomRatios();
- for (int i = 1; i < zoomRatios.size(); i++) {
- if (zoomRatio * ZOOM_RATIO_MULTIPLIER >= zoomRatios.get(i)) {
- bestZoomIndex = i;
- reportedCropRegion = availableReportedCropRegions.get(i);
- previewCropRegion = availablePreviewCropRegions.get(i);
- reportedZoomRatio = zoomRatios.get(i);
- } else {
- break;
- }
- }
- }
-
- if (DEBUG) {
- Log.v(TAG, "convertZoomRatio - zoom calculated to: " +
- "zoomIndex = " + bestZoomIndex +
- ", reported crop region = " + reportedCropRegion +
- ", preview crop region = " + previewCropRegion +
- ", reported zoom ratio = " + reportedZoomRatio);
- }
-
- return new ZoomData(bestZoomIndex, reportedCropRegion,
- previewCropRegion, reportedZoomRatio);
- }
-
- /**
- * Convert the user-specified crop region into zoom data; which can be used
- * to set the parameters to a specific zoom index, or to report back to the user what the
- * actual zoom was, or for other calculations requiring the current preview crop region.
- *
- * <p>None of the parameters are mutated.</p>
- *
- * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
- * @param cropRegion the user-specified crop region
- * @param previewSize the current preview size (in pixels)
- * @param params the current camera parameters (not mutated)
- *
- * @return the zoom index, and the effective/reported crop regions (relative to active array)
- */
- public static ZoomData convertScalerCropRegion(Rect activeArraySize, Rect
- cropRegion, Size previewSize, Camera.Parameters params) {
- Rect activeArraySizeOnly = new Rect(
- /*left*/0, /*top*/0,
- activeArraySize.width(), activeArraySize.height());
-
- Rect userCropRegion = cropRegion;
-
- if (userCropRegion == null) {
- userCropRegion = activeArraySizeOnly;
- }
-
- if (DEBUG) {
- Log.v(TAG, "convertScalerCropRegion - user crop region was " + userCropRegion);
- }
-
- final Rect reportedCropRegion = new Rect();
- final Rect previewCropRegion = new Rect();
- final int zoomIdx = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
- previewSize, userCropRegion,
- /*out*/reportedCropRegion, /*out*/previewCropRegion);
- final float reportedZoomRatio = 1.0f;
-
- if (DEBUG) {
- Log.v(TAG, "convertScalerCropRegion - zoom calculated to: " +
- "zoomIndex = " + zoomIdx +
- ", reported crop region = " + reportedCropRegion +
- ", preview crop region = " + previewCropRegion +
- ", reported zoom ratio = " + reportedZoomRatio);
- }
-
- return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion, reportedZoomRatio);
- }
-
- /**
- * Calculate the actual/effective/reported normalized rectangle data from a metering
- * rectangle.
- *
- * <p>If any of the rectangles are out-of-range of their intended bounding box,
- * the {@link #RECTANGLE_EMPTY empty rectangle} is substituted instead
- * (with a weight of {@code 0}).</p>
- *
- * <p>The metering rectangle is bound by the crop region (effective/reported respectively).
- * The metering {@link Camera.Area area} is bound by {@code [-1000, 1000]}.</p>
- *
- * <p>No parameters are mutated; returns the new metering data.</p>
- *
- * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
- * @param meteringRect the user-specified metering rectangle
- * @param zoomData the calculated zoom data corresponding to this request
- *
- * @return the metering area, the reported/effective metering rectangles
- */
- public static MeteringData convertMeteringRectangleToLegacy(
- Rect activeArray, MeteringRectangle meteringRect, ZoomData zoomData) {
- Rect previewCrop = zoomData.previewCrop;
-
- float scaleW = (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN) * 1.0f /
- previewCrop.width();
- float scaleH = (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN) * 1.0f /
- previewCrop.height();
-
- Matrix transform = new Matrix();
- // Move the preview crop so that top,left is at (0,0), otherwise after scaling
- // the corner bounds will be outside of [-1000, 1000]
- transform.setTranslate(-previewCrop.left, -previewCrop.top);
- // Scale into [0, 2000] range about the center of the preview
- transform.postScale(scaleW, scaleH);
- // Move so that top left of a typical rect is at [-1000, -1000]
- transform.postTranslate(/*dx*/NORMALIZED_RECTANGLE_MIN, /*dy*/NORMALIZED_RECTANGLE_MIN);
-
- /*
- * Calculate the preview metering region (effective), and the camera1 api
- * normalized metering region.
- */
- Rect normalizedRegionUnbounded = ParamsUtils.mapRect(transform, meteringRect.getRect());
-
- /*
- * Try to intersect normalized area with [-1000, 1000] rectangle; otherwise
- * it's completely out of range
- */
- Rect normalizedIntersected = new Rect(normalizedRegionUnbounded);
-
- Camera.Area meteringArea;
- if (!normalizedIntersected.intersect(NORMALIZED_RECTANGLE_DEFAULT)) {
- Log.w(TAG,
- "convertMeteringRectangleToLegacy - metering rectangle too small, " +
- "no metering will be done");
- normalizedIntersected.set(RECTANGLE_EMPTY);
- meteringArea = new Camera.Area(RECTANGLE_EMPTY,
- MeteringRectangle.METERING_WEIGHT_DONT_CARE);
- } else {
- meteringArea = new Camera.Area(normalizedIntersected,
- meteringRect.getMeteringWeight());
- }
-
- /*
- * Calculate effective preview metering region
- */
- Rect previewMetering = meteringRect.getRect();
- if (!previewMetering.intersect(previewCrop)) {
- previewMetering.set(RECTANGLE_EMPTY);
- }
-
- /*
- * Calculate effective reported metering region
- * - Transform the calculated metering area back into active array space
- * - Clip it to be a subset of the reported crop region
- */
- Rect reportedMetering;
- {
- Camera.Area normalizedAreaUnbounded = new Camera.Area(
- normalizedRegionUnbounded, meteringRect.getMeteringWeight());
- WeightedRectangle reportedMeteringRect = convertCameraAreaToActiveArrayRectangle(
- activeArray, zoomData, normalizedAreaUnbounded, /*usePreviewCrop*/false);
- reportedMetering = reportedMeteringRect.rect;
- }
-
- if (DEBUG) {
- Log.v(TAG, String.format(
- "convertMeteringRectangleToLegacy - activeArray = %s, meteringRect = %s, " +
- "previewCrop = %s, meteringArea = %s, previewMetering = %s, " +
- "reportedMetering = %s, normalizedRegionUnbounded = %s",
- activeArray, meteringRect,
- previewCrop, stringFromArea(meteringArea), previewMetering,
- reportedMetering, normalizedRegionUnbounded));
- }
-
- return new MeteringData(meteringArea, previewMetering, reportedMetering);
- }
-
- /**
- * Convert the normalized camera area from [-1000, 1000] coordinate space
- * into the active array-based coordinate space.
- *
- * <p>Values out of range are clipped to be within the resulting (reported) crop
- * region. It is possible to have values larger than the preview crop.</p>
- *
- * <p>Weights out of range of [0, 1000] are clipped to be within the range.</p>
- *
- * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
- * @param zoomData the calculated zoom data corresponding to this request
- * @param area the normalized camera area
- *
- * @return the weighed rectangle in active array coordinate space, with the weight
- */
- public static WeightedRectangle convertCameraAreaToActiveArrayRectangle(
- Rect activeArray, ZoomData zoomData, Camera.Area area) {
- return convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, area,
- /*usePreviewCrop*/true);
- }
-
- /**
- * Convert an api1 face into an active-array based api2 face.
- *
- * <p>Out-of-ranges scores and ids will be clipped to be within range (with a warning).</p>
- *
- * @param face a non-{@code null} api1 face
- * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
- * @param zoomData the calculated zoom data corresponding to this request
- *
- * @return a non-{@code null} api2 face
- *
- * @throws NullPointerException if the {@code face} was {@code null}
- */
- public static Face convertFaceFromLegacy(Camera.Face face, Rect activeArray,
- ZoomData zoomData) {
- checkNotNull(face, "face must not be null");
-
- Face api2Face;
-
- Camera.Area fakeArea = new Camera.Area(face.rect, /*weight*/1);
-
- WeightedRectangle faceRect =
- convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, fakeArea);
-
- Point leftEye = face.leftEye, rightEye = face.rightEye, mouth = face.mouth;
- if (leftEye != null && rightEye != null && mouth != null && leftEye.x != -2000 &&
- leftEye.y != -2000 && rightEye.x != -2000 && rightEye.y != -2000 &&
- mouth.x != -2000 && mouth.y != -2000) {
- leftEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
- leftEye, /*usePreviewCrop*/true);
- rightEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
- leftEye, /*usePreviewCrop*/true);
- mouth = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
- leftEye, /*usePreviewCrop*/true);
-
- api2Face = faceRect.toFace(face.id, leftEye, rightEye, mouth);
- } else {
- api2Face = faceRect.toFace();
- }
-
- return api2Face;
- }
-
- private static Point convertCameraPointToActiveArrayPoint(
- Rect activeArray, ZoomData zoomData, Point point, boolean usePreviewCrop) {
- Rect pointedRect = new Rect(point.x, point.y, point.x, point.y);
- Camera.Area pointedArea = new Area(pointedRect, /*weight*/1);
-
- WeightedRectangle adjustedRect =
- convertCameraAreaToActiveArrayRectangle(activeArray,
- zoomData, pointedArea, usePreviewCrop);
-
- Point transformedPoint = new Point(adjustedRect.rect.left, adjustedRect.rect.top);
-
- return transformedPoint;
- }
-
- private static WeightedRectangle convertCameraAreaToActiveArrayRectangle(
- Rect activeArray, ZoomData zoomData, Camera.Area area, boolean usePreviewCrop) {
- Rect previewCrop = zoomData.previewCrop;
- Rect reportedCrop = zoomData.reportedCrop;
-
- float scaleW = previewCrop.width() * 1.0f /
- (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN);
- float scaleH = previewCrop.height() * 1.0f /
- (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN);
-
- /*
- * Calculate the reported metering region from the non-intersected normalized region
- * by scaling and translating back into active array-relative coordinates.
- */
- Matrix transform = new Matrix();
-
- // Move top left from (-1000, -1000) to (0, 0)
- transform.setTranslate(/*dx*/NORMALIZED_RECTANGLE_MAX, /*dy*/NORMALIZED_RECTANGLE_MAX);
-
- // Scale from [0, 2000] back into the preview rectangle
- transform.postScale(scaleW, scaleH);
-
- // Move the rect so that the [-1000,-1000] point ends up at the preview [left, top]
- transform.postTranslate(previewCrop.left, previewCrop.top);
-
- Rect cropToIntersectAgainst = usePreviewCrop ? previewCrop : reportedCrop;
-
- // Now apply the transformation backwards to get the reported metering region
- Rect reportedMetering = ParamsUtils.mapRect(transform, area.rect);
- // Intersect it with the crop region, to avoid reporting out-of-bounds
- // metering regions
- if (!reportedMetering.intersect(cropToIntersectAgainst)) {
- reportedMetering.set(RECTANGLE_EMPTY);
- }
-
- int weight = area.weight;
- if (weight < MeteringRectangle.METERING_WEIGHT_MIN) {
- Log.w(TAG,
- "convertCameraAreaToMeteringRectangle - rectangle "
- + stringFromArea(area) + " has too small weight, clip to 0");
- weight = 0;
- }
-
- return new WeightedRectangle(reportedMetering, area.weight);
- }
-
-
- private ParameterUtils() {
- throw new AssertionError();
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/PerfMeasurement.java b/core/java/android/hardware/camera2/legacy/PerfMeasurement.java
deleted file mode 100644
index 53278c7e4f97..000000000000
--- a/core/java/android/hardware/camera2/legacy/PerfMeasurement.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.io.BufferedWriter;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * GPU and CPU performance measurement for the legacy implementation.
- *
- * <p>Measures CPU and GPU processing duration for a set of operations, and dumps
- * the results into a file.</p>
- *
- * <p>Rough usage:
- * <pre>
- * {@code
- * <set up workload>
- * <start long-running workload>
- * mPerfMeasurement.startTimer();
- * ...render a frame...
- * mPerfMeasurement.stopTimer();
- * <end workload>
- * mPerfMeasurement.dumpPerformanceData("/sdcard/my_data.txt");
- * }
- * </pre>
- * </p>
- *
- * <p>All calls to this object must be made within the same thread, and the same GL context.
- * PerfMeasurement cannot be used outside of a GL context. The only exception is
- * dumpPerformanceData, which can be called outside of a valid GL context.</p>
- */
-class PerfMeasurement {
- private static final String TAG = "PerfMeasurement";
-
- public static final int DEFAULT_MAX_QUERIES = 3;
-
- private final long mNativeContext;
-
- private int mCompletedQueryCount = 0;
-
- /**
- * Values for completed measurements
- */
- private ArrayList<Long> mCollectedGpuDurations = new ArrayList<>();
- private ArrayList<Long> mCollectedCpuDurations = new ArrayList<>();
- private ArrayList<Long> mCollectedTimestamps = new ArrayList<>();
-
- /**
- * Values for in-progress measurements (waiting for async GPU results)
- */
- private Queue<Long> mTimestampQueue = new LinkedList<>();
- private Queue<Long> mCpuDurationsQueue = new LinkedList<>();
-
- private long mStartTimeNs;
-
- /**
- * The value returned by {@link #nativeGetNextGlDuration} if no new timing
- * measurement is available since the last call.
- */
- private static final long NO_DURATION_YET = -1l;
-
- /**
- * The value returned by {@link #nativeGetNextGlDuration} if timing failed for
- * the next timing interval
- */
- private static final long FAILED_TIMING = -2l;
-
- /**
- * Create a performance measurement object with a maximum of {@value #DEFAULT_MAX_QUERIES}
- * in-progess queries.
- */
- public PerfMeasurement() {
- mNativeContext = nativeCreateContext(DEFAULT_MAX_QUERIES);
- }
-
- /**
- * Create a performance measurement object with maxQueries as the maximum number of
- * in-progress queries.
- *
- * @param maxQueries maximum in-progress queries, must be larger than 0.
- * @throws IllegalArgumentException if maxQueries is less than 1.
- */
- public PerfMeasurement(int maxQueries) {
- if (maxQueries < 1) throw new IllegalArgumentException("maxQueries is less than 1");
- mNativeContext = nativeCreateContext(maxQueries);
- }
-
- /**
- * Returns true if the Gl timing methods will work, false otherwise.
- *
- * <p>Must be called within a valid GL context.</p>
- */
- public static boolean isGlTimingSupported() {
- return nativeQuerySupport();
- }
-
- /**
- * Dump collected data to file, and clear the stored data.
- *
- * <p>
- * Format is a simple csv-like text file with a header,
- * followed by a 3-column list of values in nanoseconds:
- * <pre>
- * timestamp gpu_duration cpu_duration
- * <long> <long> <long>
- * <long> <long> <long>
- * <long> <long> <long>
- * ....
- * </pre>
- * </p>
- */
- public void dumpPerformanceData(String path) {
- try (BufferedWriter dump = new BufferedWriter(new FileWriter(path))) {
- dump.write("timestamp gpu_duration cpu_duration\n");
- for (int i = 0; i < mCollectedGpuDurations.size(); i++) {
- dump.write(String.format("%d %d %d\n",
- mCollectedTimestamps.get(i),
- mCollectedGpuDurations.get(i),
- mCollectedCpuDurations.get(i)));
- }
- mCollectedTimestamps.clear();
- mCollectedGpuDurations.clear();
- mCollectedCpuDurations.clear();
- } catch (IOException e) {
- Log.e(TAG, "Error writing data dump to " + path + ":" + e);
- }
- }
-
- /**
- * Start a GPU/CPU timing measurement.
- *
- * <p>Call before starting a rendering pass. Only one timing measurement can be active at once,
- * so {@link #stopTimer} must be called before the next call to this method.</p>
- *
- * @throws IllegalStateException if the maximum number of queries are in progress already,
- * or the method is called multiple times in a row, or there is
- * a GPU error.
- */
- public void startTimer() {
- nativeStartGlTimer(mNativeContext);
- mStartTimeNs = SystemClock.elapsedRealtimeNanos();
- }
-
- /**
- * Finish a GPU/CPU timing measurement.
- *
- * <p>Call after finishing all the drawing for a rendering pass. Only one timing measurement can
- * be active at once, so {@link #startTimer} must be called before the next call to this
- * method.</p>
- *
- * @throws IllegalStateException if no GL timer is currently started, or there is a GPU
- * error.
- */
- public void stopTimer() {
- // Complete CPU timing
- long endTimeNs = SystemClock.elapsedRealtimeNanos();
- mCpuDurationsQueue.add(endTimeNs - mStartTimeNs);
- // Complete GL timing
- nativeStopGlTimer(mNativeContext);
-
- // Poll to see if GL timing results have arrived; if so
- // store the results for a frame
- long duration = getNextGlDuration();
- if (duration > 0) {
- mCollectedGpuDurations.add(duration);
- mCollectedTimestamps.add(mTimestampQueue.isEmpty() ?
- NO_DURATION_YET : mTimestampQueue.poll());
- mCollectedCpuDurations.add(mCpuDurationsQueue.isEmpty() ?
- NO_DURATION_YET : mCpuDurationsQueue.poll());
- }
- if (duration == FAILED_TIMING) {
- // Discard timestamp and CPU measurement since GPU measurement failed
- if (!mTimestampQueue.isEmpty()) {
- mTimestampQueue.poll();
- }
- if (!mCpuDurationsQueue.isEmpty()) {
- mCpuDurationsQueue.poll();
- }
- }
- }
-
- /**
- * Add a timestamp to a timing measurement. These are queued up and matched to completed
- * workload measurements as they become available.
- */
- public void addTimestamp(long timestamp) {
- mTimestampQueue.add(timestamp);
- }
-
- /**
- * Get the next available GPU timing measurement.
- *
- * <p>Since the GPU works asynchronously, the results of a single start/stopGlTimer measurement
- * will only be available some time after the {@link #stopTimer} call is made. Poll this method
- * until the result becomes available. If multiple start/endTimer measurements are made in a
- * row, the results will be available in FIFO order.</p>
- *
- * @return The measured duration of the GPU workload for the next pending query, or
- * {@link #NO_DURATION_YET} if no queries are pending or the next pending query has not
- * yet finished, or {@link #FAILED_TIMING} if the GPU was unable to complete the
- * measurement.
- *
- * @throws IllegalStateException If there is a GPU error.
- *
- */
- private long getNextGlDuration() {
- long duration = nativeGetNextGlDuration(mNativeContext);
- if (duration > 0) {
- mCompletedQueryCount++;
- }
- return duration;
- }
-
- /**
- * Returns the number of measurements so far that returned a valid duration
- * measurement.
- */
- public int getCompletedQueryCount() {
- return mCompletedQueryCount;
- }
-
- @Override
- protected void finalize() {
- nativeDeleteContext(mNativeContext);
- }
-
- /**
- * Create a native performance measurement context.
- *
- * @param maxQueryCount maximum in-progress queries; must be >= 1.
- */
- private static native long nativeCreateContext(int maxQueryCount);
-
- /**
- * Delete the native context.
- *
- * <p>Not safe to call more than once.</p>
- */
- private static native void nativeDeleteContext(long contextHandle);
-
- /**
- * Query whether the relevant Gl extensions are available for Gl timing
- */
- private static native boolean nativeQuerySupport();
-
- /**
- * Start a GL timing section.
- *
- * <p>All GL commands between this method and the next {@link #nativeEndGlTimer} will be
- * included in the timing.</p>
- *
- * <p>Must be called from the same thread as calls to {@link #nativeEndGlTimer} and
- * {@link #nativeGetNextGlDuration}.</p>
- *
- * @throws IllegalStateException if a GL error occurs or start is called repeatedly.
- */
- protected static native void nativeStartGlTimer(long contextHandle);
-
- /**
- * Finish a GL timing section.
- *
- * <p>Some time after this call returns, the time the GPU took to
- * execute all work submitted between the latest {@link #nativeStartGlTimer} and
- * this call, will become available from calling {@link #nativeGetNextGlDuration}.</p>
- *
- * <p>Must be called from the same thread as calls to {@link #nativeStartGlTimer} and
- * {@link #nativeGetNextGlDuration}.</p>
- *
- * @throws IllegalStateException if a GL error occurs or stop is called before start
- */
- protected static native void nativeStopGlTimer(long contextHandle);
-
- /**
- * Get the next available GL duration measurement, in nanoseconds.
- *
- * <p>Must be called from the same thread as calls to {@link #nativeStartGlTimer} and
- * {@link #nativeEndGlTimer}.</p>
- *
- * @return the next GL duration measurement, or {@link #NO_DURATION_YET} if
- * no new measurement is available, or {@link #FAILED_TIMING} if timing
- * failed for the next duration measurement.
- * @throws IllegalStateException if a GL error occurs
- */
- protected static native long nativeGetNextGlDuration(long contextHandle);
-
-
-}
diff --git a/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java b/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
deleted file mode 100644
index e19ebf2d616b..000000000000
--- a/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.MessageQueue;
-
-public class RequestHandlerThread extends HandlerThread {
-
- /**
- * Ensure that the MessageQueue's idle handler gets run by poking the message queue;
- * normally if the message queue is already idle, the idle handler won't get invoked.
- *
- * <p>Users of this handler thread should ignore this message.</p>
- */
- public final static int MSG_POKE_IDLE_HANDLER = -1;
-
- private final ConditionVariable mStarted = new ConditionVariable(false);
- private final ConditionVariable mIdle = new ConditionVariable(true);
- private Handler.Callback mCallback;
- private volatile Handler mHandler;
-
- public RequestHandlerThread(String name, Handler.Callback callback) {
- super(name, Thread.MAX_PRIORITY);
- mCallback = callback;
- }
-
- @Override
- protected void onLooperPrepared() {
- mHandler = new Handler(getLooper(), mCallback);
- mStarted.open();
- }
-
- // Blocks until thread has started
- public void waitUntilStarted() {
- mStarted.block();
- }
-
- // May return null if the handler is not set up yet.
- public Handler getHandler() {
- return mHandler;
- }
-
- // Blocks until thread has started
- public Handler waitAndGetHandler() {
- waitUntilStarted();
- return getHandler();
- }
-
- // Atomic multi-type message existence check
- public boolean hasAnyMessages(int[] what) {
- synchronized (mHandler.getLooper().getQueue()) {
- for (int i : what) {
- if (mHandler.hasMessages(i)) {
- return true;
- }
- }
- }
- return false;
- }
-
- // Atomic multi-type message remove
- public void removeMessages(int[] what) {
- synchronized (mHandler.getLooper().getQueue()) {
- for (int i : what) {
- mHandler.removeMessages(i);
- }
- }
- }
-
- private final MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
- @Override
- public boolean queueIdle() {
- mIdle.open();
- return false;
- }
- };
-
- // Blocks until thread is idling
- public void waitUntilIdle() {
- Handler handler = waitAndGetHandler();
- MessageQueue queue = handler.getLooper().getQueue();
- if (queue.isIdle()) {
- return;
- }
- mIdle.close();
- queue.addIdleHandler(mIdleHandler);
- // Ensure that the idle handler gets run even if the looper already went idle
- handler.sendEmptyMessage(MSG_POKE_IDLE_HANDLER);
- if (queue.isIdle()) {
- return;
- }
- mIdle.block();
- }
-
-}
diff --git a/core/java/android/hardware/camera2/legacy/RequestHolder.java b/core/java/android/hardware/camera2/legacy/RequestHolder.java
deleted file mode 100644
index 98b761b8a04f..000000000000
--- a/core/java/android/hardware/camera2/legacy/RequestHolder.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.hardware.camera2.CaptureRequest;
-import android.util.Log;
-import android.view.Surface;
-
-import java.util.Collection;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Semi-immutable container for a single capture request and associated information,
- * the only mutable characteristic of this container is whether or not is has been
- * marked as "failed" using {@code #failRequest}.
- */
-public class RequestHolder {
- private static final String TAG = "RequestHolder";
-
- private final boolean mRepeating;
- private final CaptureRequest mRequest;
- private final int mRequestId;
- private final int mSubsequeceId;
- private final long mFrameNumber;
- private final int mNumJpegTargets;
- private final int mNumPreviewTargets;
- private volatile boolean mFailed = false;
- private boolean mOutputAbandoned = false;
-
- private final Collection<Long> mJpegSurfaceIds;
-
- /**
- * A builder class for {@link RequestHolder} objects.
- *
- * <p>
- * This allows per-request queries to be cached for repeating {@link CaptureRequest} objects.
- * </p>
- */
- public final static class Builder {
- private final int mRequestId;
- private final int mSubsequenceId;
- private final CaptureRequest mRequest;
- private final boolean mRepeating;
- private final int mNumJpegTargets;
- private final int mNumPreviewTargets;
- private final Collection<Long> mJpegSurfaceIds;
-
- /**
- * Construct a new {@link Builder} to generate {@link RequestHolder} objects.
- *
- * @param requestId the ID to set in {@link RequestHolder} objects.
- * @param subsequenceId the sequence ID to set in {@link RequestHolder} objects.
- * @param request the original {@link CaptureRequest} to set in {@link RequestHolder}
- * objects.
- * @param repeating {@code true} if the request is repeating.
- */
- public Builder(int requestId, int subsequenceId, CaptureRequest request,
- boolean repeating, Collection<Long> jpegSurfaceIds) {
- checkNotNull(request, "request must not be null");
- mRequestId = requestId;
- mSubsequenceId = subsequenceId;
- mRequest = request;
- mRepeating = repeating;
- mJpegSurfaceIds = jpegSurfaceIds;
- mNumJpegTargets = numJpegTargets(mRequest);
- mNumPreviewTargets = numPreviewTargets(mRequest);
- }
-
- /**
- * Returns true if the given surface requires jpeg buffers.
- *
- * @param s a {@link android.view.Surface} to check.
- * @return true if the surface requires a jpeg buffer.
- */
- private boolean jpegType(Surface s)
- throws LegacyExceptionUtils.BufferQueueAbandonedException {
- return LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds);
- }
-
- /**
- * Returns true if the given surface requires non-jpeg buffer types.
- *
- * <p>
- * "Jpeg buffer" refers to the buffers returned in the jpeg
- * {@link android.hardware.Camera.PictureCallback}. Non-jpeg buffers are created using a tee
- * of the preview stream drawn to the surface
- * set via {@link android.hardware.Camera#setPreviewDisplay(android.view.SurfaceHolder)} or
- * equivalent methods.
- * </p>
- * @param s a {@link android.view.Surface} to check.
- * @return true if the surface requires a non-jpeg buffer type.
- */
- private boolean previewType(Surface s)
- throws LegacyExceptionUtils.BufferQueueAbandonedException {
- return !jpegType(s);
- }
-
- /**
- * Returns the number of surfaces targeted by the request that require jpeg buffers.
- */
- private int numJpegTargets(CaptureRequest request) {
- int count = 0;
- for (Surface s : request.getTargets()) {
- try {
- if (jpegType(s)) {
- ++count;
- }
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.d(TAG, "Surface abandoned, skipping...", e);
- }
- }
- return count;
- }
-
- /**
- * Returns the number of surfaces targeted by the request that require non-jpeg buffers.
- */
- private int numPreviewTargets(CaptureRequest request) {
- int count = 0;
- for (Surface s : request.getTargets()) {
- try {
- if (previewType(s)) {
- ++count;
- }
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.d(TAG, "Surface abandoned, skipping...", e);
- }
- }
- return count;
- }
-
- /**
- * Build a new {@link RequestHolder} using with parameters generated from this
- * {@link Builder}.
- *
- * @param frameNumber the {@code framenumber} to generate in the {@link RequestHolder}.
- * @return a {@link RequestHolder} constructed with the {@link Builder}'s parameters.
- */
- public RequestHolder build(long frameNumber) {
- return new RequestHolder(mRequestId, mSubsequenceId, mRequest, mRepeating, frameNumber,
- mNumJpegTargets, mNumPreviewTargets, mJpegSurfaceIds);
- }
- }
-
- private RequestHolder(int requestId, int subsequenceId, CaptureRequest request,
- boolean repeating, long frameNumber, int numJpegTargets,
- int numPreviewTargets, Collection<Long> jpegSurfaceIds) {
- mRepeating = repeating;
- mRequest = request;
- mRequestId = requestId;
- mSubsequeceId = subsequenceId;
- mFrameNumber = frameNumber;
- mNumJpegTargets = numJpegTargets;
- mNumPreviewTargets = numPreviewTargets;
- mJpegSurfaceIds = jpegSurfaceIds;
- }
-
- /**
- * Return the request id for the contained {@link CaptureRequest}.
- */
- public int getRequestId() {
- return mRequestId;
- }
-
- /**
- * Returns true if the contained request is repeating.
- */
- public boolean isRepeating() {
- return mRepeating;
- }
-
- /**
- * Return the subsequence id for this request.
- */
- public int getSubsequeceId() {
- return mSubsequeceId;
- }
-
- /**
- * Returns the frame number for this request.
- */
- public long getFrameNumber() {
- return mFrameNumber;
- }
-
- /**
- * Returns the contained request.
- */
- public CaptureRequest getRequest() {
- return mRequest;
- }
-
- /**
- * Returns a read-only collection of the surfaces targeted by the contained request.
- */
- public Collection<Surface> getHolderTargets() {
- return getRequest().getTargets();
- }
-
- /**
- * Returns true if any of the surfaces targeted by the contained request require jpeg buffers.
- */
- public boolean hasJpegTargets() {
- return mNumJpegTargets > 0;
- }
-
- /**
- * Returns true if any of the surfaces targeted by the contained request require a
- * non-jpeg buffer type.
- */
- public boolean hasPreviewTargets(){
- return mNumPreviewTargets > 0;
- }
-
- /**
- * Return the number of jpeg-type surfaces targeted by this request.
- */
- public int numJpegTargets() {
- return mNumJpegTargets;
- }
-
- /**
- * Return the number of non-jpeg-type surfaces targeted by this request.
- */
- public int numPreviewTargets() {
- return mNumPreviewTargets;
- }
-
- /**
- * Returns true if the given surface requires jpeg buffers.
- *
- * @param s a {@link android.view.Surface} to check.
- * @return true if the surface requires a jpeg buffer.
- */
- public boolean jpegType(Surface s)
- throws LegacyExceptionUtils.BufferQueueAbandonedException {
- return LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds);
- }
-
- /**
- * Mark this request as failed.
- */
- public void failRequest() {
- Log.w(TAG, "Capture failed for request: " + getRequestId());
- mFailed = true;
- }
-
- /**
- * Return {@code true} if this request failed.
- */
- public boolean requestFailed() {
- return mFailed;
- }
-
- /**
- * Mark at least one of this request's output surfaces is abandoned.
- */
- public void setOutputAbandoned() {
- mOutputAbandoned = true;
- }
-
- /**
- * Return if any of this request's output surface is abandoned.
- */
- public boolean isOutputAbandoned() {
- return mOutputAbandoned;
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/RequestQueue.java b/core/java/android/hardware/camera2/legacy/RequestQueue.java
deleted file mode 100644
index fb444022c6db..000000000000
--- a/core/java/android/hardware/camera2/legacy/RequestQueue.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.legacy;
-
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.utils.SubmitInfo;
-import android.util.Log;
-
-import java.util.ArrayDeque;
-import java.util.List;
-
-/**
- * A queue of bursts of requests.
- *
- * <p>This queue maintains the count of frames that have been produced, and is thread safe.</p>
- */
-public class RequestQueue {
- private static final String TAG = "RequestQueue";
-
- public static final long INVALID_FRAME = -1;
-
- private BurstHolder mRepeatingRequest = null;
- private final ArrayDeque<BurstHolder> mRequestQueue = new ArrayDeque<BurstHolder>();
-
- private long mCurrentFrameNumber = 0;
- private long mCurrentRepeatingFrameNumber = INVALID_FRAME;
- private int mCurrentRequestId = 0;
- private final List<Long> mJpegSurfaceIds;
-
- public final class RequestQueueEntry {
- private final BurstHolder mBurstHolder;
- private final Long mFrameNumber;
- private final boolean mQueueEmpty;
-
- public BurstHolder getBurstHolder() {
- return mBurstHolder;
- }
- public Long getFrameNumber() {
- return mFrameNumber;
- }
- public boolean isQueueEmpty() {
- return mQueueEmpty;
- }
-
- public RequestQueueEntry(BurstHolder burstHolder, Long frameNumber, boolean queueEmpty) {
- mBurstHolder = burstHolder;
- mFrameNumber = frameNumber;
- mQueueEmpty = queueEmpty;
- }
- }
-
- public RequestQueue(List<Long> jpegSurfaceIds) {
- mJpegSurfaceIds = jpegSurfaceIds;
- }
-
- /**
- * Return and remove the next burst on the queue.
- *
- * <p>If a repeating burst is returned, it will not be removed.</p>
- *
- * @return an entry containing the next burst, the current frame number, and flag about whether
- * request queue becomes empty. Null if no burst exists.
- */
- public synchronized RequestQueueEntry getNext() {
- BurstHolder next = mRequestQueue.poll();
- boolean queueEmptied = (next != null && mRequestQueue.size() == 0);
- if (next == null && mRepeatingRequest != null) {
- next = mRepeatingRequest;
- mCurrentRepeatingFrameNumber = mCurrentFrameNumber +
- next.getNumberOfRequests();
- }
-
- if (next == null) {
- return null;
- }
-
- RequestQueueEntry ret = new RequestQueueEntry(next, mCurrentFrameNumber, queueEmptied);
- mCurrentFrameNumber += next.getNumberOfRequests();
- return ret;
- }
-
- /**
- * Cancel a repeating request.
- *
- * @param requestId the id of the repeating request to cancel.
- * @return the last frame to be returned from the HAL for the given repeating request, or
- * {@code INVALID_FRAME} if none exists.
- */
- public synchronized long stopRepeating(int requestId) {
- long ret = INVALID_FRAME;
- if (mRepeatingRequest != null && mRepeatingRequest.getRequestId() == requestId) {
- mRepeatingRequest = null;
- ret = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
- mCurrentRepeatingFrameNumber - 1;
- mCurrentRepeatingFrameNumber = INVALID_FRAME;
- Log.i(TAG, "Repeating capture request cancelled.");
- } else {
- Log.e(TAG, "cancel failed: no repeating request exists for request id: " + requestId);
- }
- return ret;
- }
-
- /**
- * Cancel a repeating request.
- *
- * @return the last frame to be returned from the HAL for the given repeating request, or
- * {@code INVALID_FRAME} if none exists.
- */
- public synchronized long stopRepeating() {
- if (mRepeatingRequest == null) {
- Log.e(TAG, "cancel failed: no repeating request exists.");
- return INVALID_FRAME;
- }
- return stopRepeating(mRepeatingRequest.getRequestId());
- }
-
- /**
- * Add a the given burst to the queue.
- *
- * <p>If the burst is repeating, replace the current repeating burst.</p>
- *
- * @param requests the burst of requests to add to the queue.
- * @param repeating true if the burst is repeating.
- * @return the submission info, including the new request id, and the last frame number, which
- * contains either the frame number of the last frame that will be returned for this request,
- * or the frame number of the last frame that will be returned for the current repeating
- * request if this burst is set to be repeating.
- */
- public synchronized SubmitInfo submit(CaptureRequest[] requests, boolean repeating) {
- int requestId = mCurrentRequestId++;
- BurstHolder burst = new BurstHolder(requestId, repeating, requests, mJpegSurfaceIds);
- long lastFrame = INVALID_FRAME;
- if (burst.isRepeating()) {
- Log.i(TAG, "Repeating capture request set.");
- if (mRepeatingRequest != null) {
- lastFrame = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
- mCurrentRepeatingFrameNumber - 1;
- }
- mCurrentRepeatingFrameNumber = INVALID_FRAME;
- mRepeatingRequest = burst;
- } else {
- mRequestQueue.offer(burst);
- lastFrame = calculateLastFrame(burst.getRequestId());
- }
- SubmitInfo info = new SubmitInfo(requestId, lastFrame);
- return info;
- }
-
- private long calculateLastFrame(int requestId) {
- long total = mCurrentFrameNumber;
- for (BurstHolder b : mRequestQueue) {
- total += b.getNumberOfRequests();
- if (b.getRequestId() == requestId) {
- return total - 1;
- }
- }
- throw new IllegalStateException(
- "At least one request must be in the queue to calculate frame number");
- }
-
-}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
deleted file mode 100644
index f9a5029bffaa..000000000000
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ /dev/null
@@ -1,1126 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.legacy;
-
-import android.graphics.SurfaceTexture;
-import android.hardware.Camera;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.impl.CameraDeviceImpl;
-import android.hardware.camera2.utils.SubmitInfo;
-import android.hardware.camera2.utils.SizeAreaComparator;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.MutableLong;
-import android.util.Pair;
-import android.util.Size;
-import android.view.Surface;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * This class executes requests to the {@link Camera}.
- *
- * <p>
- * The main components of this class are:
- * - A message queue of requests to the {@link Camera}.
- * - A thread that consumes requests to the {@link Camera} and executes them.
- * - A {@link GLThreadManager} that draws to the configured output {@link Surface}s.
- * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations.
- * </p>
- */
-@SuppressWarnings("deprecation")
-public class RequestThreadManager {
- private final String TAG;
- private final int mCameraId;
- private final RequestHandlerThread mRequestThread;
-
- private static final boolean DEBUG = false;
- // For slightly more spammy messages that will get repeated every frame
- private static final boolean VERBOSE = false;
- private Camera mCamera;
- private final CameraCharacteristics mCharacteristics;
-
- private final CameraDeviceState mDeviceState;
- private final CaptureCollector mCaptureCollector;
- private final LegacyFocusStateMapper mFocusStateMapper;
- private final LegacyFaceDetectMapper mFaceDetectMapper;
-
- private static final int MSG_CONFIGURE_OUTPUTS = 1;
- private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
- private static final int MSG_CLEANUP = 3;
-
- private static final int MAX_IN_FLIGHT_REQUESTS = 2;
-
- private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms
- private static final int JPEG_FRAME_TIMEOUT = 4000; // ms (same as CTS for API2)
- private static final int REQUEST_COMPLETE_TIMEOUT = JPEG_FRAME_TIMEOUT;
-
- private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
- private boolean mPreviewRunning = false;
-
- private final List<Surface> mPreviewOutputs = new ArrayList<>();
- private final List<Surface> mCallbackOutputs = new ArrayList<>();
- private GLThreadManager mGLThreadManager;
- private SurfaceTexture mPreviewTexture;
- private Camera.Parameters mParams;
-
- private final List<Long> mJpegSurfaceIds = new ArrayList<>();
-
- private Size mIntermediateBufferSize;
-
- private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds);
- private LegacyRequest mLastRequest = null;
- private SurfaceTexture mDummyTexture;
- private Surface mDummySurface;
-
- private final Object mIdleLock = new Object();
- private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
- private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
-
- private final AtomicBoolean mQuit = new AtomicBoolean(false);
-
- // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write
- // limitations for (b/17379185).
- private static final boolean USE_BLOB_FORMAT_OVERRIDE = true;
-
- /**
- * Container object for Configure messages.
- */
- private static class ConfigureHolder {
- public final ConditionVariable condition;
- public final Collection<Pair<Surface, Size>> surfaces;
-
- public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface,
- Size>> surfaces) {
- this.condition = condition;
- this.surfaces = surfaces;
- }
- }
-
- /**
- * Counter class used to calculate and log the current FPS of frame production.
- */
- public static class FpsCounter {
- //TODO: Hook this up to SystTrace?
- private static final String TAG = "FpsCounter";
- private int mFrameCount = 0;
- private long mLastTime = 0;
- private long mLastPrintTime = 0;
- private double mLastFps = 0;
- private final String mStreamType;
- private static final long NANO_PER_SECOND = 1000000000; //ns
-
- public FpsCounter(String streamType) {
- mStreamType = streamType;
- }
-
- public synchronized void countFrame() {
- mFrameCount++;
- long nextTime = SystemClock.elapsedRealtimeNanos();
- if (mLastTime == 0) {
- mLastTime = nextTime;
- }
- if (nextTime > mLastTime + NANO_PER_SECOND) {
- long elapsed = nextTime - mLastTime;
- mLastFps = mFrameCount * (NANO_PER_SECOND / (double) elapsed);
- mFrameCount = 0;
- mLastTime = nextTime;
- }
- }
-
- public synchronized double checkFps() {
- return mLastFps;
- }
-
- public synchronized void staggeredLog() {
- if (mLastTime > mLastPrintTime + 5 * NANO_PER_SECOND) {
- mLastPrintTime = mLastTime;
- Log.d(TAG, "FPS for " + mStreamType + " stream: " + mLastFps );
- }
- }
-
- public synchronized void countAndLog() {
- countFrame();
- staggeredLog();
- }
- }
- /**
- * Fake preview for jpeg captures when there is no active preview
- */
- private void createDummySurface() {
- if (mDummyTexture == null || mDummySurface == null) {
- mDummyTexture = new SurfaceTexture(/*ignored*/0);
- // TODO: use smallest default sizes
- mDummyTexture.setDefaultBufferSize(640, 480);
- mDummySurface = new Surface(mDummyTexture);
- }
- }
-
- private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
- @Override
- public void onError(int i, Camera camera) {
- switch(i) {
- case Camera.CAMERA_ERROR_EVICTED: {
- flush();
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED);
- } break;
- case Camera.CAMERA_ERROR_DISABLED: {
- flush();
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISABLED);
- } break;
- default: {
- Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- } break;
- }
- }
- };
-
- private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
-
- private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
- @Override
- public void onPictureTaken(byte[] data, Camera camera) {
- Log.i(TAG, "Received jpeg.");
- Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced();
- if (captureInfo == null || captureInfo.first == null) {
- Log.e(TAG, "Dropping jpeg frame.");
- return;
- }
- RequestHolder holder = captureInfo.first;
- long timestamp = captureInfo.second;
- for (Surface s : holder.getHolderTargets()) {
- try {
- if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
- Log.i(TAG, "Producing jpeg buffer...");
-
- int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
- totalSize = (totalSize + 3) & ~0x3; // round up to nearest octonibble
- LegacyCameraDevice.setNextTimestamp(s, timestamp);
-
- if (USE_BLOB_FORMAT_OVERRIDE) {
- // Override to RGBA_8888 format.
- LegacyCameraDevice.setSurfaceFormat(s,
- LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
-
- int dimen = (int) Math.ceil(Math.sqrt(totalSize));
- dimen = (dimen + 0xf) & ~0xf; // round up to nearest multiple of 16
- LegacyCameraDevice.setSurfaceDimens(s, dimen, dimen);
- LegacyCameraDevice.produceFrame(s, data, dimen, dimen,
- CameraMetadataNative.NATIVE_JPEG_FORMAT);
- } else {
- LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1);
- LegacyCameraDevice.produceFrame(s, data, totalSize, /*height*/1,
- CameraMetadataNative.NATIVE_JPEG_FORMAT);
- }
- }
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.w(TAG, "Surface abandoned, dropping frame. ", e);
- }
- }
-
- mReceivedJpeg.open();
- }
- };
-
- private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
- @Override
- public void onShutter() {
- mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos());
- }
- };
-
- private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
- new SurfaceTexture.OnFrameAvailableListener() {
- @Override
- public void onFrameAvailable(SurfaceTexture surfaceTexture) {
- if (DEBUG) {
- mPrevCounter.countAndLog();
- }
- mGLThreadManager.queueNewFrame();
- }
- };
-
- private void stopPreview() {
- if (VERBOSE) {
- Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
- }
- if (mPreviewRunning) {
- mCamera.stopPreview();
- mPreviewRunning = false;
- }
- }
-
- private void startPreview() {
- if (VERBOSE) {
- Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
- }
- if (!mPreviewRunning) {
- // XX: CameraClient:;startPreview is not getting called after a stop
- mCamera.startPreview();
- mPreviewRunning = true;
- }
- }
-
- private void doJpegCapturePrepare(RequestHolder request) throws IOException {
- if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
-
- if (!mPreviewRunning) {
- if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
-
- createDummySurface();
- mCamera.setPreviewTexture(mDummyTexture);
- startPreview();
- }
- }
-
- private void doJpegCapture(RequestHolder request) {
- if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
-
- mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
- mPreviewRunning = false;
- }
-
- private void doPreviewCapture(RequestHolder request) throws IOException {
- if (VERBOSE) {
- Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
- }
-
- if (mPreviewRunning) {
- return; // Already running
- }
-
- if (mPreviewTexture == null) {
- throw new IllegalStateException(
- "Preview capture called with no preview surfaces configured.");
- }
-
- mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
- mIntermediateBufferSize.getHeight());
- mCamera.setPreviewTexture(mPreviewTexture);
-
- startPreview();
- }
-
- private void disconnectCallbackSurfaces() {
- for (Surface s : mCallbackOutputs) {
- try {
- LegacyCameraDevice.disconnectSurface(s);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.d(TAG, "Surface abandoned, skipping...", e);
- }
- }
- }
-
- private void configureOutputs(Collection<Pair<Surface, Size>> outputs) {
- if (DEBUG) {
- String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
- Log.d(TAG, "configureOutputs with " + outputsStr);
- }
-
- try {
- stopPreview();
- } catch (RuntimeException e) {
- Log.e(TAG, "Received device exception in configure call: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- return;
- }
-
- /*
- * Try to release the previous preview's surface texture earlier if we end up
- * using a different one; this also reduces the likelihood of getting into a deadlock
- * when disconnecting from the old previous texture at a later time.
- */
- try {
- mCamera.setPreviewTexture(/*surfaceTexture*/null);
- } catch (IOException e) {
- Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
- } catch (RuntimeException e) {
- Log.e(TAG, "Received device exception in configure call: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- return;
- }
-
- if (mGLThreadManager != null) {
- mGLThreadManager.waitUntilStarted();
- mGLThreadManager.ignoreNewFrames();
- mGLThreadManager.waitUntilIdle();
- }
- resetJpegSurfaceFormats(mCallbackOutputs);
- disconnectCallbackSurfaces();
-
- mPreviewOutputs.clear();
- mCallbackOutputs.clear();
- mJpegSurfaceIds.clear();
- mPreviewTexture = null;
-
- List<Size> previewOutputSizes = new ArrayList<>();
- List<Size> callbackOutputSizes = new ArrayList<>();
-
- int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
- int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- if (outputs != null) {
- for (Pair<Surface, Size> outPair : outputs) {
- Surface s = outPair.first;
- Size outSize = outPair.second;
- try {
- int format = LegacyCameraDevice.detectSurfaceType(s);
- LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
- switch (format) {
- case CameraMetadataNative.NATIVE_JPEG_FORMAT:
- if (USE_BLOB_FORMAT_OVERRIDE) {
- // Override to RGBA_8888 format.
- LegacyCameraDevice.setSurfaceFormat(s,
- LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
- }
- mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
- mCallbackOutputs.add(s);
- callbackOutputSizes.add(outSize);
-
- // LegacyCameraDevice is the producer of JPEG output surfaces
- // so LegacyCameraDevice needs to connect to the surfaces.
- LegacyCameraDevice.connectSurface(s);
- break;
- default:
- LegacyCameraDevice.setScalingMode(s, LegacyCameraDevice.
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- mPreviewOutputs.add(s);
- previewOutputSizes.add(outSize);
- break;
- }
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.w(TAG, "Surface abandoned, skipping...", e);
- }
- }
- }
- try {
- mParams = mCamera.getParameters();
- } catch (RuntimeException e) {
- Log.e(TAG, "Received device exception: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- return;
- }
-
- List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
- int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
- if (DEBUG) {
- Log.d(TAG, "doPreviewCapture - Selected range [" +
- bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
- bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
- }
- mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
- bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
-
- Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs,
- callbackOutputSizes, mParams);
-
- if (previewOutputSizes.size() > 0) {
-
- Size largestOutput = SizeAreaComparator.findLargestByArea(previewOutputSizes);
-
- // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
- Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
-
- Size chosenJpegDimen = (smallestSupportedJpegSize != null) ? smallestSupportedJpegSize
- : largestJpegDimen;
-
- List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
- mParams.getSupportedPreviewSizes());
-
- // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
- // of the configured output dimensions. If none exists, fall back to using the largest
- // supported preview size.
- long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
- Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
- for (Size s : supportedPreviewSizes) {
- long currArea = s.getWidth() * s.getHeight();
- long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
- if (checkAspectRatiosMatch(chosenJpegDimen, s) && (currArea < bestArea &&
- currArea >= largestOutputArea)) {
- bestPreviewDimen = s;
- }
- }
-
- mIntermediateBufferSize = bestPreviewDimen;
- mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
- mIntermediateBufferSize.getHeight());
-
- if (DEBUG) {
- Log.d(TAG, "Intermediate buffer selected with dimens: " +
- bestPreviewDimen.toString());
- }
- } else {
- mIntermediateBufferSize = null;
- if (DEBUG) {
- Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
- }
- }
-
- if (smallestSupportedJpegSize != null) {
- /*
- * Set takePicture size to the smallest supported JPEG size large enough
- * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
- */
-
- Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
- mParams.setPictureSize(
- smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
- }
-
- // TODO: Detect and optimize single-output paths here to skip stream teeing.
- if (mGLThreadManager == null) {
- mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
- mGLThreadManager.start();
- }
- mGLThreadManager.waitUntilStarted();
- List<Pair<Surface, Size>> previews = new ArrayList<>();
- Iterator<Size> previewSizeIter = previewOutputSizes.iterator();
- for (Surface p : mPreviewOutputs) {
- previews.add(new Pair<>(p, previewSizeIter.next()));
- }
- mGLThreadManager.setConfigurationAndWait(previews, mCaptureCollector);
-
- for (Surface p : mPreviewOutputs) {
- try {
- LegacyCameraDevice.setSurfaceOrientation(p, facing, orientation);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.e(TAG, "Surface abandoned, skipping setSurfaceOrientation()", e);
- }
- }
-
- mGLThreadManager.allowNewFrames();
- mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
- if (mPreviewTexture != null) {
- mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
- }
-
- try {
- mCamera.setParameters(mParams);
- } catch (RuntimeException e) {
- Log.e(TAG, "Received device exception while configuring: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
-
- }
- }
-
- private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
- if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
- return;
- }
- for(Surface s : surfaces) {
- if (s == null || !s.isValid()) {
- Log.w(TAG, "Jpeg surface is invalid, skipping...");
- continue;
- }
- try {
- LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.w(TAG, "Surface abandoned, skipping...", e);
- }
- }
- }
-
- /**
- * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
- * than all of the configured {@code JPEG} outputs (by both width and height).
- *
- * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
- * still satisfies the above constraint.</p>
- *
- * <p>As a result, the returned size is guaranteed to be usable without needing
- * to upscale any of the outputs. If only one {@code JPEG} surface is used,
- * then no scaling/cropping is necessary between the taken picture and
- * the {@code JPEG} output surface.</p>
- *
- * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
- * @param params api1 parameters (used for reading only)
- *
- * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
- * {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
- * surfaces.
- */
- private Size calculatePictureSize( List<Surface> callbackOutputs,
- List<Size> callbackSizes, Camera.Parameters params) {
- /*
- * Find the largest JPEG size (if any), from the configured outputs:
- * - the api1 picture size should be set to the smallest legal size that's at least as large
- * as the largest configured JPEG size
- */
- if (callbackOutputs.size() != callbackSizes.size()) {
- throw new IllegalStateException("Input collections must be same length");
- }
- List<Size> configuredJpegSizes = new ArrayList<>();
- Iterator<Size> sizeIterator = callbackSizes.iterator();
- for (Surface callbackSurface : callbackOutputs) {
- Size jpegSize = sizeIterator.next();
- if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
- continue; // Ignore non-JPEG callback formats
- }
-
- configuredJpegSizes.add(jpegSize);
- }
- if (!configuredJpegSizes.isEmpty()) {
- /*
- * Find the largest configured JPEG width, and height, independently
- * of the rest.
- *
- * The rest of the JPEG streams can be cropped out of this smallest bounding
- * rectangle.
- */
- int maxConfiguredJpegWidth = -1;
- int maxConfiguredJpegHeight = -1;
- for (Size jpegSize : configuredJpegSizes) {
- maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
- jpegSize.getWidth() : maxConfiguredJpegWidth;
- maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
- jpegSize.getHeight() : maxConfiguredJpegHeight;
- }
- Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
-
- List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
- params.getSupportedPictureSizes());
-
- /*
- * Find the smallest supported JPEG size that can fit the smallest bounding
- * rectangle for the configured JPEG sizes.
- */
- List<Size> candidateSupportedJpegSizes = new ArrayList<>();
- for (Size supportedJpegSize : supportedJpegSizes) {
- if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
- supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
- candidateSupportedJpegSizes.add(supportedJpegSize);
- }
- }
-
- if (candidateSupportedJpegSizes.isEmpty()) {
- throw new AssertionError(
- "Could not find any supported JPEG sizes large enough to fit " +
- smallestBoundJpegSize);
- }
-
- Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
- new SizeAreaComparator());
-
- if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
- Log.w(TAG,
- String.format(
- "configureOutputs - Will need to crop picture %s into "
- + "smallest bound size %s",
- smallestSupportedJpegSize, smallestBoundJpegSize));
- }
-
- return smallestSupportedJpegSize;
- }
-
- return null;
- }
-
- private static boolean checkAspectRatiosMatch(Size a, Size b) {
- float aAspect = a.getWidth() / (float) a.getHeight();
- float bAspect = b.getWidth() / (float) b.getHeight();
-
- return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
- }
-
- // Calculate the highest FPS range supported
- private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
- if (frameRates.size() == 0) {
- Log.e(TAG, "No supported frame rates returned!");
- return null;
- }
-
- int bestMin = 0;
- int bestMax = 0;
- int bestIndex = 0;
- int index = 0;
- for (int[] rate : frameRates) {
- int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
- int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
- if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
- bestMin = minFps;
- bestMax = maxFps;
- bestIndex = index;
- }
- index++;
- }
-
- return frameRates.get(bestIndex);
- }
-
- private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
- private boolean mCleanup = false;
- private final LegacyResultMapper mMapper = new LegacyResultMapper();
-
- @Override
- public boolean handleMessage(Message msg) {
- if (mCleanup) {
- return true;
- }
-
- if (DEBUG) {
- Log.d(TAG, "Request thread handling message:" + msg.what);
- }
- long startTime = 0;
- if (DEBUG) {
- startTime = SystemClock.elapsedRealtimeNanos();
- }
- switch (msg.what) {
- case MSG_CONFIGURE_OUTPUTS:
- ConfigureHolder config = (ConfigureHolder) msg.obj;
- int sizes = config.surfaces != null ? config.surfaces.size() : 0;
- Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
-
- try {
- boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
- TimeUnit.MILLISECONDS);
- if (!success) {
- Log.e(TAG, "Timed out while queueing configure request.");
- mCaptureCollector.failAll();
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted while waiting for requests to complete.");
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- break;
- }
-
- configureOutputs(config.surfaces);
- config.condition.open();
- if (DEBUG) {
- long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
- Log.d(TAG, "Configure took " + totalTime + " ns");
- }
- break;
- case MSG_SUBMIT_CAPTURE_REQUEST:
- Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
- boolean anyRequestOutputAbandoned = false;
-
- // Get the next burst from the request queue.
- RequestQueue.RequestQueueEntry nextBurst = mRequestQueue.getNext();
-
- if (nextBurst == null) {
- // If there are no further requests queued, wait for any currently executing
- // requests to complete, then switch to idle state.
- try {
- boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
- TimeUnit.MILLISECONDS);
- if (!success) {
- Log.e(TAG,
- "Timed out while waiting for prior requests to complete.");
- mCaptureCollector.failAll();
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- break;
- }
-
- synchronized (mIdleLock) {
- // Retry the the request queue.
- nextBurst = mRequestQueue.getNext();
-
- // If we still have no queued requests, go idle.
- if (nextBurst == null) {
- mDeviceState.setIdle();
- break;
- }
- }
- }
-
- if (nextBurst != null) {
- // Queue another capture if we did not get the last burst.
- handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
-
- // Check whether capture queue becomes empty
- if (nextBurst.isQueueEmpty()) {
- mDeviceState.setRequestQueueEmpty();
- }
- }
-
- // Complete each request in the burst
- BurstHolder burstHolder = nextBurst.getBurstHolder();
- List<RequestHolder> requests =
- burstHolder.produceRequestHolders(nextBurst.getFrameNumber());
- for (RequestHolder holder : requests) {
- CaptureRequest request = holder.getRequest();
-
- boolean paramsChanged = false;
-
- // Only update parameters if the request has changed
- if (mLastRequest == null || mLastRequest.captureRequest != request) {
-
- // The intermediate buffer is sometimes null, but we always need
- // the Camera1 API configured preview size
- Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
-
- LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
- request, previewSize, mParams); // params are copied
-
-
- // Parameters are mutated as a side-effect
- LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
-
- // If the parameters have changed, set them in the Camera1 API.
- if (!mParams.same(legacyRequest.parameters)) {
- try {
- mCamera.setParameters(legacyRequest.parameters);
- } catch (RuntimeException e) {
- // If setting the parameters failed, report a request error to
- // the camera client, and skip any further work for this request
- Log.e(TAG, "Exception while setting camera parameters: ", e);
- holder.failRequest();
- mDeviceState.setCaptureStart(holder, /*timestamp*/0,
- CameraDeviceImpl.CameraDeviceCallbacks.
- ERROR_CAMERA_REQUEST);
- continue;
- }
- paramsChanged = true;
- mParams = legacyRequest.parameters;
- }
-
- mLastRequest = legacyRequest;
- }
-
- try {
- boolean success = mCaptureCollector.queueRequest(holder,
- mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
-
- if (!success) {
- // Report a request error if we timed out while queuing this.
- Log.e(TAG, "Timed out while queueing capture request.");
- holder.failRequest();
- mDeviceState.setCaptureStart(holder, /*timestamp*/0,
- CameraDeviceImpl.CameraDeviceCallbacks.
- ERROR_CAMERA_REQUEST);
- continue;
- }
-
- // Starting the preview needs to happen before enabling
- // face detection or auto focus
- if (holder.hasPreviewTargets()) {
- doPreviewCapture(holder);
- }
- if (holder.hasJpegTargets()) {
- while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
- TimeUnit.MILLISECONDS)) {
- // Fail preview requests until the queue is empty.
- Log.e(TAG, "Timed out while waiting for preview requests to " +
- "complete.");
- mCaptureCollector.failNextPreview();
- }
- mReceivedJpeg.close();
- doJpegCapturePrepare(holder);
- }
-
- /*
- * Do all the actions that require a preview to have been started
- */
-
- // Toggle face detection on/off
- // - do this before AF to give AF a chance to use faces
- mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
-
- // Unconditionally process AF triggers, since they're non-idempotent
- // - must be done after setting the most-up-to-date AF mode
- mFocusStateMapper.processRequestTriggers(request, mParams);
-
- if (holder.hasJpegTargets()) {
- doJpegCapture(holder);
- if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
- Log.e(TAG, "Hit timeout for jpeg callback!");
- mCaptureCollector.failNextJpeg();
- }
- }
-
- } catch (IOException e) {
- Log.e(TAG, "Received device exception during capture call: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- break;
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted during capture: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- break;
- } catch (RuntimeException e) {
- Log.e(TAG, "Received device exception during capture call: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- break;
- }
-
- if (paramsChanged) {
- if (DEBUG) {
- Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
- }
- try {
- mParams = mCamera.getParameters();
- } catch (RuntimeException e) {
- Log.e(TAG, "Received device exception: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- break;
- }
-
- // Update parameters to the latest that we think the camera is using
- mLastRequest.setParameters(mParams);
- }
-
- MutableLong timestampMutable = new MutableLong(/*value*/0L);
- try {
- boolean success = mCaptureCollector.waitForRequestCompleted(holder,
- REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
- /*out*/timestampMutable);
-
- if (!success) {
- Log.e(TAG, "Timed out while waiting for request to complete.");
- mCaptureCollector.failAll();
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted waiting for request completion: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- break;
- }
-
- CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
- mLastRequest, timestampMutable.value);
- /*
- * Order matters: The default result mapper is state-less; the
- * other mappers carry state and may override keys set by the default
- * mapper with their own values.
- */
-
- // Update AF state
- mFocusStateMapper.mapResultTriggers(result);
- // Update face-related results
- mFaceDetectMapper.mapResultFaces(result, mLastRequest);
-
- if (!holder.requestFailed()) {
- mDeviceState.setCaptureResult(holder, result);
- }
-
- if (holder.isOutputAbandoned()) {
- anyRequestOutputAbandoned = true;
- }
- }
-
- // Stop the repeating request if any of its output surfaces is abandoned.
- if (anyRequestOutputAbandoned && burstHolder.isRepeating()) {
- long lastFrameNumber = cancelRepeating(burstHolder.getRequestId());
- if (DEBUG) {
- Log.d(TAG, "Stopped repeating request. Last frame number is " +
- lastFrameNumber);
- }
- if (lastFrameNumber != RequestQueue.INVALID_FRAME) {
- mDeviceState.setRepeatingRequestError(lastFrameNumber,
- burstHolder.getRequestId());
- } else {
- Log.e(TAG, "Repeating request id: " + burstHolder.getRequestId() +
- " already canceled!");
- }
- }
-
- if (DEBUG) {
- long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
- Log.d(TAG, "Capture request took " + totalTime + " ns");
- mRequestCounter.countAndLog();
- }
- break;
- case MSG_CLEANUP:
- mCleanup = true;
- try {
- boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
- TimeUnit.MILLISECONDS);
- if (!success) {
- Log.e(TAG, "Timed out while queueing cleanup request.");
- mCaptureCollector.failAll();
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
- mDeviceState.setError(
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
- }
- if (mGLThreadManager != null) {
- mGLThreadManager.quit();
- mGLThreadManager = null;
- }
- disconnectCallbackSurfaces();
- if (mCamera != null) {
- mCamera.release();
- mCamera = null;
- }
- break;
- case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
- // OK: Ignore message.
- break;
- default:
- throw new AssertionError("Unhandled message " + msg.what +
- " on RequestThread.");
- }
- return true;
- }
- };
-
- /**
- * Create a new RequestThreadManager.
- *
- * @param cameraId the id of the camera to use.
- * @param camera an open camera object. The RequestThreadManager takes ownership of this camera
- * object, and is responsible for closing it.
- * @param characteristics the static camera characteristics corresponding to this camera device
- * @param deviceState a {@link CameraDeviceState} state machine.
- */
- public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
- CameraDeviceState deviceState) {
- mCamera = checkNotNull(camera, "camera must not be null");
- mCameraId = cameraId;
- mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
- String name = String.format("RequestThread-%d", cameraId);
- TAG = name;
- mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
- mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
- mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
- mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
- mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
- mCamera.setDetailedErrorCallback(mErrorCallback);
- }
-
- /**
- * Start the request thread.
- */
- public void start() {
- mRequestThread.start();
- }
-
- /**
- * Flush any pending requests.
- *
- * @return the last frame number.
- */
- public long flush() {
- Log.i(TAG, "Flushing all pending requests.");
- long lastFrame = mRequestQueue.stopRepeating();
- mCaptureCollector.failAll();
- return lastFrame;
- }
-
- /**
- * Quit the request thread, and clean up everything.
- */
- public void quit() {
- if (!mQuit.getAndSet(true)) { // Avoid sending messages on dead thread's handler.
- Handler handler = mRequestThread.waitAndGetHandler();
- handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
- mRequestThread.quitSafely();
- try {
- mRequestThread.join();
- } catch (InterruptedException e) {
- Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
- mRequestThread.getName(), mRequestThread.getId()));
- }
- }
- }
-
- /**
- * Submit the given burst of requests to be captured.
- *
- * <p>If the burst is repeating, replace the current repeating burst.</p>
- *
- * @param requests the burst of requests to add to the queue.
- * @param repeating true if the burst is repeating.
- * @return the submission info, including the new request id, and the last frame number, which
- * contains either the frame number of the last frame that will be returned for this request,
- * or the frame number of the last frame that will be returned for the current repeating
- * request if this burst is set to be repeating.
- */
- public SubmitInfo submitCaptureRequests(CaptureRequest[] requests, boolean repeating) {
- Handler handler = mRequestThread.waitAndGetHandler();
- SubmitInfo info;
- synchronized (mIdleLock) {
- info = mRequestQueue.submit(requests, repeating);
- handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
- }
- return info;
- }
-
- /**
- * Cancel a repeating request.
- *
- * @param requestId the id of the repeating request to cancel.
- * @return the last frame to be returned from the HAL for the given repeating request, or
- * {@code INVALID_FRAME} if none exists.
- */
- public long cancelRepeating(int requestId) {
- return mRequestQueue.stopRepeating(requestId);
- }
-
- /**
- * Configure with the current list of output Surfaces.
- *
- * <p>
- * This operation blocks until the configuration is complete.
- * </p>
- *
- * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
- *
- * @param outputs a {@link java.util.Collection} of outputs to configure.
- */
- public void configure(Collection<Pair<Surface, Size>> outputs) {
- Handler handler = mRequestThread.waitAndGetHandler();
- final ConditionVariable condition = new ConditionVariable(/*closed*/false);
- ConfigureHolder holder = new ConfigureHolder(condition, outputs);
- handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
- condition.block();
- }
-
- public void setAudioRestriction(int mode) {
- if (mCamera != null) {
- mCamera.setAudioRestriction(mode);
- }
- throw new IllegalStateException("Camera has been released!");
- }
-
- public int getAudioRestriction() {
- if (mCamera != null) {
- return mCamera.getAudioRestriction();
- }
- throw new IllegalStateException("Camera has been released!");
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/SizeAreaComparator.java b/core/java/android/hardware/camera2/legacy/SizeAreaComparator.java
deleted file mode 100644
index 75a5bab94867..000000000000
--- a/core/java/android/hardware/camera2/legacy/SizeAreaComparator.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.hardware.camera2.legacy;
-
-import android.hardware.Camera;
-
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Comparator for api1 {@link Camera.Size} objects by the area.
- *
- * <p>This comparator totally orders by rectangle area. Tie-breaks on width.</p>
- */
-@SuppressWarnings("deprecation")
-public class SizeAreaComparator implements Comparator<Camera.Size> {
- /**
- * {@inheritDoc}
- */
- @Override
- public int compare(Camera.Size size, Camera.Size size2) {
- checkNotNull(size, "size must not be null");
- checkNotNull(size2, "size2 must not be null");
-
- if (size.equals(size2)) {
- return 0;
- }
-
- long width = size.width;
- long width2 = size2.width;
- long area = width * size.height;
- long area2 = width2 * size2.height;
-
- if (area == area2) {
- return (width > width2) ? 1 : -1;
- }
-
- return (area > area2) ? 1 : -1;
- }
-
- /**
- * Get the largest api1 {@code Camera.Size} from the list by comparing each size's area
- * by each other using {@link SizeAreaComparator}.
- *
- * @param sizes a non-{@code null} list of non-{@code null} sizes
- * @return a non-{@code null} size
- *
- * @throws NullPointerException if {@code sizes} or any elements in it were {@code null}
- */
- public static Camera.Size findLargestByArea(List<Camera.Size> sizes) {
- checkNotNull(sizes, "sizes must not be null");
-
- return Collections.max(sizes, new SizeAreaComparator());
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
deleted file mode 100644
index a4c65aeb1050..000000000000
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.legacy;
-
-import android.graphics.ImageFormat;
-import android.graphics.RectF;
-import android.graphics.SurfaceTexture;
-import android.hardware.camera2.CameraCharacteristics;
-import android.os.Environment;
-import android.opengl.EGL14;
-import android.opengl.EGLConfig;
-import android.opengl.EGLContext;
-import android.opengl.EGLDisplay;
-import android.opengl.EGLSurface;
-import android.opengl.GLES11Ext;
-import android.opengl.GLES20;
-import android.opengl.Matrix;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Size;
-import android.view.Surface;
-import android.os.SystemProperties;
-
-import java.io.File;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * A renderer class that manages the GL state, and can draw a frame into a set of output
- * {@link Surface}s.
- */
-public class SurfaceTextureRenderer {
- private static final String TAG = SurfaceTextureRenderer.class.getSimpleName();
- private static final boolean DEBUG = false;
- private static final int EGL_RECORDABLE_ANDROID = 0x3142; // from EGL/eglext.h
- private static final int GL_MATRIX_SIZE = 16;
- private static final int VERTEX_POS_SIZE = 3;
- private static final int VERTEX_UV_SIZE = 2;
- private static final int EGL_COLOR_BITLENGTH = 8;
- private static final int GLES_VERSION = 2;
- private static final int PBUFFER_PIXEL_BYTES = 4;
-
- private static final int FLIP_TYPE_NONE = 0;
- private static final int FLIP_TYPE_HORIZONTAL = 1;
- private static final int FLIP_TYPE_VERTICAL = 2;
- private static final int FLIP_TYPE_BOTH = FLIP_TYPE_HORIZONTAL | FLIP_TYPE_VERTICAL;
-
- private static final DateTimeFormatter LOG_NAME_TIME_FORMATTER =
- DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss", Locale.ROOT);
-
- private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
- private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
- private EGLConfig mConfigs;
-
- private class EGLSurfaceHolder {
- Surface surface;
- EGLSurface eglSurface;
- int width;
- int height;
- }
-
- private List<EGLSurfaceHolder> mSurfaces = new ArrayList<EGLSurfaceHolder>();
- private List<EGLSurfaceHolder> mConversionSurfaces = new ArrayList<EGLSurfaceHolder>();
-
- private ByteBuffer mPBufferPixels;
-
- // Hold this to avoid GC
- private volatile SurfaceTexture mSurfaceTexture;
-
- private static final int FLOAT_SIZE_BYTES = 4;
- private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
- private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
- private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
-
- // Sampling is mirrored across the horizontal axis
- private static final float[] sHorizontalFlipTriangleVertices = {
- // X, Y, Z, U, V
- -1.0f, -1.0f, 0, 1.f, 0.f,
- 1.0f, -1.0f, 0, 0.f, 0.f,
- -1.0f, 1.0f, 0, 1.f, 1.f,
- 1.0f, 1.0f, 0, 0.f, 1.f,
- };
-
- // Sampling is mirrored across the vertical axis
- private static final float[] sVerticalFlipTriangleVertices = {
- // X, Y, Z, U, V
- -1.0f, -1.0f, 0, 0.f, 1.f,
- 1.0f, -1.0f, 0, 1.f, 1.f,
- -1.0f, 1.0f, 0, 0.f, 0.f,
- 1.0f, 1.0f, 0, 1.f, 0.f,
- };
-
- // Sampling is mirrored across the both axes
- private static final float[] sBothFlipTriangleVertices = {
- // X, Y, Z, U, V
- -1.0f, -1.0f, 0, 1.f, 1.f,
- 1.0f, -1.0f, 0, 0.f, 1.f,
- -1.0f, 1.0f, 0, 1.f, 0.f,
- 1.0f, 1.0f, 0, 0.f, 0.f,
- };
-
- // Sampling is 1:1 for a straight copy for the back camera
- private static final float[] sRegularTriangleVertices = {
- // X, Y, Z, U, V
- -1.0f, -1.0f, 0, 0.f, 0.f,
- 1.0f, -1.0f, 0, 1.f, 0.f,
- -1.0f, 1.0f, 0, 0.f, 1.f,
- 1.0f, 1.0f, 0, 1.f, 1.f,
- };
-
- private FloatBuffer mRegularTriangleVertices;
- private FloatBuffer mHorizontalFlipTriangleVertices;
- private FloatBuffer mVerticalFlipTriangleVertices;
- private FloatBuffer mBothFlipTriangleVertices;
- private final int mFacing;
-
- /**
- * As used in this file, this vertex shader maps a unit square to the view, and
- * tells the fragment shader to interpolate over it. Each surface pixel position
- * is mapped to a 2D homogeneous texture coordinate of the form (s, t, 0, 1) with
- * s and t in the inclusive range [0, 1], and the matrix from
- * {@link SurfaceTexture#getTransformMatrix(float[])} is used to map this
- * coordinate to a texture location.
- */
- private static final String VERTEX_SHADER =
- "uniform mat4 uMVPMatrix;\n" +
- "uniform mat4 uSTMatrix;\n" +
- "attribute vec4 aPosition;\n" +
- "attribute vec4 aTextureCoord;\n" +
- "varying vec2 vTextureCoord;\n" +
- "void main() {\n" +
- " gl_Position = uMVPMatrix * aPosition;\n" +
- " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
- "}\n";
-
- /**
- * This fragment shader simply draws the color in the 2D texture at
- * the location from the {@code VERTEX_SHADER}.
- */
- private static final String FRAGMENT_SHADER =
- "#extension GL_OES_EGL_image_external : require\n" +
- "precision mediump float;\n" +
- "varying vec2 vTextureCoord;\n" +
- "uniform samplerExternalOES sTexture;\n" +
- "void main() {\n" +
- " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
- "}\n";
-
- private float[] mMVPMatrix = new float[GL_MATRIX_SIZE];
- private float[] mSTMatrix = new float[GL_MATRIX_SIZE];
-
- private int mProgram;
- private int mTextureID = 0;
- private int muMVPMatrixHandle;
- private int muSTMatrixHandle;
- private int maPositionHandle;
- private int maTextureHandle;
-
- private PerfMeasurement mPerfMeasurer = null;
- private static final String LEGACY_PERF_PROPERTY = "persist.camera.legacy_perf";
-
- public SurfaceTextureRenderer(int facing) {
- mFacing = facing;
-
- mRegularTriangleVertices = ByteBuffer.allocateDirect(sRegularTriangleVertices.length *
- FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
- mRegularTriangleVertices.put(sRegularTriangleVertices).position(0);
-
- mHorizontalFlipTriangleVertices = ByteBuffer.allocateDirect(
- sHorizontalFlipTriangleVertices.length * FLOAT_SIZE_BYTES).
- order(ByteOrder.nativeOrder()).asFloatBuffer();
- mHorizontalFlipTriangleVertices.put(sHorizontalFlipTriangleVertices).position(0);
-
- mVerticalFlipTriangleVertices = ByteBuffer.allocateDirect(
- sVerticalFlipTriangleVertices.length * FLOAT_SIZE_BYTES).
- order(ByteOrder.nativeOrder()).asFloatBuffer();
- mVerticalFlipTriangleVertices.put(sVerticalFlipTriangleVertices).position(0);
-
- mBothFlipTriangleVertices = ByteBuffer.allocateDirect(
- sBothFlipTriangleVertices.length * FLOAT_SIZE_BYTES).
- order(ByteOrder.nativeOrder()).asFloatBuffer();
- mBothFlipTriangleVertices.put(sBothFlipTriangleVertices).position(0);
-
- Matrix.setIdentityM(mSTMatrix, 0);
- }
-
- private int loadShader(int shaderType, String source) {
- int shader = GLES20.glCreateShader(shaderType);
- checkGlError("glCreateShader type=" + shaderType);
- GLES20.glShaderSource(shader, source);
- GLES20.glCompileShader(shader);
- int[] compiled = new int[1];
- GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
- if (compiled[0] == 0) {
- Log.e(TAG, "Could not compile shader " + shaderType + ":");
- Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
- GLES20.glDeleteShader(shader);
- // TODO: handle this more gracefully
- throw new IllegalStateException("Could not compile shader " + shaderType);
- }
- return shader;
- }
-
- private int createProgram(String vertexSource, String fragmentSource) {
- int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
- if (vertexShader == 0) {
- return 0;
- }
- int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
- if (pixelShader == 0) {
- return 0;
- }
-
- int program = GLES20.glCreateProgram();
- checkGlError("glCreateProgram");
- if (program == 0) {
- Log.e(TAG, "Could not create program");
- }
- GLES20.glAttachShader(program, vertexShader);
- checkGlError("glAttachShader");
- GLES20.glAttachShader(program, pixelShader);
- checkGlError("glAttachShader");
- GLES20.glLinkProgram(program);
- int[] linkStatus = new int[1];
- GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
- if (linkStatus[0] != GLES20.GL_TRUE) {
- Log.e(TAG, "Could not link program: ");
- Log.e(TAG, GLES20.glGetProgramInfoLog(program));
- GLES20.glDeleteProgram(program);
- // TODO: handle this more gracefully
- throw new IllegalStateException("Could not link program");
- }
- return program;
- }
-
- private void drawFrame(SurfaceTexture st, int width, int height, int flipType)
- throws LegacyExceptionUtils.BufferQueueAbandonedException {
- checkGlError("onDrawFrame start");
- st.getTransformMatrix(mSTMatrix);
-
- Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);
-
- // Find intermediate buffer dimensions
- Size dimens;
- try {
- dimens = LegacyCameraDevice.getTextureSize(st);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- // Should never hit this.
- throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
- }
- float texWidth = dimens.getWidth();
- float texHeight = dimens.getHeight();
-
- if (texWidth <= 0 || texHeight <= 0) {
- throw new IllegalStateException("Illegal intermediate texture with dimension of 0");
- }
-
- // Letterbox or pillar-box output dimensions into intermediate dimensions.
- RectF intermediate = new RectF(/*left*/0, /*top*/0, /*right*/texWidth, /*bottom*/texHeight);
- RectF output = new RectF(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
- android.graphics.Matrix boxingXform = new android.graphics.Matrix();
- boxingXform.setRectToRect(output, intermediate, android.graphics.Matrix.ScaleToFit.CENTER);
- boxingXform.mapRect(output);
-
- // Find scaling factor from pillar-boxed/letter-boxed output dimensions to intermediate
- // buffer dimensions.
- float scaleX = intermediate.width() / output.width();
- float scaleY = intermediate.height() / output.height();
-
- // Intermediate texture is implicitly scaled to 'fill' the output dimensions in clip space
- // coordinates in the shader. To avoid stretching, we need to scale the larger dimension
- // of the intermediate buffer so that the output buffer is actually letter-boxed
- // or pillar-boxed into the intermediate buffer after clipping.
- Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleX, /*y*/scaleY, /*z*/1);
-
- if (DEBUG) {
- Log.d(TAG, "Scaling factors (S_x = " + scaleX + ",S_y = " + scaleY + ") used for " +
- width + "x" + height + " surface, intermediate buffer size is " + texWidth +
- "x" + texHeight);
- }
-
- // Set viewport to be output buffer dimensions
- GLES20.glViewport(0, 0, width, height);
-
- if (DEBUG) {
- GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
- GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
- }
-
- GLES20.glUseProgram(mProgram);
- checkGlError("glUseProgram");
-
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
-
- FloatBuffer triangleVertices;
- switch(flipType) {
- case FLIP_TYPE_HORIZONTAL:
- triangleVertices = mHorizontalFlipTriangleVertices;
- break;
- case FLIP_TYPE_VERTICAL:
- triangleVertices = mVerticalFlipTriangleVertices;
- break;
- case FLIP_TYPE_BOTH:
- triangleVertices = mBothFlipTriangleVertices;
- break;
- default:
- triangleVertices = mRegularTriangleVertices;
- break;
- }
-
- triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
- GLES20.glVertexAttribPointer(maPositionHandle, VERTEX_POS_SIZE, GLES20.GL_FLOAT,
- /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
- checkGlError("glVertexAttribPointer maPosition");
- GLES20.glEnableVertexAttribArray(maPositionHandle);
- checkGlError("glEnableVertexAttribArray maPositionHandle");
-
- triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
- GLES20.glVertexAttribPointer(maTextureHandle, VERTEX_UV_SIZE, GLES20.GL_FLOAT,
- /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
- checkGlError("glVertexAttribPointer maTextureHandle");
- GLES20.glEnableVertexAttribArray(maTextureHandle);
- checkGlError("glEnableVertexAttribArray maTextureHandle");
-
- GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix,
- /*offset*/ 0);
- GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix,
- /*offset*/ 0);
-
- GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*offset*/ 0, /*count*/ 4);
- checkGlDrawError("glDrawArrays");
- }
-
- /**
- * Initializes GL state. Call this after the EGL surface has been created and made current.
- */
- private void initializeGLState() {
- mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
- if (mProgram == 0) {
- throw new IllegalStateException("failed creating program");
- }
- maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
- checkGlError("glGetAttribLocation aPosition");
- if (maPositionHandle == -1) {
- throw new IllegalStateException("Could not get attrib location for aPosition");
- }
- maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
- checkGlError("glGetAttribLocation aTextureCoord");
- if (maTextureHandle == -1) {
- throw new IllegalStateException("Could not get attrib location for aTextureCoord");
- }
-
- muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
- checkGlError("glGetUniformLocation uMVPMatrix");
- if (muMVPMatrixHandle == -1) {
- throw new IllegalStateException("Could not get attrib location for uMVPMatrix");
- }
-
- muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
- checkGlError("glGetUniformLocation uSTMatrix");
- if (muSTMatrixHandle == -1) {
- throw new IllegalStateException("Could not get attrib location for uSTMatrix");
- }
-
- int[] textures = new int[1];
- GLES20.glGenTextures(/*n*/ 1, textures, /*offset*/ 0);
-
- mTextureID = textures[0];
- GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
- checkGlError("glBindTexture mTextureID");
-
- GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
- GLES20.GL_NEAREST);
- GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
- GLES20.GL_LINEAR);
- GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
- GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
- GLES20.GL_CLAMP_TO_EDGE);
- checkGlError("glTexParameter");
- }
-
- private int getTextureId() {
- return mTextureID;
- }
-
- private void clearState() {
- mSurfaces.clear();
- for (EGLSurfaceHolder holder : mConversionSurfaces) {
- try {
- LegacyCameraDevice.disconnectSurface(holder.surface);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.w(TAG, "Surface abandoned, skipping...", e);
- }
- }
- mConversionSurfaces.clear();
- mPBufferPixels = null;
- if (mSurfaceTexture != null) {
- mSurfaceTexture.release();
- }
- mSurfaceTexture = null;
- }
-
- private void configureEGLContext() {
- mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
- if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
- throw new IllegalStateException("No EGL14 display");
- }
- int[] version = new int[2];
- if (!EGL14.eglInitialize(mEGLDisplay, version, /*offset*/ 0, version, /*offset*/ 1)) {
- throw new IllegalStateException("Cannot initialize EGL14");
- }
-
- int[] attribList = {
- EGL14.EGL_RED_SIZE, EGL_COLOR_BITLENGTH,
- EGL14.EGL_GREEN_SIZE, EGL_COLOR_BITLENGTH,
- EGL14.EGL_BLUE_SIZE, EGL_COLOR_BITLENGTH,
- EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
- EGL_RECORDABLE_ANDROID, 1,
- EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT | EGL14.EGL_WINDOW_BIT,
- EGL14.EGL_NONE
- };
- EGLConfig[] configs = new EGLConfig[1];
- int[] numConfigs = new int[1];
- EGL14.eglChooseConfig(mEGLDisplay, attribList, /*offset*/ 0, configs, /*offset*/ 0,
- configs.length, numConfigs, /*offset*/ 0);
- checkEglError("eglCreateContext RGB888+recordable ES2");
- mConfigs = configs[0];
- int[] attrib_list = {
- EGL14.EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
- EGL14.EGL_NONE
- };
- mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
- attrib_list, /*offset*/ 0);
- checkEglError("eglCreateContext");
- if(mEGLContext == EGL14.EGL_NO_CONTEXT) {
- throw new IllegalStateException("No EGLContext could be made");
- }
- }
-
- private void configureEGLOutputSurfaces(Collection<EGLSurfaceHolder> surfaces) {
- if (surfaces == null || surfaces.size() == 0) {
- throw new IllegalStateException("No Surfaces were provided to draw to");
- }
- int[] surfaceAttribs = {
- EGL14.EGL_NONE
- };
- for (EGLSurfaceHolder holder : surfaces) {
- holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs,
- holder.surface, surfaceAttribs, /*offset*/ 0);
- checkEglError("eglCreateWindowSurface");
- }
- }
-
- private void configureEGLPbufferSurfaces(Collection<EGLSurfaceHolder> surfaces) {
- if (surfaces == null || surfaces.size() == 0) {
- throw new IllegalStateException("No Surfaces were provided to draw to");
- }
-
- int maxLength = 0;
- for (EGLSurfaceHolder holder : surfaces) {
- int length = holder.width * holder.height;
- // Find max surface size, ensure PBuffer can hold this many pixels
- maxLength = (length > maxLength) ? length : maxLength;
- int[] surfaceAttribs = {
- EGL14.EGL_WIDTH, holder.width,
- EGL14.EGL_HEIGHT, holder.height,
- EGL14.EGL_NONE
- };
- holder.eglSurface =
- EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0);
- checkEglError("eglCreatePbufferSurface");
- }
- mPBufferPixels = ByteBuffer.allocateDirect(maxLength * PBUFFER_PIXEL_BYTES)
- .order(ByteOrder.nativeOrder());
- }
-
- private void releaseEGLContext() {
- if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
- EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
- EGL14.EGL_NO_CONTEXT);
- dumpGlTiming();
- if (mSurfaces != null) {
- for (EGLSurfaceHolder holder : mSurfaces) {
- if (holder.eglSurface != null) {
- EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
- }
- }
- }
- if (mConversionSurfaces != null) {
- for (EGLSurfaceHolder holder : mConversionSurfaces) {
- if (holder.eglSurface != null) {
- EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
- }
- }
- }
- EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
- EGL14.eglReleaseThread();
- EGL14.eglTerminate(mEGLDisplay);
- }
-
- mConfigs = null;
- mEGLDisplay = EGL14.EGL_NO_DISPLAY;
- mEGLContext = EGL14.EGL_NO_CONTEXT;
- clearState();
- }
-
- private void makeCurrent(EGLSurface surface)
- throws LegacyExceptionUtils.BufferQueueAbandonedException {
- EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext);
- checkEglDrawError("makeCurrent");
- }
-
- private boolean swapBuffers(EGLSurface surface)
- throws LegacyExceptionUtils.BufferQueueAbandonedException {
- boolean result = EGL14.eglSwapBuffers(mEGLDisplay, surface);
-
- int error = EGL14.eglGetError();
- switch (error) {
- case EGL14.EGL_SUCCESS:
- return result;
-
- // Check for an abandoned buffer queue, or other error conditions out
- // of the user's control.
- //
- // From the EGL 1.4 spec (2013-12-04), Section 3.9.4 Posting Errors:
- //
- // If eglSwapBuffers is called and the native window associated with
- // surface is no longer valid, an EGL_BAD_NATIVE_WINDOW error is
- // generated.
- //
- // We also interpret EGL_BAD_SURFACE as indicating an abandoned
- // surface, even though the EGL spec does not document it as such, for
- // backwards compatibility with older versions of this file.
- case EGL14.EGL_BAD_NATIVE_WINDOW:
- case EGL14.EGL_BAD_SURFACE:
- throw new LegacyExceptionUtils.BufferQueueAbandonedException();
-
- default:
- throw new IllegalStateException(
- "swapBuffers: EGL error: 0x" + Integer.toHexString(error));
- }
- }
-
- private void checkEglDrawError(String msg)
- throws LegacyExceptionUtils.BufferQueueAbandonedException {
- int error;
- if ((error = EGL14.eglGetError()) == EGL14.EGL_BAD_NATIVE_WINDOW) {
- throw new LegacyExceptionUtils.BufferQueueAbandonedException();
- }
- if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
- throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error));
- }
- }
-
- private void checkEglError(String msg) {
- int error;
- if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
- throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error));
- }
- }
-
- private void checkGlError(String msg) {
- int error;
- while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
- throw new IllegalStateException(
- msg + ": GLES20 error: 0x" + Integer.toHexString(error));
- }
- }
-
- private void checkGlDrawError(String msg)
- throws LegacyExceptionUtils.BufferQueueAbandonedException {
- int error;
- boolean surfaceAbandoned = false;
- boolean glError = false;
- while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
- if (error == GLES20.GL_OUT_OF_MEMORY) {
- surfaceAbandoned = true;
- } else {
- glError = true;
- }
- }
- if (glError) {
- throw new IllegalStateException(
- msg + ": GLES20 error: 0x" + Integer.toHexString(error));
- }
- if (surfaceAbandoned) {
- throw new LegacyExceptionUtils.BufferQueueAbandonedException();
- }
- }
-
- /**
- * Save a measurement dump to disk, in
- * {@code /sdcard/CameraLegacy/durations_<time>_<width1>x<height1>_...txt}
- */
- private void dumpGlTiming() {
- if (mPerfMeasurer == null) return;
-
- File legacyStorageDir = new File(Environment.getExternalStorageDirectory(), "CameraLegacy");
- if (!legacyStorageDir.exists()){
- if (!legacyStorageDir.mkdirs()){
- Log.e(TAG, "Failed to create directory for data dump");
- return;
- }
- }
-
- StringBuilder path = new StringBuilder(legacyStorageDir.getPath());
- path.append(File.separator);
- path.append("durations_");
-
- path.append(formatTimestamp(System.currentTimeMillis()));
- path.append("_S");
- for (EGLSurfaceHolder surface : mSurfaces) {
- path.append(String.format("_%d_%d", surface.width, surface.height));
- }
- path.append("_C");
- for (EGLSurfaceHolder surface : mConversionSurfaces) {
- path.append(String.format("_%d_%d", surface.width, surface.height));
- }
- path.append(".txt");
- mPerfMeasurer.dumpPerformanceData(path.toString());
- }
-
- private static String formatTimestamp(long timeMillis) {
- // This is a replacement for {@link Time#format2445()} that doesn't suffer from Y2038
- // issues.
- Instant instant = Instant.ofEpochMilli(timeMillis);
- ZoneId zoneId = ZoneId.systemDefault();
- LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneId);
- return LOG_NAME_TIME_FORMATTER.format(localDateTime);
- }
-
- private void setupGlTiming() {
- if (PerfMeasurement.isGlTimingSupported()) {
- Log.d(TAG, "Enabling GL performance measurement");
- mPerfMeasurer = new PerfMeasurement();
- } else {
- Log.d(TAG, "GL performance measurement not supported on this device");
- mPerfMeasurer = null;
- }
- }
-
- private void beginGlTiming() {
- if (mPerfMeasurer == null) return;
- mPerfMeasurer.startTimer();
- }
-
- private void addGlTimestamp(long timestamp) {
- if (mPerfMeasurer == null) return;
- mPerfMeasurer.addTimestamp(timestamp);
- }
-
- private void endGlTiming() {
- if (mPerfMeasurer == null) return;
- mPerfMeasurer.stopTimer();
- }
-
- /**
- * Return the surface texture to draw to - this is the texture use to when producing output
- * surface buffers.
- *
- * @return a {@link SurfaceTexture}.
- */
- public SurfaceTexture getSurfaceTexture() {
- return mSurfaceTexture;
- }
-
- /**
- * Set a collection of output {@link Surface}s that can be drawn to.
- *
- * @param surfaces a {@link Collection} of surfaces.
- */
- public void configureSurfaces(Collection<Pair<Surface, Size>> surfaces) {
- releaseEGLContext();
-
- if (surfaces == null || surfaces.size() == 0) {
- Log.w(TAG, "No output surfaces configured for GL drawing.");
- return;
- }
-
- for (Pair<Surface, Size> p : surfaces) {
- Surface s = p.first;
- Size surfaceSize = p.second;
- // If pixel conversions aren't handled by egl, use a pbuffer
- try {
- EGLSurfaceHolder holder = new EGLSurfaceHolder();
- holder.surface = s;
- holder.width = surfaceSize.getWidth();
- holder.height = surfaceSize.getHeight();
- if (LegacyCameraDevice.needsConversion(s)) {
- mConversionSurfaces.add(holder);
- // LegacyCameraDevice is the producer of surfaces if it's not handled by EGL,
- // so LegacyCameraDevice needs to connect to the surfaces.
- LegacyCameraDevice.connectSurface(s);
- } else {
- mSurfaces.add(holder);
- }
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.w(TAG, "Surface abandoned, skipping configuration... ", e);
- }
- }
-
- // Set up egl display
- configureEGLContext();
-
- // Set up regular egl surfaces if needed
- if (mSurfaces.size() > 0) {
- configureEGLOutputSurfaces(mSurfaces);
- }
-
- // Set up pbuffer surface if needed
- if (mConversionSurfaces.size() > 0) {
- configureEGLPbufferSurfaces(mConversionSurfaces);
- }
-
- try {
- makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface :
- mConversionSurfaces.get(0).eglSurface);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.w(TAG, "Surface abandoned, skipping configuration... ", e);
- }
-
- initializeGLState();
- mSurfaceTexture = new SurfaceTexture(getTextureId());
-
- // Set up performance tracking if enabled
- if (SystemProperties.getBoolean(LEGACY_PERF_PROPERTY, false)) {
- setupGlTiming();
- }
- }
-
- /**
- * Draw the current buffer in the {@link SurfaceTexture} returned from
- * {@link #getSurfaceTexture()} into the set of target {@link Surface}s
- * in the next request from the given {@link CaptureCollector}, or drop
- * the frame if none is available.
- *
- * <p>
- * Any {@link Surface}s targeted must be a subset of the {@link Surface}s
- * set in the last {@link #configureSurfaces(java.util.Collection)} call.
- * </p>
- *
- * @param targetCollector the surfaces to draw to.
- */
- public void drawIntoSurfaces(CaptureCollector targetCollector) {
- if ((mSurfaces == null || mSurfaces.size() == 0)
- && (mConversionSurfaces == null || mConversionSurfaces.size() == 0)) {
- return;
- }
-
- boolean doTiming = targetCollector.hasPendingPreviewCaptures();
- checkGlError("before updateTexImage");
-
- if (doTiming) {
- beginGlTiming();
- }
-
- mSurfaceTexture.updateTexImage();
-
- long timestamp = mSurfaceTexture.getTimestamp();
-
- Pair<RequestHolder, Long> captureHolder = targetCollector.previewCaptured(timestamp);
-
- // No preview request queued, drop frame.
- if (captureHolder == null) {
- if (DEBUG) {
- Log.d(TAG, "Dropping preview frame.");
- }
- if (doTiming) {
- endGlTiming();
- }
- return;
- }
-
- RequestHolder request = captureHolder.first;
-
- Collection<Surface> targetSurfaces = request.getHolderTargets();
- if (doTiming) {
- addGlTimestamp(timestamp);
- }
-
- List<Long> targetSurfaceIds = new ArrayList();
- try {
- targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.w(TAG, "Surface abandoned, dropping frame. ", e);
- request.setOutputAbandoned();
- }
-
- for (EGLSurfaceHolder holder : mSurfaces) {
- if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
- try{
- LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
- holder.height);
- makeCurrent(holder.eglSurface);
-
- LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
- drawFrame(mSurfaceTexture, holder.width, holder.height,
- (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
- FLIP_TYPE_HORIZONTAL : FLIP_TYPE_NONE);
- swapBuffers(holder.eglSurface);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.w(TAG, "Surface abandoned, dropping frame. ", e);
- request.setOutputAbandoned();
- }
- }
- }
- for (EGLSurfaceHolder holder : mConversionSurfaces) {
- if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
- // glReadPixels reads from the bottom of the buffer, so add an extra vertical flip
- try {
- makeCurrent(holder.eglSurface);
- drawFrame(mSurfaceTexture, holder.width, holder.height,
- (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
- FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- // Should never hit this.
- throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
- }
- mPBufferPixels.clear();
- GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height,
- GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
- checkGlError("glReadPixels");
-
- try {
- int format = LegacyCameraDevice.detectSurfaceType(holder.surface);
- LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
- holder.height);
- LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
- LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(),
- holder.width, holder.height, format);
- } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
- Log.w(TAG, "Surface abandoned, dropping frame. ", e);
- request.setOutputAbandoned();
- }
- }
- }
- targetCollector.previewProduced();
-
- if (doTiming) {
- endGlTiming();
- }
- }
-
- /**
- * Clean up the current GL context.
- */
- public void cleanupEGLContext() {
- releaseEGLContext();
- }
-
- /**
- * Drop all current GL operations on the floor.
- */
- public void flush() {
- // TODO: implement flush
- Log.e(TAG, "Flush not yet implemented.");
- }
-}
diff --git a/core/java/android/hardware/camera2/legacy/package.html b/core/java/android/hardware/camera2/legacy/package.html
deleted file mode 100644
index db6f78bbf628..000000000000
--- a/core/java/android/hardware/camera2/legacy/package.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<body>
-{@hide}
-</body> \ No newline at end of file
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 20d9c30bb4cc..776d155e5b3e 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -685,6 +685,12 @@ public final class MandatoryStreamCombination {
"Standard still image capture"),
};
+ private static StreamCombinationTemplate sConcurrentDepthOnlyStreamCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.DEPTH16, SizeThreshold.VGA) },
+ "Depth capture for mesh based object rendering"),
+ };
+
/**
* Helper builder class to generate a list of available mandatory stream combinations.
* @hide
@@ -729,19 +735,21 @@ public final class MandatoryStreamCombination {
getAvailableMandatoryConcurrentStreamCombinations() {
// Since concurrent streaming support is optional, we mandate these stream
// combinations regardless of camera device capabilities.
+
+ StreamCombinationTemplate []chosenStreamCombinations = sConcurrentStreamCombinations;
if (!isColorOutputSupported()) {
- Log.v(TAG, "Device is not backward compatible!");
- throw new IllegalArgumentException("Camera device which is not BACKWARD_COMPATIBLE"
- + " cannot have mandatory concurrent streams");
+ Log.v(TAG, "Device is not backward compatible, depth streams are mandatory!");
+ chosenStreamCombinations = sConcurrentDepthOnlyStreamCombinations;
}
+ Size sizeVGAp = new Size(640, 480);
Size size720p = new Size(1280, 720);
Size size1440p = new Size(1920, 1440);
ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations =
new ArrayList<MandatoryStreamCombination>();
availableConcurrentStreamCombinations.ensureCapacity(
- sConcurrentStreamCombinations.length);
- for (StreamCombinationTemplate combTemplate : sConcurrentStreamCombinations) {
+ chosenStreamCombinations.length);
+ for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
ArrayList<MandatoryStreamInformation> streamsInfo =
new ArrayList<MandatoryStreamInformation>();
streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
@@ -753,6 +761,9 @@ public final class MandatoryStreamCombination {
case s1440p:
formatSize = size1440p;
break;
+ case VGA:
+ formatSize = sizeVGAp;
+ break;
default:
formatSize = size720p;
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index c37f9fe2465c..52251ba90b98 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -24,7 +24,6 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.legacy.LegacyCameraDevice;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.SurfaceUtils;
import android.util.Range;
@@ -69,6 +68,8 @@ public final class StreamConfigurationMap {
private static final String TAG = "StreamConfigurationMap";
+ private static final int MAX_DIMEN_FOR_ROUNDING = 1920; // maximum allowed width for rounding
+
/**
* Create a new {@link StreamConfigurationMap}.
*
@@ -568,7 +569,7 @@ public final class StreamConfigurationMap {
if (config.getSize().equals(surfaceSize)) {
return true;
} else if (isFlexible &&
- (config.getSize().getWidth() <= LegacyCameraDevice.MAX_DIMEN_FOR_ROUNDING)) {
+ (config.getSize().getWidth() <= MAX_DIMEN_FOR_ROUNDING)) {
return true;
}
}
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index abe1372ebde4..35b5c1599070 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -16,10 +16,14 @@
package android.hardware.camera2.utils;
+import static android.system.OsConstants.EINVAL;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.ImageFormat;
-import android.hardware.camera2.legacy.LegacyCameraDevice;
-import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.util.Range;
import android.util.Size;
@@ -35,6 +39,15 @@ import java.util.List;
*/
public class SurfaceUtils {
+ // Usage flags not yet included in HardwareBuffer
+ private static final int USAGE_RENDERSCRIPT = 0x00100000;
+ private static final int USAGE_HW_COMPOSER = 0x00000800;
+
+ // Image formats not yet included in PixelFormat
+ private static final int BGRA_8888 = 0x5;
+
+ private static final int BAD_VALUE = -EINVAL;
+
/**
* Check if a surface is for preview consumer based on consumer end point Gralloc usage flags.
*
@@ -42,7 +55,17 @@ public class SurfaceUtils {
* @return true if the surface is for preview consumer, false otherwise.
*/
public static boolean isSurfaceForPreview(Surface surface) {
- return LegacyCameraDevice.isPreviewConsumer(surface);
+ checkNotNull(surface);
+ long usageFlags = nativeDetectSurfaceUsageFlags(surface);
+ long disallowedFlags = HardwareBuffer.USAGE_VIDEO_ENCODE | USAGE_RENDERSCRIPT
+ | HardwareBuffer.USAGE_CPU_READ_OFTEN;
+ long allowedFlags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | USAGE_HW_COMPOSER
+ | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT;
+ boolean previewConsumer = ((usageFlags & disallowedFlags) == 0
+ && (usageFlags & allowedFlags) != 0);
+ int surfaceFormat = getSurfaceFormat(surface);
+
+ return previewConsumer;
}
/**
@@ -53,7 +76,17 @@ public class SurfaceUtils {
* @return true if the surface is for hardware video encoder consumer, false otherwise.
*/
public static boolean isSurfaceForHwVideoEncoder(Surface surface) {
- return LegacyCameraDevice.isVideoEncoderConsumer(surface);
+ checkNotNull(surface);
+ long usageFlags = nativeDetectSurfaceUsageFlags(surface);
+ long disallowedFlags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | USAGE_HW_COMPOSER
+ | USAGE_RENDERSCRIPT | HardwareBuffer.USAGE_CPU_READ_OFTEN;
+ long allowedFlags = HardwareBuffer.USAGE_VIDEO_ENCODE;
+ boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0
+ && (usageFlags & allowedFlags) != 0);
+
+ int surfaceFormat = getSurfaceFormat(surface);
+
+ return videoEncoderConsumer;
}
/**
@@ -63,9 +96,10 @@ public class SurfaceUtils {
* @return the native object id of the surface, 0 if surface is not backed by a native object.
*/
public static long getSurfaceId(Surface surface) {
+ checkNotNull(surface);
try {
- return LegacyCameraDevice.getSurfaceId(surface);
- } catch (BufferQueueAbandonedException e) {
+ return nativeGetSurfaceId(surface);
+ } catch (IllegalArgumentException e) {
return 0;
}
}
@@ -80,11 +114,13 @@ public class SurfaceUtils {
*/
@UnsupportedAppUsage
public static Size getSurfaceSize(Surface surface) {
- try {
- return LegacyCameraDevice.getSurfaceSize(surface);
- } catch (BufferQueueAbandonedException e) {
- throw new IllegalArgumentException("Surface was abandoned", e);
- }
+ checkNotNull(surface);
+
+ int[] dimens = new int[2];
+ int errorFlag = nativeDetectSurfaceDimens(surface, /*out*/dimens);
+ if (errorFlag == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned");
+
+ return new Size(dimens[0], dimens[1]);
}
/**
@@ -96,11 +132,17 @@ public class SurfaceUtils {
* @throws IllegalArgumentException if the surface is already abandoned.
*/
public static int getSurfaceFormat(Surface surface) {
- try {
- return LegacyCameraDevice.detectSurfaceType(surface);
- } catch (BufferQueueAbandonedException e) {
- throw new IllegalArgumentException("Surface was abandoned", e);
+ checkNotNull(surface);
+ int surfaceType = nativeDetectSurfaceType(surface);
+ if (surfaceType == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned");
+
+ // TODO: remove this override since the default format should be
+ // ImageFormat.PRIVATE. b/9487482
+ if ((surfaceType >= PixelFormat.RGBA_8888
+ && surfaceType <= BGRA_8888)) {
+ surfaceType = ImageFormat.PRIVATE;
}
+ return surfaceType;
}
/**
@@ -112,11 +154,10 @@ public class SurfaceUtils {
* @throws IllegalArgumentException if the surface is already abandoned.
*/
public static int getSurfaceDataspace(Surface surface) {
- try {
- return LegacyCameraDevice.detectSurfaceDataspace(surface);
- } catch (BufferQueueAbandonedException e) {
- throw new IllegalArgumentException("Surface was abandoned", e);
- }
+ checkNotNull(surface);
+ int dataSpace = nativeDetectSurfaceDataspace(surface);
+ if (dataSpace == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned");
+ return dataSpace;
}
/**
@@ -125,9 +166,21 @@ public class SurfaceUtils {
*
*/
public static boolean isFlexibleConsumer(Surface output) {
- return LegacyCameraDevice.isFlexibleConsumer(output);
+ checkNotNull(output);
+ long usageFlags = nativeDetectSurfaceUsageFlags(output);
+
+ // Keep up to date with allowed consumer types in
+ // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+ long disallowedFlags = HardwareBuffer.USAGE_VIDEO_ENCODE | USAGE_RENDERSCRIPT;
+ long allowedFlags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
+ | HardwareBuffer.USAGE_CPU_READ_OFTEN
+ | USAGE_HW_COMPOSER;
+ boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0
+ && (usageFlags & allowedFlags) != 0);
+ return flexibleConsumer;
}
+
/**
* A high speed output surface can only be preview or hardware encoder surface.
*
@@ -209,4 +262,14 @@ public class SurfaceUtils {
}
}
+ private static native int nativeDetectSurfaceType(Surface surface);
+
+ private static native int nativeDetectSurfaceDataspace(Surface surface);
+
+ private static native long nativeDetectSurfaceUsageFlags(Surface surface);
+
+ private static native int nativeDetectSurfaceDimens(Surface surface,
+ /*out*/int[/*2*/] dimens);
+
+ private static native long nativeGetSurfaceId(Surface surface);
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index b64ce999ff82..885d137dd2fe 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,14 +41,17 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
-import android.util.Log;
import android.util.Slog;
import android.view.Surface;
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
+import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
/**
* A class that coordinates access to the face authentication hardware.
@@ -67,15 +70,19 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
private static final int MSG_REMOVED = 105;
private static final int MSG_GET_FEATURE_COMPLETED = 106;
private static final int MSG_SET_FEATURE_COMPLETED = 107;
+ private static final int MSG_CHALLENGE_GENERATED = 108;
+ private static final int MSG_FACE_DETECTED = 109;
- private IFaceService mService;
+ private final IFaceService mService;
private final Context mContext;
private IBinder mToken = new Binder();
private AuthenticationCallback mAuthenticationCallback;
+ private FaceDetectionCallback mFaceDetectionCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private SetFeatureCallback mSetFeatureCallback;
private GetFeatureCallback mGetFeatureCallback;
+ private GenerateChallengeCallback mGenerateChallengeCallback;
private CryptoObject mCryptoObject;
private Face mRemovalFace;
private Handler mHandler;
@@ -99,6 +106,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
@Override // binder call
+ public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
+ mHandler.obtainMessage(MSG_FACE_DETECTED, sensorId, userId, isStrongBiometric)
+ .sendToTarget();
+ }
+
+ @Override // binder call
public void onAuthenticationFailed() {
mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
}
@@ -126,6 +139,17 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
args.arg2 = value;
mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget();
}
+
+ @Override
+ public void onChallengeGenerated(long challenge) {
+ if (mGenerateChallengeCallback instanceof InternalGenerateChallengeCallback) {
+ // Perform this on system_server thread, since the application's thread is
+ // blocked waiting for the result
+ mGenerateChallengeCallback.onGenerateChallengeResult(challenge);
+ } else {
+ mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, challenge).sendToTarget();
+ }
+ }
};
/**
@@ -150,7 +174,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
*
* @param crypto object associated with the call or null if none required.
* @param cancel an object that can be used to cancel authentication
- * @param flags optional flags; should be 0
* @param callback an object to receive authentication events
* @param handler an optional handler to handle callback events
* @throws IllegalArgumentException if the crypto operation is not supported or is not backed
@@ -162,8 +185,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
- authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
+ @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
+ authenticate(crypto, cancel, callback, handler, mContext.getUserId());
}
/**
@@ -187,7 +210,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
*
* @param crypto object associated with the call or null if none required.
* @param cancel an object that can be used to cancel authentication
- * @param flags optional flags; should be 0
* @param callback an object to receive authentication events
* @param handler an optional handler to handle callback events
* @param userId userId to authenticate for
@@ -199,15 +221,14 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* @hide
*/
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler,
- int userId) {
+ @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
if (cancel != null) {
if (cancel.isCanceled()) {
- Log.w(TAG, "authentication already canceled");
+ Slog.w(TAG, "authentication already canceled");
return;
} else {
cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
@@ -222,9 +243,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
final long operationId = crypto != null ? crypto.getOpId() : 0;
Trace.beginSection("FaceManager#authenticate");
mService.authenticate(mToken, operationId, userId, mServiceReceiver,
- flags, mContext.getOpPackageName());
+ mContext.getOpPackageName());
} catch (RemoteException e) {
- Log.w(TAG, "Remote exception while authenticating: ", e);
+ Slog.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
// Though this may not be a hardware issue, it will cause apps to give up or
// try again later.
@@ -239,15 +260,43 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
/**
+ * Uses the face hardware to detect for the presence of a face, without giving details about
+ * accept/reject/lockout.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void detectFace(@NonNull CancellationSignal cancel,
+ @NonNull FaceDetectionCallback callback, int userId) {
+ if (mService == null) {
+ return;
+ }
+
+ if (cancel.isCanceled()) {
+ Slog.w(TAG, "Detection already cancelled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnFaceDetectionCancelListener());
+ }
+
+ mFaceDetectionCallback = callback;
+
+ try {
+ mService.detectFace(mToken, userId, mServiceReceiver, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Remote exception when requesting finger detect", e);
+ }
+ }
+
+ /**
* Defaults to {@link FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback,
* int[], Surface)} with {@code surface} set to null.
*
* @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[], Surface)
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void enroll(int userId, byte[] token, CancellationSignal cancel,
+ public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures) {
- enroll(userId, token, cancel, callback, disabledFeatures, null /* surface */);
+ enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures, null /* surface */);
}
/**
@@ -262,7 +311,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* @param token a unique token provided by a recent creation or verification of device
* credentials (e.g. pin, pattern or password).
* @param cancel an object that can be used to cancel enrollment
- * @param flags optional flags
* @param userId the user to whom this face will belong to
* @param callback an object to receive enrollment events
* @param surface optional camera preview surface for a single-camera device. Must be null if
@@ -270,7 +318,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void enroll(int userId, byte[] token, CancellationSignal cancel,
+ public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface surface) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
@@ -278,7 +326,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
if (cancel != null) {
if (cancel.isCanceled()) {
- Log.w(TAG, "enrollment already canceled");
+ Slog.w(TAG, "enrollment already canceled");
return;
} else {
cancel.setOnCancelListener(new OnEnrollCancelListener());
@@ -289,10 +337,10 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
try {
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enroll");
- mService.enroll(userId, mToken, token, mServiceReceiver,
+ mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver,
mContext.getOpPackageName(), disabledFeatures, surface);
} catch (RemoteException e) {
- Log.w(TAG, "Remote exception in enroll: ", e);
+ Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or
// try again later.
callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
@@ -314,15 +362,15 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* which point the object is no longer valid. The operation can be canceled by using the
* provided cancel object.
*
- * @param token a unique token provided by a recent creation or verification of device
- * credentials (e.g. pin, pattern or password).
+ * @param hardwareAuthToken a unique token provided by a recent creation or verification of
+ * device credentials (e.g. pin, pattern or password).
* @param cancel an object that can be used to cancel enrollment
* @param userId the user to whom this face will belong to
* @param callback an object to receive enrollment events
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void enrollRemotely(int userId, byte[] token, CancellationSignal cancel,
+ public void enrollRemotely(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
@@ -330,7 +378,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
if (cancel != null) {
if (cancel.isCanceled()) {
- Log.w(TAG, "enrollRemotely is already canceled.");
+ Slog.w(TAG, "enrollRemotely is already canceled.");
return;
} else {
cancel.setOnCancelListener(new OnEnrollCancelListener());
@@ -341,10 +389,10 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
try {
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enrollRemotely");
- mService.enrollRemotely(userId, mToken, token, mServiceReceiver,
+ mService.enrollRemotely(userId, mToken, hardwareAuthToken, mServiceReceiver,
mContext.getOpPackageName(), disabledFeatures);
} catch (RemoteException e) {
- Log.w(TAG, "Remote exception in enrollRemotely: ", e);
+ Slog.w(TAG, "Remote exception in enrollRemotely: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or
// try again later.
callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
@@ -357,53 +405,68 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
/**
- * Requests an auth token to tie sensitive operations to the confirmation of
- * existing device credentials (e.g. pin/pattern/password).
- *
+ * Same as {@link #generateChallenge(GenerateChallengeCallback)}, except blocks until the
+ * TEE/hardware operation is complete.
+ * @return challenge generated in the TEE/hardware
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public long generateChallenge() {
- long result = 0;
- if (mService != null) {
- try {
- result = mService.generateChallenge(mToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ public long generateChallengeBlocking() {
+ final AtomicReference<Long> result = new AtomicReference<>();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final GenerateChallengeCallback callback = new InternalGenerateChallengeCallback() {
+ @Override
+ public void onGenerateChallengeResult(long challenge) {
+ result.set(challenge);
+ latch.countDown();
}
+ };
+
+ generateChallenge(callback);
+
+ try {
+ latch.await(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Interrupted while generatingChallenge", e);
+ e.printStackTrace();
}
- return result;
+ return result.get();
}
/**
- * Invalidates the current auth token.
+ * Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a
+ * HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification.
+ * The HardwareAuthenticationToken can then be sent to the biometric HAL together with a
+ * request to perform sensitive operation(s) (for example enroll or setFeature), represented
+ * by the challenge. Doing this ensures that a the sensitive operation cannot be performed
+ * unless the user has entered confirmed PIN/Pattern/Password.
+ *
+ * @see com.android.server.locksettings.LockSettingsService
*
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public int revokeChallenge() {
- int result = 0;
+ public void generateChallenge(GenerateChallengeCallback callback) {
if (mService != null) {
try {
- result = mService.revokeChallenge(mToken);
+ mGenerateChallengeCallback = callback;
+ mService.generateChallenge(mToken, mServiceReceiver, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- return result;
}
/**
+ * Invalidates the current auth token.
+ *
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void setFeature(int userId, int feature, boolean enabled, byte[] token,
- SetFeatureCallback callback) {
+ public void revokeChallenge() {
if (mService != null) {
try {
- mSetFeatureCallback = callback;
- mService.setFeature(userId, feature, enabled, token, mServiceReceiver,
- mContext.getOpPackageName());
+ mService.revokeChallenge(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -414,11 +477,13 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void getFeature(int userId, int feature, GetFeatureCallback callback) {
+ public void setFeature(int userId, int feature, boolean enabled, byte[] hardwareAuthToken,
+ SetFeatureCallback callback) {
if (mService != null) {
try {
- mGetFeatureCallback = callback;
- mService.getFeature(userId, feature, mServiceReceiver, mContext.getOpPackageName());
+ mSetFeatureCallback = callback;
+ mService.setFeature(mToken, userId, feature, enabled, hardwareAuthToken,
+ mServiceReceiver, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -426,14 +491,15 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
/**
- * Pokes the the driver to have it start looking for faces again.
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void userActivity() {
+ public void getFeature(int userId, int feature, GetFeatureCallback callback) {
if (mService != null) {
try {
- mService.userActivity();
+ mGetFeatureCallback = callback;
+ mService.getFeature(mToken, userId, feature, mServiceReceiver,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -458,7 +524,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver,
mContext.getOpPackageName());
} catch (RemoteException e) {
- Log.w(TAG, "Remote exception in remove: ", e);
+ Slog.w(TAG, "Remote exception in remove: ", e);
if (callback != null) {
callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
@@ -546,12 +612,30 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
throw e.rethrowFromSystemServer();
}
} else {
- Log.w(TAG, "isFaceHardwareDetected(): Service not connected!");
+ Slog.w(TAG, "isFaceHardwareDetected(): Service not connected!");
}
return false;
}
/**
+ * Get statically configured sensor properties.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ @NonNull
+ public List<FaceSensorProperties> getSensorProperties() {
+ try {
+ if (mService == null || !mService.isHardwareDetected(mContext.getOpPackageName())) {
+ return new ArrayList<>();
+ }
+ return mService.getSensorProperties(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return new ArrayList<>();
+ }
+
+ /**
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
@@ -563,7 +647,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
new IBiometricServiceLockoutResetCallback.Stub() {
@Override
- public void onLockoutReset(IRemoteCallback serverCallback)
+ public void onLockoutReset(int sensorId, IRemoteCallback serverCallback)
throws RemoteException {
try {
final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
@@ -572,7 +656,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
wakeLock.acquire();
mHandler.post(() -> {
try {
- callback.onLockoutReset();
+ callback.onLockoutReset(sensorId);
} finally {
wakeLock.release();
}
@@ -581,12 +665,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
serverCallback.sendResult(null /* data */);
}
}
- });
+ }, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
- Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
+ Slog.w(TAG, "addLockoutResetCallback(): Service not connected!");
}
}
@@ -610,6 +694,18 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
}
+ private void cancelFaceDetect() {
+ if (mService == null) {
+ return;
+ }
+
+ try {
+ mService.cancelFaceDetect(mToken, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* @hide
*/
@@ -867,6 +963,13 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
/**
+ * @hide
+ */
+ public interface FaceDetectionCallback {
+ void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric);
+ }
+
+ /**
* Callback structure provided to {@link FaceManager#enroll(long,
* EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()}
* must provide an implementation of this to {@link FaceManager#enroll(long,
@@ -949,7 +1052,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* authentication
* again.
*/
- public void onLockoutReset() {
+ public void onLockoutReset(int sensorId) {
}
}
@@ -967,6 +1070,16 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
public abstract void onCompleted(boolean success, int feature, boolean value);
}
+ /**
+ * @hide
+ */
+ public abstract static class GenerateChallengeCallback {
+ public abstract void onGenerateChallengeResult(long challenge);
+ }
+
+ private abstract static class InternalGenerateChallengeCallback
+ extends GenerateChallengeCallback {}
+
private class OnEnrollCancelListener implements OnCancelListener {
@Override
public void onCancel() {
@@ -987,6 +1100,13 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
}
+ private class OnFaceDetectionCancelListener implements OnCancelListener {
+ @Override
+ public void onCancel() {
+ cancelFaceDetect();
+ }
+ }
+
private class MyHandler extends Handler {
private MyHandler(Context context) {
super(context.getMainLooper());
@@ -1030,8 +1150,15 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
(boolean) args.arg2 /* value */);
args.recycle();
break;
+ case MSG_CHALLENGE_GENERATED:
+ sendChallengeGenerated((long) msg.obj /* challenge */);
+ break;
+ case MSG_FACE_DETECTED:
+ sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+ (boolean) msg.obj /* isStrongBiometric */);
+ break;
default:
- Log.w(TAG, "Unknown message: " + msg.what);
+ Slog.w(TAG, "Unknown message: " + msg.what);
}
Trace.endSection();
}
@@ -1051,12 +1178,27 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
mGetFeatureCallback.onCompleted(success, feature, value);
}
+ private void sendChallengeGenerated(long challenge) {
+ if (mGenerateChallengeCallback == null) {
+ return;
+ }
+ mGenerateChallengeCallback.onGenerateChallengeResult(challenge);
+ }
+
+ private void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
+ if (mFaceDetectionCallback == null) {
+ Slog.e(TAG, "sendFaceDetected, callback null");
+ return;
+ }
+ mFaceDetectionCallback.onFaceDetected(sensorId, userId, isStrongBiometric);
+ }
+
private void sendRemovedResult(Face face, int remaining) {
if (mRemovalCallback == null) {
return;
}
if (face == null) {
- Log.e(TAG, "Received MSG_REMOVED, but face is null");
+ Slog.e(TAG, "Received MSG_REMOVED, but face is null");
return;
}
mRemovalCallback.onRemovalSucceeded(face, remaining);
diff --git a/core/java/android/hardware/face/FaceSensorProperties.aidl b/core/java/android/hardware/face/FaceSensorProperties.aidl
new file mode 100644
index 000000000000..d83ee4b17fa7
--- /dev/null
+++ b/core/java/android/hardware/face/FaceSensorProperties.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.face;
+
+parcelable FaceSensorProperties; \ No newline at end of file
diff --git a/core/java/android/hardware/face/FaceSensorProperties.java b/core/java/android/hardware/face/FaceSensorProperties.java
new file mode 100644
index 000000000000..e3b2fbb6c614
--- /dev/null
+++ b/core/java/android/hardware/face/FaceSensorProperties.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container for face sensor properties.
+ * @hide
+ */
+public class FaceSensorProperties implements Parcelable {
+
+ public final int sensorId;
+ public final boolean supportsFaceDetection;
+
+ /**
+ * Initializes SensorProperties with specified values
+ */
+ public FaceSensorProperties(int sensorId, boolean supportsFaceDetection) {
+ this.sensorId = sensorId;
+ this.supportsFaceDetection = supportsFaceDetection;
+ }
+
+ protected FaceSensorProperties(Parcel in) {
+ sensorId = in.readInt();
+ supportsFaceDetection = in.readBoolean();
+ }
+
+ public static final Creator<FaceSensorProperties> CREATOR =
+ new Creator<FaceSensorProperties>() {
+ @Override
+ public FaceSensorProperties createFromParcel(Parcel in) {
+ return new FaceSensorProperties(in);
+ }
+
+ @Override
+ public FaceSensorProperties[] newArray(int size) {
+ return new FaceSensorProperties[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(sensorId);
+ dest.writeBoolean(supportsFaceDetection);
+ }
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index f5ac047885ca..a9097d401349 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -19,6 +19,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
+import android.hardware.face.FaceSensorProperties;
import android.view.Surface;
/**
@@ -27,9 +28,16 @@ import android.view.Surface;
* @hide
*/
interface IFaceService {
+ // Retrieve static sensor properties for all face sensors
+ List<FaceSensorProperties> getSensorProperties(String opPackageName);
+
// Authenticate the given sessionId with a face
- void authenticate(IBinder token, long operationId, int userid,
- IFaceServiceReceiver receiver, int flags, String opPackageName);
+ void authenticate(IBinder token, long operationId, int userid, IFaceServiceReceiver receiver,
+ String opPackageName);
+
+ // Uses the face hardware to detect for the presence of a face, without giving details
+ // about accept/reject/lockout.
+ void detectFace(IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName);
// This method prepares the service to start authenticating, but doesn't start authentication.
// This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
@@ -46,16 +54,19 @@ interface IFaceService {
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
+ // Cancel face detection
+ void cancelFaceDetect(IBinder token, String opPackageName);
+
// Same as above, with extra arguments.
void cancelAuthenticationFromService(IBinder token, String opPackageName,
int callingUid, int callingPid, int callingUserId);
// Start face enrollment
- void enroll(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
+ void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
String opPackageName, in int [] disabledFeatures, in Surface surface);
// Start remote face enrollment
- void enrollRemotely(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
+ void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
String opPackageName, in int [] disabledFeatures);
// Cancel enrollment in progress
@@ -72,29 +83,31 @@ interface IFaceService {
boolean isHardwareDetected(String opPackageName);
// Get a pre-enrollment authentication token
- long generateChallenge(IBinder token);
+ void generateChallenge(IBinder token, IFaceServiceReceiver receiver, String opPackageName);
// Finish an enrollment sequence and invalidate the authentication token
- int revokeChallenge(IBinder token);
+ void revokeChallenge(IBinder token, String opPackageName);
// Determine if a user has at least one enrolled face
boolean hasEnrolledFaces(int userId, String opPackageName);
+ // Return the LockoutTracker status for the specified user
+ int getLockoutModeForUser(int userId);
+
// Gets the authenticator ID for face
long getAuthenticatorId(int callingUserId);
// Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
- void resetLockout(int userId, in byte [] token);
+ void resetLockout(int userId, in byte [] hardwareAuthToken);
// Add a callback which gets notified when the face lockout period expired.
- void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
-
- void setFeature(int userId, int feature, boolean enabled, in byte [] token,
- IFaceServiceReceiver receiver, String opPackageName);
+ void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
- void getFeature(int userId, int feature, IFaceServiceReceiver receiver, String opPackageName);
+ void setFeature(IBinder token, int userId, int feature, boolean enabled,
+ in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, String opPackageName);
- void userActivity();
+ void getFeature(IBinder token, int userId, int feature, IFaceServiceReceiver receiver,
+ String opPackageName);
// Give FaceService its ID. See AuthService.java
void initializeConfiguration(int sensorId);
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index ff3f6f39586e..2600b7def03a 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -25,9 +25,11 @@ oneway interface IFaceServiceReceiver {
void onEnrollResult(in Face face, int remaining);
void onAcquired(int acquiredInfo, int vendorCode);
void onAuthenticationSucceeded(in Face face, int userId, boolean isStrongBiometric);
+ void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric);
void onAuthenticationFailed();
void onError(int error, int vendorCode);
void onRemoved(in Face face, int remaining);
void onFeatureSet(boolean success, int feature);
void onFeatureGet(boolean success, int feature, boolean value);
+ void onChallengeGenerated(long challenge);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 5ff854233b08..e384da7574ad 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,7 @@ package android.hardware.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import android.annotation.NonNull;
@@ -49,8 +50,12 @@ import android.util.Slog;
import android.view.Surface;
import java.security.Signature;
+import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -62,6 +67,7 @@ import javax.crypto.Mac;
* it's much more realistic to have a system-provided authentication dialog since the method may
* vary by vendor/device.
*/
+@SuppressWarnings("deprecation")
@Deprecated
@SystemService(Context.FINGERPRINT_SERVICE)
@RequiresFeature(PackageManager.FEATURE_FINGERPRINT)
@@ -75,13 +81,17 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
private static final int MSG_AUTHENTICATION_FAILED = 103;
private static final int MSG_ERROR = 104;
private static final int MSG_REMOVED = 105;
+ private static final int MSG_CHALLENGE_GENERATED = 106;
+ private static final int MSG_FINGERPRINT_DETECTED = 107;
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
private AuthenticationCallback mAuthenticationCallback;
+ private FingerprintDetectionCallback mFingerprintDetectionCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
+ private GenerateChallengeCallback mGenerateChallengeCallback;
private CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
@@ -106,6 +116,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
+ private class OnFingerprintDetectionCancelListener implements OnCancelListener {
+ @Override
+ public void onCancel() {
+ cancelFingerprintDetect();
+ }
+ }
+
/**
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
@@ -216,7 +233,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
public boolean isStrongBiometric() {
return mIsStrongBiometric;
}
- };
+ }
/**
* Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject,
@@ -268,11 +285,23 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
*/
@Override
public void onAuthenticationAcquired(int acquireInfo) {}
- };
+ }
+
+ /**
+ * Callback structure provided for {@link #detectFingerprint(CancellationSignal,
+ * FingerprintDetectionCallback, int, Surface)}.
+ * @hide
+ */
+ public interface FingerprintDetectionCallback {
+ /**
+ * Invoked when a fingerprint has been detected.
+ */
+ void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric);
+ }
/**
* Callback structure provided to {@link FingerprintManager#enroll(byte[], CancellationSignal,
- * int, int, EnrollmentCallback)} must provide an implementation of this for listening to
+ * int, EnrollmentCallback)} must provide an implementation of this for listening to
* fingerprint events.
*
* @hide
@@ -303,7 +332,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
* @param remaining The number of remaining steps
*/
public void onEnrollmentProgress(int remaining) { }
- };
+ }
/**
* Callback structure provided to {@link #remove}. Users of {@link FingerprintManager} may
@@ -331,7 +360,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
* fingerprints in the group, and 0 after the last fingerprint is removed.
*/
public void onRemovalSucceeded(Fingerprint fp, int remaining) { }
- };
+ }
/**
* @hide
@@ -342,8 +371,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
* Called when lockout period expired and clients are allowed to listen for fingerprint
* again.
*/
- public void onLockoutReset() { }
- };
+ public void onLockoutReset(int sensorId) { }
+ }
+
+ /**
+ * @hide
+ */
+ public abstract static class GenerateChallengeCallback {
+ public abstract void onChallengeGenerated(long challenge);
+ }
+
+ private abstract static class InternalGenerateChallengeCallback
+ extends GenerateChallengeCallback {}
/**
* Request authentication of a crypto object. This call warms up the fingerprint hardware
@@ -372,7 +411,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
- authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
+ authenticate(crypto, cancel, callback, handler, mContext.getUserId());
}
/**
@@ -388,18 +427,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
- * Defaults to {@link FingerprintManager#authenticate(CryptoObject, CancellationSignal, int,
+ * Defaults to {@link FingerprintManager#authenticate(CryptoObject, CancellationSignal,
* AuthenticationCallback, Handler, int, Surface)} with {@code surface} set to null.
*
- * @see FingerprintManager#authenticate(CryptoObject, CancellationSignal, int,
+ * @see FingerprintManager#authenticate(CryptoObject, CancellationSignal,
* AuthenticationCallback, Handler, int, Surface)
*
* @hide
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
- authenticate(crypto, cancel, flags, callback, handler, userId, null /* surface */);
+ @NonNull AuthenticationCallback callback, Handler handler, int userId) {
+ authenticate(crypto, cancel, callback, handler, userId, null /* surface */);
}
/**
@@ -413,7 +452,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId,
+ @NonNull AuthenticationCallback callback, Handler handler, int userId,
@Nullable Surface surface) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
@@ -434,7 +473,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
mAuthenticationCallback = callback;
mCryptoObject = crypto;
final long operationId = crypto != null ? crypto.getOpId() : 0;
- mService.authenticate(mToken, operationId, userId, mServiceReceiver, flags,
+ mService.authenticate(mToken, operationId, userId, mServiceReceiver,
mContext.getOpPackageName(), surface);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception while authenticating: ", e);
@@ -448,18 +487,47 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
- * Defaults to {@link FingerprintManager#enroll(byte[], CancellationSignal, int, int,
+ * Uses the fingerprint hardware to detect for the presence of a finger, without giving details
+ * about accept/reject/lockout.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void detectFingerprint(@NonNull CancellationSignal cancel,
+ @NonNull FingerprintDetectionCallback callback, int userId, @Nullable Surface surface) {
+ if (mService == null) {
+ return;
+ }
+
+ if (cancel.isCanceled()) {
+ Slog.w(TAG, "Detection already cancelled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener());
+ }
+
+ mFingerprintDetectionCallback = callback;
+
+ try {
+ mService.detectFingerprint(mToken, userId, mServiceReceiver,
+ mContext.getOpPackageName(), surface);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Remote exception when requesting finger detect", e);
+ }
+ }
+
+ /**
+ * Defaults to {@link FingerprintManager#enroll(byte[], CancellationSignal, int,
* EnrollmentCallback, Surface)} with {@code surface} set to null.
*
- * @see FingerprintManager#enroll(byte[], CancellationSignal, int, int, EnrollmentCallback,
+ * @see FingerprintManager#enroll(byte[], CancellationSignal, int, EnrollmentCallback,
* Surface)
*
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
- public void enroll(byte [] token, CancellationSignal cancel, int flags,
- int userId, EnrollmentCallback callback) {
- enroll(token, cancel, flags, userId, callback, null /* surface */);
+ public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
+ EnrollmentCallback callback) {
+ enroll(hardwareAuthToken, cancel, userId, callback, null /* surface */);
}
/**
@@ -473,14 +541,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
* @param token a unique token provided by a recent creation or verification of device
* credentials (e.g. pin, pattern or password).
* @param cancel an object that can be used to cancel enrollment
- * @param flags optional flags
* @param userId the user to whom this fingerprint will belong to
* @param callback an object to receive enrollment events
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
- public void enroll(byte [] token, CancellationSignal cancel, int flags,
- int userId, EnrollmentCallback callback, @Nullable Surface surface) {
+ public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
+ EnrollmentCallback callback, @Nullable Surface surface) {
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentUserId();
}
@@ -500,7 +567,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
if (mService != null) {
try {
mEnrollmentCallback = callback;
- mService.enroll(mToken, token, userId, mServiceReceiver, flags,
+ mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
mContext.getOpPackageName(), surface);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
@@ -514,19 +581,56 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
- * Requests a pre-enrollment auth token to tie enrollment to the confirmation of
- * existing device credentials (e.g. pin/pattern/password).
+ * Same as {@link #generateChallenge(GenerateChallengeCallback)}, except blocks until the
+ * TEE/hardware operation is complete.
+ * @return challenge generated in the TEE/hardware
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
- public long preEnroll() {
- long result = 0;
+ public long generateChallengeBlocking() {
+ final AtomicReference<Long> result = new AtomicReference<>();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final GenerateChallengeCallback callback = new InternalGenerateChallengeCallback() {
+ @Override
+ public void onChallengeGenerated(long challenge) {
+ result.set(challenge);
+ latch.countDown();
+ }
+ };
+
+ generateChallenge(callback);
+
+ try {
+ latch.await(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Interrupted while generatingChallenge", e);
+ e.printStackTrace();
+ }
+
+ return result.get();
+ }
+
+
+ /**
+ * Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a
+ * HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification.
+ * The HardwareAuthenticationToken can then be sent to the biometric HAL together with a
+ * request to perform sensitive operation(s) (for example enroll), represented by the challenge.
+ * Doing this ensures that a the sensitive operation cannot be performed unless the user has
+ * entered confirmed PIN/Pattern/Password.
+ *
+ * @see com.android.server.locksettings.LockSettingsService
+ *
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FINGERPRINT)
+ public void generateChallenge(GenerateChallengeCallback callback) {
if (mService != null) try {
- result = mService.preEnroll(mToken);
+ mGenerateChallengeCallback = callback;
+ mService.generateChallenge(mToken, mServiceReceiver, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- return result;
}
/**
@@ -534,14 +638,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
- public int postEnroll() {
- int result = 0;
+ public void revokeChallenge() {
if (mService != null) try {
- result = mService.postEnroll(mToken);
+ mService.revokeChallenge(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- return result;
}
/**
@@ -558,7 +660,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
if (mService != null) try {
mRemovalCallback = callback;
mRemovalFingerprint = fp;
- mService.remove(mToken, fp.getBiometricId(), fp.getGroupId(), userId, mServiceReceiver,
+ mService.remove(mToken, fp.getBiometricId(), userId, mServiceReceiver,
mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in remove: ", e);
@@ -636,6 +738,57 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ if (mService == null) {
+ Slog.w(TAG, "setUdfpsOverlayController: no fingerprint service");
+ return;
+ }
+
+ try {
+ mService.setUdfpsOverlayController(controller);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onFingerDown(int x, int y, float minor, float major) {
+ if (mService == null) {
+ Slog.w(TAG, "onFingerDown: no fingerprint service");
+ return;
+ }
+
+ try {
+ mService.onFingerDown(x, y, minor, major);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onFingerUp() {
+ if (mService == null) {
+ Slog.w(TAG, "onFingerDown: no fingerprint service");
+ return;
+ }
+
+ try {
+ mService.onFingerUp();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
@@ -692,6 +845,24 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
+ * Get statically configured sensor properties.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ @NonNull
+ public List<FingerprintSensorProperties> getSensorProperties() {
+ try {
+ if (mService == null || !mService.isHardwareDetected(mContext.getOpPackageName())) {
+ return new ArrayList<>();
+ }
+ return mService.getSensorProperties(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return new ArrayList<>();
+ }
+
+ /**
* @hide
*/
public void addLockoutResetCallback(final LockoutResetCallback callback) {
@@ -702,7 +873,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
new IBiometricServiceLockoutResetCallback.Stub() {
@Override
- public void onLockoutReset(IRemoteCallback serverCallback)
+ public void onLockoutReset(int sensorId, IRemoteCallback serverCallback)
throws RemoteException {
try {
final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
@@ -710,7 +881,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
wakeLock.acquire();
mHandler.post(() -> {
try {
- callback.onLockoutReset();
+ callback.onLockoutReset(sensorId);
} finally {
wakeLock.release();
}
@@ -719,7 +890,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
serverCallback.sendResult(null /* data */);
}
}
- });
+ }, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -760,9 +931,19 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
case MSG_REMOVED:
sendRemovedResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
break;
+ case MSG_CHALLENGE_GENERATED:
+ sendChallengeGenerated((long) msg.obj /* challenge */);
+ break;
+ case MSG_FINGERPRINT_DETECTED:
+ sendFingerprintDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+ (boolean) msg.obj /* isStrongBiometric */);
+ break;
+ default:
+ Slog.w(TAG, "Unknown message: " + msg.what);
+
}
}
- };
+ }
private void sendRemovedResult(Fingerprint fingerprint, int remaining) {
if (mRemovalCallback == null) {
@@ -779,12 +960,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
return;
}
- int groupId = fingerprint.getGroupId();
- int reqGroupId = mRemovalFingerprint.getGroupId();
- if (groupId != reqGroupId) {
- Slog.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
- return;
- }
mRemovalCallback.onRemovalSucceeded(fingerprint, remaining);
}
@@ -845,6 +1020,22 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
+ private void sendChallengeGenerated(long challenge) {
+ if (mGenerateChallengeCallback == null) {
+ Slog.e(TAG, "sendChallengeGenerated, callback null");
+ return;
+ }
+ mGenerateChallengeCallback.onChallengeGenerated(challenge);
+ }
+
+ private void sendFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {
+ if (mFingerprintDetectionCallback == null) {
+ Slog.e(TAG, "sendFingerprintDetected, callback null");
+ return;
+ }
+ mFingerprintDetectionCallback.onFingerprintDetected(sensorId, userId, isStrongBiometric);
+ }
+
/**
* @hide
*/
@@ -852,7 +1043,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
mContext = context;
mService = service;
if (mService == null) {
- Slog.v(TAG, "FingerprintManagerService was null");
+ Slog.v(TAG, "FingerprintService was null");
}
mHandler = new MyHandler(context);
}
@@ -881,6 +1072,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
+ private void cancelFingerprintDetect() {
+ if (mService == null) {
+ return;
+ }
+
+ try {
+ mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* @hide
*/
@@ -984,6 +1187,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
fp).sendToTarget();
}
+ @Override
+ public void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {
+ mHandler.obtainMessage(MSG_FINGERPRINT_DETECTED, sensorId, userId, isStrongBiometric)
+ .sendToTarget();
+ }
+
@Override // binder call
public void onAuthenticationFailed() {
mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
@@ -998,6 +1207,17 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
public void onRemoved(Fingerprint fp, int remaining) {
mHandler.obtainMessage(MSG_REMOVED, remaining, 0, fp).sendToTarget();
}
+
+ @Override // binder call
+ public void onChallengeGenerated(long challenge) {
+ if (mGenerateChallengeCallback instanceof InternalGenerateChallengeCallback) {
+ // Perform this on system_server thread, since the application's thread is
+ // blocked waiting for the result
+ mGenerateChallengeCallback.onChallengeGenerated(challenge);
+ } else {
+ mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, challenge).sendToTarget();
+ }
+ }
};
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.aidl b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.aidl
new file mode 100644
index 000000000000..83d853dd4048
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.fingerprint;
+
+parcelable FingerprintSensorProperties; \ No newline at end of file
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
new file mode 100644
index 000000000000..a774121c43f4
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+import android.annotation.IntDef;
+import android.hardware.face.FaceSensorProperties;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Container for fingerprint sensor properties.
+ * @hide
+ */
+public class FingerprintSensorProperties implements Parcelable {
+
+ public static final int TYPE_UNKNOWN = 0;
+ public static final int TYPE_REAR = 1;
+ public static final int TYPE_UDFPS = 2;
+ public static final int TYPE_POWER_BUTTON = 3;
+
+ @IntDef({
+ TYPE_UNKNOWN,
+ TYPE_REAR,
+ TYPE_UDFPS,
+ TYPE_POWER_BUTTON})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SensorType {}
+
+ public final int sensorId;
+ public final @SensorType int sensorType;
+
+ /**
+ * Initializes SensorProperties with specified values
+ */
+ public FingerprintSensorProperties(int sensorId, @SensorType int sensorType) {
+ this.sensorId = sensorId;
+ this.sensorType = sensorType;
+ }
+
+ protected FingerprintSensorProperties(Parcel in) {
+ sensorId = in.readInt();
+ sensorType = in.readInt();
+ }
+
+ public static final Creator<FingerprintSensorProperties> CREATOR =
+ new Creator<FingerprintSensorProperties>() {
+ @Override
+ public FingerprintSensorProperties createFromParcel(Parcel in) {
+ return new FingerprintSensorProperties(in);
+ }
+
+ @Override
+ public FingerprintSensorProperties[] newArray(int size) {
+ return new FingerprintSensorProperties[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(sensorId);
+ dest.writeInt(sensorType);
+ }
+}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 44e290831576..f6069d81934f 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -19,7 +19,9 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintSensorProperties;
import android.view.Surface;
import java.util.List;
@@ -28,12 +30,19 @@ import java.util.List;
* @hide
*/
interface IFingerprintService {
+ // Retrieve static sensor properties for all fingerprint sensors
+ List<FingerprintSensorProperties> getSensorProperties(String opPackageName);
+
// Authenticate the given sessionId with a fingerprint. This is protected by
// USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes
// through FingerprintManager now.
void authenticate(IBinder token, long operationId, int userId,
- IFingerprintServiceReceiver receiver, int flags, String opPackageName,
- in Surface surface);
+ IFingerprintServiceReceiver receiver, String opPackageName, in Surface surface);
+
+ // Uses the fingerprint hardware to detect for the presence of a finger, without giving details
+ // about accept/reject/lockout.
+ void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
+ String opPackageName, in Surface surface);
// This method prepares the service to start authenticating, but doesn't start authentication.
// This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
@@ -50,21 +59,24 @@ interface IFingerprintService {
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
+ // Cancel finger detection
+ void cancelFingerprintDetect(IBinder token, String opPackageName);
+
// Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
// an additional uid, pid, userid.
void cancelAuthenticationFromService(IBinder token, String opPackageName,
int callingUid, int callingPid, int callingUserId);
// Start fingerprint enrollment
- void enroll(IBinder token, in byte [] cryptoToken, int userId, IFingerprintServiceReceiver receiver,
- int flags, String opPackageName, in Surface surface);
+ void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
+ String opPackageName, in Surface surface);
// Cancel enrollment in progress
void cancelEnrollment(IBinder token);
// Any errors resulting from this call will be returned to the listener
- void remove(IBinder token, int fingerId, int groupId, int userId,
- IFingerprintServiceReceiver receiver, String opPackageName);
+ void remove(IBinder token, int fingerId, int userId, IFingerprintServiceReceiver receiver,
+ String opPackageName);
// Rename the fingerprint specified by fingerId and userId to the given name
void rename(int fingerId, int userId, String name);
@@ -76,22 +88,25 @@ interface IFingerprintService {
boolean isHardwareDetected(String opPackageName);
// Get a pre-enrollment authentication token
- long preEnroll(IBinder token);
+ void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver, String opPackageName);
// Finish an enrollment sequence and invalidate the authentication token
- int postEnroll(IBinder token);
+ void revokeChallenge(IBinder token, String opPackageName);
// Determine if a user has at least one enrolled fingerprint
boolean hasEnrolledFingerprints(int userId, String opPackageName);
+ // Return the LockoutTracker status for the specified user
+ int getLockoutModeForUser(int userId);
+
// Gets the authenticator ID for fingerprint
long getAuthenticatorId(int callingUserId);
// Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
- void resetLockout(int userId, in byte [] cryptoToken);
+ void resetLockout(int userId, in byte [] hardwareAuthToken);
// Add a callback which gets notified when the fingerprint lockout period expired.
- void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
+ void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
// Check if a client request is currently being handled
boolean isClientActive();
@@ -104,4 +119,13 @@ interface IFingerprintService {
// Give FingerprintService its ID. See AuthService.java
void initializeConfiguration(int sensorId);
+
+ // Notifies about a finger touching the sensor area.
+ void onFingerDown(int x, int y, float minor, float major);
+
+ // Notifies about a finger leaving the sensor area.
+ void onFingerUp();
+
+ // Sets the controller for managing the UDFPS overlay.
+ void setUdfpsOverlayController(in IUdfpsOverlayController controller);
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 87748a32b40b..ad8fbc087ed0 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -25,7 +25,9 @@ oneway interface IFingerprintServiceReceiver {
void onEnrollResult(in Fingerprint fp, int remaining);
void onAcquired(int acquiredInfo, int vendorCode);
void onAuthenticationSucceeded(in Fingerprint fp, int userId, boolean isStrongBiometric);
+ void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric);
void onAuthenticationFailed();
void onError(int error, int vendorCode);
void onRemoved(in Fingerprint fp, int remaining);
+ void onChallengeGenerated(long challenge);
}
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
new file mode 100644
index 000000000000..32530da74e89
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.fingerprint;
+
+/**
+ * Interface for interacting with the under-display fingerprint sensor (UDFPS) overlay.
+ * @hide
+ */
+oneway interface IUdfpsOverlayController {
+ // Shows the overlay.
+ void showUdfpsOverlay();
+
+ // Hides the overlay.
+ void hideUdfpsOverlay();
+}
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index fc88f1e3062f..2579ee687827 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -37,6 +37,7 @@ import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ConcurrentUtils;
import java.util.ArrayList;
import java.util.List;
@@ -891,6 +892,11 @@ public final class HdmiControlManager {
* <p>To stop getting the notification,
* use {@link #removeHotplugEventListener(HotplugEventListener)}.
*
+ * Note that each invocation of the callback will be executed on an arbitrary
+ * Binder thread. This means that all callback implementations must be
+ * thread safe. To specify the execution thread, use
+ * {@link addHotplugEventListener(Executor, HotplugEventListener)}.
+ *
* @param listener {@link HotplugEventListener} instance
* @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener)
*
@@ -899,6 +905,24 @@ public final class HdmiControlManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void addHotplugEventListener(HotplugEventListener listener) {
+ addHotplugEventListener(ConcurrentUtils.DIRECT_EXECUTOR, listener);
+ }
+
+ /**
+ * Adds a listener to get informed of {@link HdmiHotplugEvent}.
+ *
+ * <p>To stop getting the notification,
+ * use {@link #removeHotplugEventListener(HotplugEventListener)}.
+ *
+ * @param listener {@link HotplugEventListener} instance
+ * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener)
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void addHotplugEventListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull HotplugEventListener listener) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
return;
@@ -907,7 +931,8 @@ public final class HdmiControlManager {
Log.e(TAG, "listener is already registered");
return;
}
- IHdmiHotplugEventListener wrappedListener = getHotplugEventListenerWrapper(listener);
+ IHdmiHotplugEventListener wrappedListener =
+ getHotplugEventListenerWrapper(executor, listener);
mHotplugEventListeners.put(listener, wrappedListener);
try {
mService.addHotplugEventListener(wrappedListener);
@@ -943,11 +968,12 @@ public final class HdmiControlManager {
}
private IHdmiHotplugEventListener getHotplugEventListenerWrapper(
- final HotplugEventListener listener) {
+ Executor executor, final HotplugEventListener listener) {
return new IHdmiHotplugEventListener.Stub() {
@Override
public void onReceived(HdmiHotplugEvent event) {
- listener.onReceived(event);;
+ Binder.clearCallingIdentity();
+ executor.execute(() -> listener.onReceived(event));
}
};
}
@@ -958,6 +984,11 @@ public final class HdmiControlManager {
* <p>To stop getting the notification,
* use {@link #removeHdmiControlStatusChangeListener(HdmiControlStatusChangeListener)}.
*
+ * Note that each invocation of the callback will be executed on an arbitrary
+ * Binder thread. This means that all callback implementations must be
+ * thread safe. To specify the execution thread, use
+ * {@link addHdmiControlStatusChangeListener(Executor, HdmiControlStatusChangeListener)}.
+ *
* @param listener {@link HdmiControlStatusChangeListener} instance
* @see HdmiControlManager#removeHdmiControlStatusChangeListener(
* HdmiControlStatusChangeListener)
@@ -966,6 +997,24 @@ public final class HdmiControlManager {
*/
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void addHdmiControlStatusChangeListener(HdmiControlStatusChangeListener listener) {
+ addHdmiControlStatusChangeListener(ConcurrentUtils.DIRECT_EXECUTOR, listener);
+ }
+
+ /**
+ * 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(@NonNull @CallbackExecutor Executor executor,
+ @NonNull HdmiControlStatusChangeListener listener) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
return;
@@ -975,7 +1024,7 @@ public final class HdmiControlManager {
return;
}
IHdmiControlStatusChangeListener wrappedListener =
- getHdmiControlStatusChangeListenerWrapper(listener);
+ getHdmiControlStatusChangeListenerWrapper(executor, listener);
mHdmiControlStatusChangeListeners.put(listener, wrappedListener);
try {
mService.addHdmiControlStatusChangeListener(wrappedListener);
@@ -1011,11 +1060,12 @@ public final class HdmiControlManager {
}
private IHdmiControlStatusChangeListener getHdmiControlStatusChangeListenerWrapper(
- final HdmiControlStatusChangeListener listener) {
+ Executor executor, final HdmiControlStatusChangeListener listener) {
return new IHdmiControlStatusChangeListener.Stub() {
@Override
public void onStatusChange(boolean isCecEnabled, boolean isCecAvailable) {
- listener.onStatusChange(isCecEnabled, isCecAvailable);
+ Binder.clearCallingIdentity();
+ executor.execute(() -> listener.onStatusChange(isCecEnabled, isCecAvailable));
}
};
}
diff --git a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
index c708d2030bb0..a16e8787df8a 100644
--- a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
@@ -22,7 +22,7 @@ package android.hardware.hdmi;
*
* @hide
*/
-interface IHdmiVendorCommandListener {
+oneway interface IHdmiVendorCommandListener {
void onReceived(int logicalAddress, int destAddress, in byte[] operands, boolean hasVendorId);
void onControlStateChanged(boolean enabled, int reason);
}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index fb44b0b66fd8..ba0636fa80ff 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -83,7 +83,7 @@ interface IInputManager {
int isMicMuted();
// Input device vibrator control.
- void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
+ void vibrate(int deviceId, in long[] pattern, in int[] amplitudes, int repeat, IBinder token);
void cancelVibrate(int deviceId, IBinder token);
void setPointerIconType(int typeId);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 72217a1196ea..f0faeb078386 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -54,8 +54,8 @@ import com.android.internal.os.SomeArgs;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.concurrent.Executor;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Provides information about input devices and available key layouts.
@@ -1298,14 +1298,17 @@ public final class InputManager {
public void vibrate(int uid, String opPkg, VibrationEffect effect,
String reason, AudioAttributes attributes) {
long[] pattern;
+ int[] amplitudes;
int repeat;
if (effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
pattern = new long[] { 0, oneShot.getDuration() };
+ amplitudes = new int[] { 0, oneShot.getAmplitude() };
repeat = -1;
} else if (effect instanceof VibrationEffect.Waveform) {
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
pattern = waveform.getTimings();
+ amplitudes = waveform.getAmplitudes();
repeat = waveform.getRepeatIndex();
} else {
// TODO: Add support for prebaked effects
@@ -1314,7 +1317,7 @@ public final class InputManager {
}
try {
- mIm.vibrate(mDeviceId, pattern, repeat, mToken);
+ mIm.vibrate(mDeviceId, pattern, amplitudes, repeat, mToken);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index a94266b811d5..f4cfc74aa77d 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -24,7 +24,6 @@ import android.os.Parcelable;
import android.os.RemoteException;
import com.android.internal.util.Preconditions;
-import java.util.Objects;
import java.util.Objects;
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index d16f070f7209..ef305e2c1867 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -160,6 +160,22 @@ public class UsbManager {
"android.hardware.usb.action.USB_ACCESSORY_DETACHED";
/**
+ * Broadcast Action: A broadcast for USB accessory handshaking information delivery
+ *
+ * This intent is sent when a USB accessory connect attempt
+ *
+ * <p>For more information about communicating with USB accessory handshake, refer to
+ * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+ *
+ * {@hide}
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public static final String ACTION_USB_ACCESSORY_HANDSHAKE =
+ "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
+
+ /**
* Boolean extra indicating whether USB is connected or disconnected.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
*
@@ -303,6 +319,52 @@ public class UsbManager {
public static final String EXTRA_ACCESSORY = "accessory";
/**
+ * A long extra indicating ms from boot to get get_protocol UEvent
+ * This is obtained with SystemClock.elapsedRealtime()
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_UEVENT_TIME =
+ "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
+
+ /**
+ * An integer extra indicating numbers of send string during handshake
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts
+ *
+ * <p>For more information about control request with identifying string information
+ * between communicating with USB accessory handshake, refer to
+ * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_STRING_COUNT =
+ "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+
+ /**
+ * Boolean extra indicating whether got start accessory or not
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_START =
+ "android.hardware.usb.extra.ACCESSORY_START";
+
+ /**
+ * A long extra indicating ms from boot to sent {@link #ACTION_USB_ACCESSORY_HANDSHAKE}
+ * This is obtained with SystemClock.elapsedRealtime()
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_HANDSHAKE_END =
+ "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+
+ /**
* Name of extra added to the {@link android.app.PendingIntent}
* passed into {@link #requestPermission(UsbDevice, PendingIntent)}
* or {@link #requestPermission(UsbAccessory, PendingIntent)}
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index 509cbe09df69..20bf6e0024e3 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -149,6 +149,11 @@ class InlineSuggestionSession {
*/
@MainThread
void invalidate() {
+ try {
+ mCallback.onInlineSuggestionsSessionInvalidated();
+ } catch (RemoteException e) {
+ Log.w(TAG, "onInlineSuggestionsSessionInvalidated() remote exception", e);
+ }
if (mResponseCallback != null) {
consumeInlineSuggestionsResponse(EMPTY_RESPONSE);
mResponseCallback.invalidate();
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSessionController.java b/core/java/android/inputmethodservice/InlineSuggestionSessionController.java
index 8c0dd2a9bf59..071c096ee2f0 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSessionController.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSessionController.java
@@ -134,6 +134,7 @@ class InlineSuggestionSessionController {
mImeClientFieldId = imeFieldId;
if (mSession != null) {
+ mSession.consumeInlineSuggestionsResponse(InlineSuggestionSession.EMPTY_RESPONSE);
// Initiates the callback to Autofill if there is a pending matching session.
// Otherwise updates the session with the Ime status.
if (!mSession.isCallbackInvoked() && match(mSession.getRequestInfo())) {
@@ -213,7 +214,6 @@ class InlineSuggestionSessionController {
mImeInputViewStarted = false;
mImeInputStarted = false;
if (mSession != null && mSession.shouldSendImeStatus()) {
- mSession.consumeInlineSuggestionsResponse(InlineSuggestionSession.EMPTY_RESPONSE);
try {
mSession.getRequestCallback().onInputMethodFinishInput();
} catch (RemoteException e) {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 5647bf90d2fb..c5a11abe1136 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -605,9 +605,6 @@ public class InputMethodService extends AbstractInputMethodService {
if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
+ " ic=" + mInputConnection);
// Unbind input is per process per display.
- // TODO(b/150902448): free-up IME surface when target is changing.
- // e.g. DisplayContent#setInputMethodTarget()
- removeImeSurface();
onUnbindInput();
mInputBinding = null;
mInputConnection = null;
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 5496e17206d9..d8a2082f4eae 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -39,7 +39,7 @@ public class LogMaker {
/**
* Min required eventlog line length.
* See: android/util/cts/EventLogTest.java
- * Size checks enforced here are intended only as sanity checks;
+ * Size limits enforced here are intended only as a precaution;
* your logs may be truncated earlier. Please log responsibly.
*
* @hide
diff --git a/core/java/android/net/ITetheredInterfaceCallback.aidl b/core/java/android/net/ITetheredInterfaceCallback.aidl
index e3d075988c8a..14aa0237f24a 100644
--- a/core/java/android/net/ITetheredInterfaceCallback.aidl
+++ b/core/java/android/net/ITetheredInterfaceCallback.aidl
@@ -17,7 +17,7 @@
package android.net;
/** @hide */
-interface ITetheredInterfaceCallback {
+oneway interface ITetheredInterfaceCallback {
void onAvailable(in String iface);
void onUnavailable();
} \ No newline at end of file
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 0e10c42e61db..0eb3c1e8ad01 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -38,7 +38,9 @@ import java.util.Arrays;
* Representation of a MAC address.
*
* This class only supports 48 bits long addresses and does not support 64 bits long addresses.
- * Instances of this class are immutable.
+ * Instances of this class are immutable. This class provides implementations of hashCode()
+ * and equals() that make it suitable for use as keys in standard implementations of
+ * {@link java.util.Map}.
*/
public final class MacAddress implements Parcelable {
@@ -122,12 +124,22 @@ public final class MacAddress implements Parcelable {
}
/**
+ * Convert this MacAddress to a byte array.
+ *
+ * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6,
+ * the returned array is [1, 2, 3, 4, 5, 6].
+ *
* @return a byte array representation of this MacAddress.
*/
public @NonNull byte[] toByteArray() {
return byteAddrFromLongAddr(mAddr);
}
+ /**
+ * Returns a human-readable representation of this MacAddress.
+ * The exact format is implementation-dependent and should not be assumed to have any
+ * particular format.
+ */
@Override
public @NonNull String toString() {
return stringAddrFromLongAddr(mAddr);
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index eba7ff348328..7234eb1d81cd 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -87,6 +87,15 @@ public class NetworkTemplate implements Parcelable {
* @hide
*/
public static final int NETWORK_TYPE_ALL = -1;
+ /**
+ * Virtual RAT type to represent 5G NSA (Non Stand Alone) mode, where the primary cell is
+ * still LTE and network allocates a secondary 5G cell so telephony reports RAT = LTE along
+ * with NR state as connected. This should not be overlapped with any of the
+ * {@code TelephonyManager.NETWORK_TYPE_*} constants.
+ *
+ * @hide
+ */
+ public static final int NETWORK_TYPE_5G_NSA = -2;
private static boolean isKnownMatchRule(final int rule) {
switch (rule) {
@@ -475,6 +484,9 @@ public class NetworkTemplate implements Parcelable {
return TelephonyManager.NETWORK_TYPE_LTE;
case TelephonyManager.NETWORK_TYPE_NR:
return TelephonyManager.NETWORK_TYPE_NR;
+ // Virtual RAT type for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}.
+ case NetworkTemplate.NETWORK_TYPE_5G_NSA:
+ return NetworkTemplate.NETWORK_TYPE_5G_NSA;
default:
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index ffe9ae9521a8..a32b41f6be4b 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -127,18 +127,6 @@ public class ProxyInfo implements Parcelable {
}
/**
- * Create a ProxyProperties that points at a PAC URL.
- * @hide
- */
- public ProxyInfo(String pacFileUrl) {
- mHost = LOCAL_HOST;
- mPort = LOCAL_PORT;
- mExclusionList = LOCAL_EXCL_LIST;
- mParsedExclusionList = parseExclusionList(mExclusionList);
- mPacFileUrl = Uri.parse(pacFileUrl);
- }
-
- /**
* Only used in PacManager after Local Proxy is bound.
* @hide
*/
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 1cb4fe8cf4e7..efe2903814d7 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -116,16 +116,27 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
private static final String LOG = Uri.class.getSimpleName();
/**
- * NOTE: EMPTY accesses this field during its own initialization, so this
- * field *must* be initialized first, or else EMPTY will see a null value!
*
- * Placeholder for strings which haven't been cached. This enables us
+ * Holds a placeholder for strings which haven't been cached. This enables us
* to cache null. We intentionally create a new String instance so we can
* compare its identity and there is no chance we will confuse it with
* user data.
+ *
+ * NOTE This value is held in its own Holder class is so that referring to
+ * {@link NotCachedHolder#NOT_CACHED} does not trigger {@code Uri.<clinit>}.
+ * For example, {@code PathPart.<init>} uses {@code NotCachedHolder.NOT_CACHED}
+ * but must not trigger {@code Uri.<clinit>}: Otherwise, the initialization of
+ * {@code Uri.EMPTY} would see a {@code null} value for {@code PathPart.EMPTY}!
+ *
+ * @hide
*/
- @SuppressWarnings("RedundantStringConstructorCall")
- private static final String NOT_CACHED = new String("NOT CACHED");
+ static class NotCachedHolder {
+ private NotCachedHolder() {
+ // prevent instantiation
+ }
+ @SuppressWarnings("RedundantStringConstructorCall")
+ static final String NOT_CACHED = new String("NOT CACHED");
+ }
/**
* The empty URI, equivalent to "".
@@ -554,11 +565,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
return findSchemeSeparator() == NOT_FOUND;
}
- private volatile String scheme = NOT_CACHED;
+ private volatile String scheme = NotCachedHolder.NOT_CACHED;
public String getScheme() {
@SuppressWarnings("StringEquality")
- boolean cached = (scheme != NOT_CACHED);
+ boolean cached = (scheme != NotCachedHolder.NOT_CACHED);
return cached ? scheme : (scheme = parseScheme());
}
@@ -968,11 +979,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
return -1;
}
- private volatile String cachedString = NOT_CACHED;
+ private volatile String cachedString = NotCachedHolder.NOT_CACHED;
public String toString() {
@SuppressWarnings("StringEquality")
- boolean cached = cachedString != NOT_CACHED;
+ boolean cached = cachedString != NotCachedHolder.NOT_CACHED;
if (cached) {
return cachedString;
}
@@ -1102,11 +1113,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
return getUserInfoPart().getDecoded();
}
- private volatile String host = NOT_CACHED;
+ private volatile String host = NotCachedHolder.NOT_CACHED;
public String getHost() {
@SuppressWarnings("StringEquality")
- boolean cached = (host != NOT_CACHED);
+ boolean cached = (host != NotCachedHolder.NOT_CACHED);
return cached ? host : (host = parseHost());
}
@@ -1305,12 +1316,12 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
return this.path.getPathSegments();
}
- private volatile String uriString = NOT_CACHED;
+ private volatile String uriString = NotCachedHolder.NOT_CACHED;
@Override
public String toString() {
@SuppressWarnings("StringEquality")
- boolean cached = (uriString != NOT_CACHED);
+ boolean cached = (uriString != NotCachedHolder.NOT_CACHED);
return cached ? uriString
: (uriString = makeUriString());
}
@@ -1992,13 +2003,13 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
private final int mCanonicalRepresentation;
AbstractPart(String encoded, String decoded) {
- if (encoded != NOT_CACHED) {
+ if (encoded != NotCachedHolder.NOT_CACHED) {
this.mCanonicalRepresentation = REPRESENTATION_ENCODED;
this.encoded = encoded;
- this.decoded = NOT_CACHED;
- } else if (decoded != NOT_CACHED) {
+ this.decoded = NotCachedHolder.NOT_CACHED;
+ } else if (decoded != NotCachedHolder.NOT_CACHED) {
this.mCanonicalRepresentation = REPRESENTATION_DECODED;
- this.encoded = NOT_CACHED;
+ this.encoded = NotCachedHolder.NOT_CACHED;
this.decoded = decoded;
} else {
throw new IllegalArgumentException("Neither encoded nor decoded");
@@ -2009,7 +2020,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
final String getDecoded() {
@SuppressWarnings("StringEquality")
- boolean hasDecoded = decoded != NOT_CACHED;
+ boolean hasDecoded = decoded != NotCachedHolder.NOT_CACHED;
return hasDecoded ? decoded : (decoded = decode(encoded));
}
@@ -2023,7 +2034,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
throw new IllegalArgumentException("Unknown representation: "
+ mCanonicalRepresentation);
}
- if (canonicalValue == NOT_CACHED) {
+ if (canonicalValue == NotCachedHolder.NOT_CACHED) {
throw new AssertionError("Canonical value not cached ("
+ mCanonicalRepresentation + ")");
}
@@ -2054,7 +2065,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
String getEncoded() {
@SuppressWarnings("StringEquality")
- boolean hasEncoded = encoded != NOT_CACHED;
+ boolean hasEncoded = encoded != NotCachedHolder.NOT_CACHED;
return hasEncoded ? encoded : (encoded = encode(decoded));
}
@@ -2085,7 +2096,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
* @param encoded part string
*/
static Part fromEncoded(String encoded) {
- return from(encoded, NOT_CACHED);
+ return from(encoded, NotCachedHolder.NOT_CACHED);
}
/**
@@ -2094,7 +2105,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
* @param decoded part string
*/
static Part fromDecoded(String decoded) {
- return from(NOT_CACHED, decoded);
+ return from(NotCachedHolder.NOT_CACHED, decoded);
}
/**
@@ -2105,7 +2116,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*/
static Part from(String encoded, String decoded) {
// We have to check both encoded and decoded in case one is
- // NOT_CACHED.
+ // NotCachedHolder.NOT_CACHED.
if (encoded == null) {
return NULL;
@@ -2159,7 +2170,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
String getEncoded() {
@SuppressWarnings("StringEquality")
- boolean hasEncoded = encoded != NOT_CACHED;
+ boolean hasEncoded = encoded != NotCachedHolder.NOT_CACHED;
// Don't encode '/'.
return hasEncoded ? encoded : (encoded = encode(decoded, "/"));
@@ -2265,7 +2276,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
* @param encoded part string
*/
static PathPart fromEncoded(String encoded) {
- return from(encoded, NOT_CACHED);
+ return from(encoded, NotCachedHolder.NOT_CACHED);
}
/**
@@ -2274,7 +2285,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
* @param decoded part string
*/
static PathPart fromDecoded(String decoded) {
- return from(NOT_CACHED, decoded);
+ return from(NotCachedHolder.NOT_CACHED, decoded);
}
/**
@@ -2301,7 +2312,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*/
static PathPart makeAbsolute(PathPart oldPart) {
@SuppressWarnings("StringEquality")
- boolean encodedCached = oldPart.encoded != NOT_CACHED;
+ boolean encodedCached = oldPart.encoded != NotCachedHolder.NOT_CACHED;
// We don't care which version we use, and we don't want to force
// unneccessary encoding/decoding.
@@ -2314,14 +2325,14 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
// Prepend encoded string if present.
String newEncoded = encodedCached
- ? "/" + oldPart.encoded : NOT_CACHED;
+ ? "/" + oldPart.encoded : NotCachedHolder.NOT_CACHED;
// Prepend decoded string if present.
@SuppressWarnings("StringEquality")
- boolean decodedCached = oldPart.decoded != NOT_CACHED;
+ boolean decodedCached = oldPart.decoded != NotCachedHolder.NOT_CACHED;
String newDecoded = decodedCached
? "/" + oldPart.decoded
- : NOT_CACHED;
+ : NotCachedHolder.NOT_CACHED;
return new PathPart(newEncoded, newDecoded);
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 947b77399e40..515704ca77f4 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -25,6 +25,8 @@ import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.os.BinderCallHeavyHitterWatcher;
+import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.BinderInternal.CallSession;
import com.android.internal.util.FastPrintWriter;
@@ -120,6 +122,14 @@ public class Binder implements IBinder {
private static native long getNativeFinalizer();
+ /**
+ * Returns the TID (task ID) for the current thread. Same as {@link Thread#getNativeTid()}
+ *
+ * @hide
+ */
+ @CriticalNative
+ public static native int getNativeTid();
+
// Use a Holder to allow static initialization of Binder in the boot image, and
// possibly to avoid some initialization ordering issues.
private static class NoImagePreloadHolder {
@@ -127,6 +137,10 @@ public class Binder implements IBinder {
Binder.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
}
+ /**
+ * The watcher to monitor the heavy hitter from incoming transactions
+ */
+ private static volatile BinderCallHeavyHitterWatcher sHeavyHitterWatcher = null;
// Transaction tracking code.
@@ -529,15 +543,17 @@ public class Binder implements IBinder {
/**
* Mark as being built with VINTF-level stability promise. This API should
- * only ever be invoked by the build system. It means that the interface
- * represented by this binder is guaranteed to be kept stable for several
- * years, and the build system also keeps snapshots of these APIs and
- * invokes the AIDL compiler to make sure that these snapshots are
- * backwards compatible. Instead of using this API, use an @VintfStability
- * interface.
+ * only ever be invoked by generated code from the aidl compiler. It means
+ * that the interface represented by this binder is guaranteed to be kept
+ * stable for several years, according to the VINTF compatibility lifecycle,
+ * and the build system also keeps snapshots of these APIs and invokes the
+ * AIDL compiler to make sure that these snapshots are backwards compatible.
+ * Instead of using this API, use the @VintfStability annotation on your
+ * AIDL interface.
*
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final native void markVintfStability();
/**
@@ -1144,6 +1160,11 @@ public class Binder implements IBinder {
// If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
final boolean tracingEnabled = Binder.isTracingEnabled();
try {
+ final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
+ if (heavyHitterWatcher != null) {
+ // Notify the heavy hitter watcher, if it's enabled
+ heavyHitterWatcher.onTransaction(callingUid, getClass(), code);
+ }
if (tracingEnabled) {
final String transactionName = getTransactionName(code);
Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
@@ -1204,4 +1225,33 @@ public class Binder implements IBinder {
StrictMode.clearGatheredViolations();
return res;
}
+
+ /**
+ * Set the configuration for the heavy hitter watcher.
+ *
+ * @hide
+ */
+ public static synchronized void setHeavyHitterWatcherConfig(final boolean enabled,
+ final int batchSize, final float threshold,
+ @Nullable final BinderCallHeavyHitterListener listener) {
+ Slog.i(TAG, "Setting heavy hitter watcher config: "
+ + enabled + ", " + batchSize + ", " + threshold);
+ BinderCallHeavyHitterWatcher watcher = sHeavyHitterWatcher;
+ if (enabled) {
+ if (listener == null) {
+ throw new IllegalArgumentException();
+ }
+ boolean newWatcher = false;
+ if (watcher == null) {
+ watcher = BinderCallHeavyHitterWatcher.getInstance();
+ newWatcher = true;
+ }
+ watcher.setConfig(true, batchSize, threshold, listener);
+ if (newWatcher) {
+ sHeavyHitterWatcher = watcher;
+ }
+ } else if (watcher != null) {
+ watcher.setConfig(false, 0, 0.0f, null);
+ }
+ }
}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 9f592e8d9718..4b2cfe222dd6 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1115,8 +1115,6 @@ public final class Debug
if (outStream != null)
outStream.close();
}
-
- VMDebug.startEmulatorTracing();
}
/**
@@ -1130,8 +1128,6 @@ public final class Debug
* region of code.</p>
*/
public static void stopNativeTracing() {
- VMDebug.stopEmulatorTracing();
-
// Open the sysfs file for writing and write "0" to it.
PrintWriter outStream = null;
try {
@@ -1160,7 +1156,7 @@ public final class Debug
* To temporarily enable tracing, use {@link #startNativeTracing()}.
*/
public static void enableEmulatorTraceOutput() {
- VMDebug.startEmulatorTracing();
+ Log.w(TAG, "Unimplemented");
}
/**
@@ -2097,25 +2093,6 @@ public final class Debug
public static final native int getBinderDeathObjectCount();
/**
- * Primes the register map cache.
- *
- * Only works for classes in the bootstrap class loader. Does not
- * cause classes to be loaded if they're not already present.
- *
- * The classAndMethodDesc argument is a concatentation of the VM-internal
- * class descriptor, method name, and method descriptor. Examples:
- * Landroid/os/Looper;.loop:()V
- * Landroid/app/ActivityThread;.main:([Ljava/lang/String;)V
- *
- * @param classAndMethodDesc the method to prepare
- *
- * @hide
- */
- public static final boolean cacheRegisterMap(String classAndMethodDesc) {
- return VMDebug.cacheRegisterMap(classAndMethodDesc);
- }
-
- /**
* Dumps the contents of VM reference tables (e.g. JNI locals and
* globals) to the log file.
*
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index 18ba5a8a4a34..bb8d4fe19aa2 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -375,7 +375,8 @@ public class DropBoxManager {
@RequiresPermission(allOf = { READ_LOGS, PACKAGE_USAGE_STATS })
public @Nullable Entry getNextEntry(String tag, long msec) {
try {
- return mService.getNextEntry(tag, msec, mContext.getOpPackageName());
+ return mService.getNextEntryWithAttribution(tag, msec, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
} catch (SecurityException e) {
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
throw e;
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 7c42c36e7747..64ab1d711765 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -96,6 +96,15 @@ public abstract class HwBinder implements IHwBinder {
throws RemoteException, NoSuchElementException;
/**
+ * This allows getService to bypass the VINTF manifest for testing only.
+ *
+ * Disabled on user builds.
+ * @hide
+ */
+ public static native final void setTrebleTestingOverride(
+ boolean testingOverride);
+
+ /**
* Configures how many threads the process-wide hwbinder threadpool
* has to process incoming requests.
*
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 7b82b1a2e0d4..415e5a60a140 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -298,14 +298,15 @@ public final class Parcel {
private static native void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len);
private static native void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len);
- @FastNative
- private static native void nativeWriteInt(long nativePtr, int val);
- @FastNative
- private static native void nativeWriteLong(long nativePtr, long val);
- @FastNative
- private static native void nativeWriteFloat(long nativePtr, float val);
- @FastNative
- private static native void nativeWriteDouble(long nativePtr, double val);
+ @CriticalNative
+ private static native int nativeWriteInt(long nativePtr, int val);
+ @CriticalNative
+ private static native int nativeWriteLong(long nativePtr, long val);
+ @CriticalNative
+ private static native int nativeWriteFloat(long nativePtr, float val);
+ @CriticalNative
+ private static native int nativeWriteDouble(long nativePtr, double val);
+ private static native void nativeSignalExceptionForError(int error);
@FastNative
private static native void nativeWriteString8(long nativePtr, String val);
@FastNative
@@ -734,12 +735,20 @@ public final class Parcel {
nativeWriteBlob(mNativePtr, b, offset, len);
}
+ // The OK status from system/core/libutils/include/utils/Errors.h .
+ // We shall pass all other error codes back to native for throwing exceptions. The error
+ // check is done in Java to allow using @CriticalNative calls for the success path.
+ private static final int OK = 0;
+
/**
* Write an integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeInt(int val) {
- nativeWriteInt(mNativePtr, val);
+ int err = nativeWriteInt(mNativePtr, val);
+ if (err != OK) {
+ nativeSignalExceptionForError(err);
+ }
}
/**
@@ -747,7 +756,10 @@ public final class Parcel {
* growing dataCapacity() if needed.
*/
public final void writeLong(long val) {
- nativeWriteLong(mNativePtr, val);
+ int err = nativeWriteLong(mNativePtr, val);
+ if (err != OK) {
+ nativeSignalExceptionForError(err);
+ }
}
/**
@@ -755,7 +767,10 @@ public final class Parcel {
* dataPosition(), growing dataCapacity() if needed.
*/
public final void writeFloat(float val) {
- nativeWriteFloat(mNativePtr, val);
+ int err = nativeWriteFloat(mNativePtr, val);
+ if (err != OK) {
+ nativeSignalExceptionForError(err);
+ }
}
/**
@@ -763,7 +778,10 @@ public final class Parcel {
* current dataPosition(), growing dataCapacity() if needed.
*/
public final void writeDouble(double val) {
- nativeWriteDouble(mNativePtr, val);
+ int err = nativeWriteDouble(mNativePtr, val);
+ if (err != OK) {
+ nativeSignalExceptionForError(err);
+ }
}
/**
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 9b360edb7238..bedbba04255e 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -99,6 +99,35 @@ public interface Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface ContentsFlags {}
+ /** @hide */
+ @IntDef(flag = true, prefix = { "PARCELABLE_STABILITY_" }, value = {
+ PARCELABLE_STABILITY_LOCAL,
+ PARCELABLE_STABILITY_VINTF,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Stability {}
+
+ /**
+ * Something that is not meant to cross compilation boundaries.
+ *
+ * Note: unlike binder/Stability.h which uses bitsets to detect stability,
+ * since we don't currently have a notion of different local locations,
+ * higher stability levels are formed at higher levels.
+ *
+ * For instance, contained entirely within system partitions.
+ * @see #getStability()
+ * @see ParcelableHolder
+ * @hide
+ */
+ public static final int PARCELABLE_STABILITY_LOCAL = 0x0000;
+ /**
+ * Something that is meant to be used between system and vendor.
+ * @see #getStability()
+ * @see ParcelableHolder
+ * @hide
+ */
+ public static final int PARCELABLE_STABILITY_VINTF = 0x0001;
+
/**
* Descriptor bit used with {@link #describeContents()}: indicates that
* the Parcelable object's flattened representation includes a file descriptor.
@@ -129,8 +158,8 @@ public interface Parcelable {
* @return true if this parcelable is stable.
* @hide
*/
- default boolean isStable() {
- return false;
+ default @Stability int getStability() {
+ return PARCELABLE_STABILITY_LOCAL;
}
/**
diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java
index c37a2ff1112c..181f94b39841 100644
--- a/core/java/android/os/ParcelableHolder.java
+++ b/core/java/android/os/ParcelableHolder.java
@@ -37,10 +37,10 @@ public final class ParcelableHolder implements Parcelable {
* if {@link ParcelableHolder} contains value, otherwise, both are null.
*/
private Parcel mParcel;
- private boolean mIsStable = false;
+ private @Parcelable.Stability int mStability = Parcelable.PARCELABLE_STABILITY_LOCAL;
- public ParcelableHolder(boolean isStable) {
- mIsStable = isStable;
+ public ParcelableHolder(@Parcelable.Stability int stability) {
+ mStability = stability;
}
private ParcelableHolder() {
@@ -50,11 +50,11 @@ public final class ParcelableHolder implements Parcelable {
/**
* {@link ParcelableHolder}'s stability is determined by the parcelable
* which contains this ParcelableHolder.
- * For more detail refer to {@link Parcelable#isStable}.
+ * For more detail refer to {@link Parcelable#getStability}.
*/
@Override
- public boolean isStable() {
- return mIsStable;
+ public @Parcelable.Stability int getStability() {
+ return mStability;
}
@NonNull
@@ -81,7 +81,8 @@ public final class ParcelableHolder implements Parcelable {
* @return {@code false} if the parcelable's stability is more unstable ParcelableHolder.
*/
public synchronized boolean setParcelable(@Nullable Parcelable p) {
- if (p != null && this.isStable() && !p.isStable()) {
+ // a ParcelableHolder can only hold things at its stability or higher
+ if (p != null && this.getStability() > p.getStability()) {
return false;
}
mParcelable = p;
@@ -123,7 +124,7 @@ public final class ParcelableHolder implements Parcelable {
* Read ParcelableHolder from a parcel.
*/
public synchronized void readFromParcel(@NonNull Parcel parcel) {
- this.mIsStable = parcel.readBoolean();
+ this.mStability = parcel.readInt();
mParcelable = null;
@@ -145,7 +146,7 @@ public final class ParcelableHolder implements Parcelable {
@Override
public synchronized void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeBoolean(this.mIsStable);
+ parcel.writeInt(this.mStability);
if (mParcel != null) {
parcel.writeInt(mParcel.dataSize());
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 8d65c92db52e..257bc5b64285 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1918,9 +1918,16 @@ public final class StrictMode {
}
private static class AndroidCloseGuardReporter implements CloseGuard.Reporter {
+
+ @Override
public void report(String message, Throwable allocationSite) {
onVmPolicyViolation(new LeakedClosableViolation(message, allocationSite));
}
+
+ @Override
+ public void report(String message) {
+ onVmPolicyViolation(new LeakedClosableViolation(message));
+ }
}
/** Called from Parcel.writeNoException() */
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
new file mode 100644
index 000000000000..f4645caee511
--- /dev/null
+++ b/core/java/android/os/TEST_MAPPING
@@ -0,0 +1,40 @@
+{
+ "presubmit": [
+ // TODO(159590499) add BugreportManagerTestCases
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "CtsBugreportTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ },
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "ShellTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "BugreportManagerTestCases"
+ },
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "CtsBugreportTestCases"
+ },
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "ShellTests"
+ }
+ ]
+}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 38ba47ab3701..ff1bcf6b3c5e 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -538,7 +538,7 @@ public final class UserHandle implements Parcelable {
UserHandle other = (UserHandle)obj;
return mHandle == other.mHandle;
}
- } catch (ClassCastException e) {
+ } catch (ClassCastException ignore) {
}
return false;
}
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index f4aa354a8574..8a7cf608cfb8 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -310,6 +310,7 @@ public final class VibrationAttributes implements Parcelable {
@Nullable VibrationEffect effect) {
mAudioAttributes = audio;
setUsage(audio);
+ setFlags(audio);
applyHapticFeedbackHeuristics(effect);
}
@@ -366,6 +367,12 @@ public final class VibrationAttributes implements Parcelable {
}
}
+ private void setFlags(@NonNull AudioAttributes audio) {
+ if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
+ mFlags |= FLAG_BYPASS_INTERRUPTION_POLICY;
+ }
+ }
+
/**
* Combines all of the attributes that have been set and returns a new
* {@link VibrationAttributes} object.
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index d35ce5b2c3f8..32c80e7069da 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -159,7 +159,7 @@ public class V4Signature {
*
* @param fileSize - size of the signed file (APK)
*/
- public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo,
+ public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo,
SigningInfo signingInfo) {
final int size =
4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 0abf8ae352af..6b5eb16d7bff 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -86,7 +86,6 @@ import android.system.Os;
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.DataUnit;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -161,11 +160,6 @@ public class StorageManager {
/** {@hide} */
public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot";
/** {@hide} */
- public static final String PROP_FUSE = "persist.sys.fuse";
- /** {@hide} */
- public static final String PROP_SETTINGS_FUSE = FeatureFlagUtils.PERSIST_PREFIX
- + FeatureFlagUtils.SETTINGS_FUSE_FLAG;
- /** {@hide} */
public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST =
"forced_scoped_storage_whitelist";
diff --git a/core/java/android/os/strictmode/LeakedClosableViolation.java b/core/java/android/os/strictmode/LeakedClosableViolation.java
index c795a6b89ec0..a2b02833afa0 100644
--- a/core/java/android/os/strictmode/LeakedClosableViolation.java
+++ b/core/java/android/os/strictmode/LeakedClosableViolation.java
@@ -21,4 +21,9 @@ public final class LeakedClosableViolation extends Violation {
super(message);
initCause(allocationSite);
}
+
+ /** @hide */
+ public LeakedClosableViolation(String message) {
+ super(message);
+ }
}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 235b0830b9aa..e23102113e9f 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -71,7 +71,7 @@ interface IPermissionManager {
void grantRuntimePermission(String packageName, String permName, int userId);
- void revokeRuntimePermission(String packageName, String permName, int userId);
+ void revokeRuntimePermission(String packageName, String permName, int userId, String reason);
void resetRuntimePermissions();
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index dea932dd6178..17a78a8f301e 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIE
import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
import static android.permission.PermissionControllerService.SERVICE_INTERFACE;
+import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
@@ -56,6 +57,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.RemoteStream;
import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.CollectionUtils;
import libcore.util.EmptyArray;
@@ -208,8 +210,15 @@ public final class PermissionControllerManager {
ServiceConnector<IPermissionController> remoteService = sRemoteServices.get(key);
if (remoteService == null) {
Intent intent = new Intent(SERVICE_INTERFACE);
- intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
+ String pkgName = context.getPackageManager().getPermissionControllerPackageName();
+ intent.setPackage(pkgName);
ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
+ if (serviceInfo == null) {
+ String errorMsg = "No PermissionController package (" + pkgName + ") for user "
+ + context.getUserId();
+ Log.wtf(TAG, errorMsg);
+ throw new IllegalStateException(errorMsg);
+ }
remoteService = new ServiceConnector.Impl<IPermissionController>(
ActivityThread.currentApplication() /* context */,
new Intent(SERVICE_INTERFACE)
@@ -490,8 +499,11 @@ public final class PermissionControllerManager {
*/
public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) {
try {
- mRemoteService.post(service -> service.asBinder().dump(fd, args))
- .get(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ mRemoteService.postAsync(service -> {
+ return AndroidFuture.runAsync(uncheckExceptions(() -> {
+ service.asBinder().dump(fd, args);
+ }), BackgroundThread.getExecutor());
+ }).get(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Log.e(TAG, "Could not get dump", e);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d44c7fd2a795..346522a504c8 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7614,6 +7614,8 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
/**
@@ -7621,6 +7623,8 @@ public final class Settings {
* Otherwise, the process will be silently killed.
* @hide
*/
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION =
"show_first_crash_dialog_dev_option";
@@ -7883,19 +7887,26 @@ public final class Settings {
public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
/**
+ * For user preference if swipe bottom to expand notification gesture enabled.
+ * @hide
+ */
+ public static final String SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED =
+ "swipe_bottom_to_notification_enabled";
+
+ /**
* For user preference if One-Handed Mode enabled.
* @hide
*/
public static final String ONE_HANDED_MODE_ENABLED = "one_handed_mode_enabled";
/**
- * For user perference if One-Handed Mode timeout.
+ * For user preference if One-Handed Mode timeout.
* @hide
*/
public static final String ONE_HANDED_MODE_TIMEOUT = "one_handed_mode_timeout";
/**
- * For user tapps app to exit One-Handed Mode.
+ * For user taps app to exit One-Handed Mode.
* @hide
*/
public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit";
@@ -8291,6 +8302,13 @@ public final class Settings {
public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
/**
+ * Whether the panic button (emergency sos) gesture should be enabled.
+ *
+ * @hide
+ */
+ public static final String PANIC_GESTURE_ENABLED = "panic_gesture_enabled";
+
+ /**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
*
@@ -11848,21 +11866,6 @@ public final class Settings {
"job_scheduler_quota_controller_constants";
/**
- * Job scheduler TimeController specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * "skip_not_ready_jobs=true5,other_key=2"
- *
- * <p>
- * Type: string
- *
- * @hide
- * @see com.android.server.job.JobSchedulerService.Constants
- */
- public static final String JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS =
- "job_scheduler_time_controller_constants";
-
- /**
* ShortcutManager specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
@@ -14181,6 +14184,8 @@ public final class Settings {
* Otherwise, the process will be silently killed.
* @hide
*/
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
/**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 4c9afcf5cd05..9f52142a5109 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1357,8 +1357,7 @@ public final class Telephony {
Object[] messages;
try {
messages = (Object[]) intent.getSerializableExtra("pdus");
- }
- catch (ClassCastException e) {
+ } catch (ClassCastException e) {
Rlog.e(TAG, "getMessagesFromIntent: " + e);
return null;
}
@@ -1370,9 +1369,12 @@ public final class Telephony {
String format = intent.getStringExtra("format");
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
- SubscriptionManager.getDefaultSmsSubscriptionId());
-
- Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId);
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Rlog.v(TAG, "getMessagesFromIntent with valid subId : " + subId);
+ } else {
+ Rlog.v(TAG, "getMessagesFromIntent");
+ }
int pduCount = messages.length;
SmsMessage[] msgs = new SmsMessage[pduCount];
@@ -3946,8 +3948,7 @@ public final class Telephony {
/**
* The APN set id. When the user manually selects an APN or the framework sets an APN as
- * preferred, all APNs with the same set id as the selected APN should be prioritized over
- * APNs in other sets.
+ * preferred, the device can only use APNs with the same set id as the selected APN.
* <p>Type: INTEGER</p>
* @hide
*/
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 08aa534be152..2d99c413cc89 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -312,7 +312,12 @@ public final class Dataset implements Parcelable {
* setting it to the {@link
* android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra. If you
* provide a dataset in the result, it will replace the authenticated dataset and
- * will be immediately filled in. If you provide a response, it will replace the
+ * will be immediately filled in. An exception to this behavior is if the original
+ * dataset represents a pinned inline suggestion (i.e. any of the field in the dataset
+ * has a pinned inline presentation, see {@link InlinePresentation#isPinned()}), then
+ * the original dataset will not be replaced,
+ * so that it can be triggered as a pending intent again.
+ * If you provide a response, it will replace the
* current response and the UI will be refreshed. For example, if you provided
* credit card information without the CVV for the data set in the {@link FillResponse
* response} then the returned data set should contain the CVV entry.
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index 9cf1b87f7eab..914169485979 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -50,7 +50,11 @@ public final class InlinePresentation implements Parcelable {
/**
* Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
- * host.
+ * host. However, it's eventually up to the host whether the UI is pinned or not.
+ *
+ * <p> Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+ * new data set returned from authentication intent. See
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
*/
private final boolean mPinned;
@@ -90,7 +94,11 @@ public final class InlinePresentation implements Parcelable {
* Specifies the UI specification for the inline suggestion.
* @param pinned
* Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
- * host.
+ * host. However, it's eventually up to the host whether the UI is pinned or not.
+ *
+ * <p> Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+ * new data set returned from authentication intent. See
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
*/
@DataClass.Generated.Member
public InlinePresentation(
@@ -126,7 +134,11 @@ public final class InlinePresentation implements Parcelable {
/**
* Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
- * host.
+ * host. However, it's eventually up to the host whether the UI is pinned or not.
+ *
+ * <p> Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+ * new data set returned from authentication intent. See
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
*/
@DataClass.Generated.Member
public boolean isPinned() {
@@ -232,7 +244,7 @@ public final class InlinePresentation implements Parcelable {
};
@DataClass.Generated(
- time = 1586992400667L,
+ time = 1593131904745L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 2677087a4c39..5d34c476a4a6 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1938,6 +1938,17 @@ public abstract class NotificationListenerService extends Service {
/**
* @hide
*/
+ public @NonNull Ranking withAudiblyAlertedInfo(@Nullable Ranking previous) {
+ if (previous != null && previous.mLastAudiblyAlertedMs > 0
+ && this.mLastAudiblyAlertedMs <= 0) {
+ this.mLastAudiblyAlertedMs = previous.mLastAudiblyAlertedMs;
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
public void populate(Ranking other) {
populate(other.mKey,
other.mRank,
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index e3cb382256ae..ab19fa9a1256 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -551,7 +551,7 @@ public class Html {
out.append(((ImageSpan) style[j]).getSource());
out.append("\">");
- // Don't output the dummy character underlying the image.
+ // Don't output the placeholder character underlying the image.
i = next;
}
if (style[j] instanceof AbsoluteSizeSpan) {
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 0863a813543c..38e3b39f8cfc 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -19,14 +19,13 @@ package android.text.format;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.icu.text.DateFormatSymbols;
+import android.icu.text.DateTimePatternGenerator;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
-import libcore.icu.ICU;
-import libcore.icu.LocaleData;
-
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
@@ -251,7 +250,8 @@ public class DateFormat {
* @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}.
*/
public static String getBestDateTimePattern(Locale locale, String skeleton) {
- return ICU.getBestDateTimePattern(skeleton, locale);
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale);
+ return dtpg.getBestPattern(skeleton);
}
/**
@@ -285,8 +285,10 @@ public class DateFormat {
*/
@UnsupportedAppUsage
public static String getTimeFormatString(Context context, int userHandle) {
- final LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
- return is24HourFormat(context, userHandle) ? d.timeFormat_Hm : d.timeFormat_hm;
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+ context.getResources().getConfiguration().locale);
+ return is24HourFormat(context, userHandle) ? dtpg.getBestPattern("Hm")
+ : dtpg.getBestPattern("hm");
}
/**
@@ -333,7 +335,52 @@ public class DateFormat {
* order returned here.
*/
public static char[] getDateFormatOrder(Context context) {
- return ICU.getDateFormatOrder(getDateFormatString(context));
+ return getDateFormatOrder(getDateFormatString(context));
+ }
+
+ /**
+ * @hide Used by internal framework class {@link android.widget.DatePickerSpinnerDelegate}.
+ */
+ public static char[] getDateFormatOrder(String pattern) {
+ char[] result = new char[3];
+ int resultIndex = 0;
+ boolean sawDay = false;
+ boolean sawMonth = false;
+ boolean sawYear = false;
+
+ for (int i = 0; i < pattern.length(); ++i) {
+ char ch = pattern.charAt(i);
+ if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') {
+ if (ch == 'd' && !sawDay) {
+ result[resultIndex++] = 'd';
+ sawDay = true;
+ } else if ((ch == 'L' || ch == 'M') && !sawMonth) {
+ result[resultIndex++] = 'M';
+ sawMonth = true;
+ } else if ((ch == 'y') && !sawYear) {
+ result[resultIndex++] = 'y';
+ sawYear = true;
+ }
+ } else if (ch == 'G') {
+ // Ignore the era specifier, if present.
+ } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
+ throw new IllegalArgumentException("Bad pattern character '" + ch + "' in "
+ + pattern);
+ } else if (ch == '\'') {
+ if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') {
+ ++i;
+ } else {
+ i = pattern.indexOf('\'', i + 1);
+ if (i == -1) {
+ throw new IllegalArgumentException("Bad quoting in " + pattern);
+ }
+ ++i;
+ }
+ } else {
+ // Ignore spaces and punctuation.
+ }
+ }
+ return result;
}
private static String getDateFormatString(Context context) {
@@ -428,7 +475,8 @@ public class DateFormat {
SpannableStringBuilder s = new SpannableStringBuilder(inFormat);
int count;
- LocaleData localeData = LocaleData.get(Locale.getDefault());
+ DateFormatSymbols dfs = getIcuDateFormatSymbols(Locale.getDefault());
+ String[] amPm = dfs.getAmPmStrings();
int len = inFormat.length();
@@ -450,14 +498,14 @@ public class DateFormat {
switch (c) {
case 'A':
case 'a':
- replacement = localeData.amPm[inDate.get(Calendar.AM_PM) - Calendar.AM];
+ replacement = amPm[inDate.get(Calendar.AM_PM) - Calendar.AM];
break;
case 'd':
replacement = zeroPad(inDate.get(Calendar.DATE), count);
break;
case 'c':
case 'E':
- replacement = getDayOfWeekString(localeData,
+ replacement = getDayOfWeekString(dfs,
inDate.get(Calendar.DAY_OF_WEEK), count, c);
break;
case 'K': // hour in am/pm (0-11)
@@ -485,8 +533,7 @@ public class DateFormat {
break;
case 'L':
case 'M':
- replacement = getMonthString(localeData,
- inDate.get(Calendar.MONTH), count, c);
+ replacement = getMonthString(dfs, inDate.get(Calendar.MONTH), count, c);
break;
case 'm':
replacement = zeroPad(inDate.get(Calendar.MINUTE), count);
@@ -519,25 +566,29 @@ public class DateFormat {
}
}
- private static String getDayOfWeekString(LocaleData ld, int day, int count, int kind) {
+ private static String getDayOfWeekString(DateFormatSymbols dfs, int day, int count, int kind) {
boolean standalone = (kind == 'c');
+ int context = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT;
+ final int width;
if (count == 5) {
- return standalone ? ld.tinyStandAloneWeekdayNames[day] : ld.tinyWeekdayNames[day];
+ width = DateFormatSymbols.NARROW;
} else if (count == 4) {
- return standalone ? ld.longStandAloneWeekdayNames[day] : ld.longWeekdayNames[day];
+ width = DateFormatSymbols.WIDE;
} else {
- return standalone ? ld.shortStandAloneWeekdayNames[day] : ld.shortWeekdayNames[day];
+ width = DateFormatSymbols.ABBREVIATED;
}
+ return dfs.getWeekdays(context, width)[day];
}
- private static String getMonthString(LocaleData ld, int month, int count, int kind) {
+ private static String getMonthString(DateFormatSymbols dfs, int month, int count, int kind) {
boolean standalone = (kind == 'L');
+ int monthContext = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT;
if (count == 5) {
- return standalone ? ld.tinyStandAloneMonthNames[month] : ld.tinyMonthNames[month];
+ return dfs.getMonths(monthContext, DateFormatSymbols.NARROW)[month];
} else if (count == 4) {
- return standalone ? ld.longStandAloneMonthNames[month] : ld.longMonthNames[month];
+ return dfs.getMonths(monthContext, DateFormatSymbols.WIDE)[month];
} else if (count == 3) {
- return standalone ? ld.shortStandAloneMonthNames[month] : ld.shortMonthNames[month];
+ return dfs.getMonths(monthContext, DateFormatSymbols.ABBREVIATED)[month];
} else {
// Calendar.JANUARY == 0, so add 1 to month.
return zeroPad(month+1, count);
@@ -632,4 +683,16 @@ public class DateFormat {
private static String zeroPad(int inValue, int inMinDigits) {
return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue);
}
+
+ /**
+ * We use Gregorian calendar for date formats in android.text.format and various UI widget
+ * historically. It's a utility method to get an {@link DateFormatSymbols} instance. Note that
+ * {@link DateFormatSymbols} has cache, and external cache is not needed unless same instance is
+ * requested repeatedly in the performance critical code.
+ *
+ * @hide
+ */
+ public static DateFormatSymbols getIcuDateFormatSymbols(Locale locale) {
+ return new DateFormatSymbols(android.icu.util.GregorianCalendar.class, locale);
+ }
}
diff --git a/core/java/android/text/format/DateIntervalFormat.java b/core/java/android/text/format/DateIntervalFormat.java
new file mode 100644
index 000000000000..de9ec7ab9ea9
--- /dev/null
+++ b/core/java/android/text/format/DateIntervalFormat.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
+import static android.text.format.DateUtils.FORMAT_UTC;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.icu.util.Calendar;
+import android.icu.util.ULocale;
+import android.util.LruCache;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.text.FieldPosition;
+import java.util.TimeZone;
+
+/**
+ * A wrapper of {@link android.icu.text.DateIntervalFormat} used by {@link DateUtilsBridge}.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public final class DateIntervalFormat {
+
+ private static final LruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS =
+ new LruCache<>(8);
+
+ private DateIntervalFormat() {
+ }
+
+ /**
+ * Format a date range.
+ */
+ @VisibleForTesting(visibility = PACKAGE)
+ public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) {
+ if ((flags & FORMAT_UTC) != 0) {
+ olsonId = "UTC";
+ }
+ // We create a java.util.TimeZone here to use libcore's data and libcore's olson ID /
+ // pseudo-tz logic.
+ TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault();
+ android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+ ULocale icuLocale = ULocale.getDefault();
+ return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags);
+ }
+
+ /**
+ * Format a date range. This is our slightly more sensible internal API.
+ * A truly sane replacement would take a skeleton instead of int flags.
+ */
+ @VisibleForTesting(visibility = PACKAGE)
+ public static String formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone,
+ long startMs, long endMs, int flags) {
+ Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs);
+ Calendar endCalendar;
+ if (startMs == endMs) {
+ endCalendar = startCalendar;
+ } else {
+ endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs);
+ }
+
+ // Special handling when the range ends at midnight:
+ // - If we're not showing times, and the range is non-empty, we fudge the end date so we
+ // don't count the day that's about to start.
+ // - If we are showing times, and the range ends at exactly 00:00 of the day following
+ // its start (which can be thought of as 24:00 the same day), we fudge the end date so we
+ // don't show the dates --- unless the start is anything displayed as 00:00, in which case
+ // we include both dates to disambiguate.
+ // This is not the behavior of icu4j's DateIntervalFormat, but it's the required behavior
+ // of Android's DateUtils.formatDateRange.
+ if (isExactlyMidnight(endCalendar)) {
+ boolean showTime = (flags & FORMAT_SHOW_TIME) == FORMAT_SHOW_TIME;
+ boolean endsDayAfterStart = DateUtilsBridge.dayDistance(startCalendar, endCalendar)
+ == 1;
+ if ((!showTime && startMs != endMs)
+ || (endsDayAfterStart
+ && !DateUtilsBridge.isDisplayMidnightUsingSkeleton(startCalendar))) {
+ endCalendar.add(Calendar.DAY_OF_MONTH, -1);
+ }
+ }
+
+ String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags);
+ synchronized (CACHED_FORMATTERS) {
+ android.icu.text.DateIntervalFormat formatter =
+ getFormatter(skeleton, icuLocale, icuTimeZone);
+ return formatter.format(startCalendar, endCalendar, new StringBuffer(),
+ new FieldPosition(0)).toString();
+ }
+ }
+
+ private static android.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale,
+ android.icu.util.TimeZone icuTimeZone) {
+ String key = skeleton + "\t" + locale + "\t" + icuTimeZone;
+ android.icu.text.DateIntervalFormat formatter = CACHED_FORMATTERS.get(key);
+ if (formatter != null) {
+ return formatter;
+ }
+ formatter = android.icu.text.DateIntervalFormat.getInstance(skeleton, locale);
+ formatter.setTimeZone(icuTimeZone);
+ CACHED_FORMATTERS.put(key, formatter);
+ return formatter;
+ }
+
+ private static boolean isExactlyMidnight(Calendar c) {
+ return c.get(Calendar.HOUR_OF_DAY) == 0
+ && c.get(Calendar.MINUTE) == 0
+ && c.get(Calendar.SECOND) == 0
+ && c.get(Calendar.MILLISECOND) == 0;
+ }
+}
diff --git a/core/java/android/text/format/DateTimeFormat.java b/core/java/android/text/format/DateTimeFormat.java
new file mode 100644
index 000000000000..064d7172c44f
--- /dev/null
+++ b/core/java/android/text/format/DateTimeFormat.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import android.icu.text.DateFormat;
+import android.icu.text.DateTimePatternGenerator;
+import android.icu.text.DisplayContext;
+import android.icu.text.SimpleDateFormat;
+import android.icu.util.Calendar;
+import android.icu.util.ULocale;
+import android.util.LruCache;
+
+/**
+ * A formatter that outputs a single date/time.
+ *
+ * @hide
+ */
+class DateTimeFormat {
+ private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
+
+ static class FormatterCache extends LruCache<String, DateFormat> {
+ FormatterCache() {
+ super(8);
+ }
+ }
+
+ private DateTimeFormat() {
+ }
+
+ public static String format(ULocale icuLocale, Calendar time, int flags,
+ DisplayContext displayContext) {
+ String skeleton = DateUtilsBridge.toSkeleton(time, flags);
+ String key = skeleton + "\t" + icuLocale + "\t" + time.getTimeZone();
+ synchronized (CACHED_FORMATTERS) {
+ DateFormat formatter = CACHED_FORMATTERS.get(key);
+ if (formatter == null) {
+ DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(
+ icuLocale);
+ formatter = new SimpleDateFormat(generator.getBestPattern(skeleton), icuLocale);
+ CACHED_FORMATTERS.put(key, formatter);
+ }
+ formatter.setContext(displayContext);
+ return formatter.format(time);
+ }
+ }
+}
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index ce676e0908cd..ff08269a93a4 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -20,6 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.icu.text.DateFormatSymbols;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.util.Measure;
@@ -27,10 +28,6 @@ import android.icu.util.MeasureUnit;
import com.android.internal.R;
-import libcore.icu.DateIntervalFormat;
-import libcore.icu.LocaleData;
-import libcore.icu.RelativeDateTimeFormatter;
-
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -205,17 +202,23 @@ public class DateUtils
*/
@Deprecated
public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
- LocaleData d = LocaleData.get(Locale.getDefault());
- String[] names;
+ DateFormatSymbols dfs = DateFormatSymbols.getInstance();
+ final int width;
switch (abbrev) {
- case LENGTH_LONG: names = d.longWeekdayNames; break;
- case LENGTH_MEDIUM: names = d.shortWeekdayNames; break;
- case LENGTH_SHORT: names = d.shortWeekdayNames; break; // TODO
- case LENGTH_SHORTER: names = d.shortWeekdayNames; break; // TODO
- case LENGTH_SHORTEST: names = d.tinyWeekdayNames; break;
- default: names = d.shortWeekdayNames; break;
+ case LENGTH_LONG:
+ width = DateFormatSymbols.WIDE;
+ break;
+ case LENGTH_SHORTEST:
+ width = DateFormatSymbols.NARROW;
+ break;
+ case LENGTH_MEDIUM:
+ case LENGTH_SHORT: // TODO
+ case LENGTH_SHORTER: // TODO
+ default:
+ width = DateFormatSymbols.ABBREVIATED;
+ break;
}
- return names[dayOfWeek];
+ return dfs.getWeekdays(DateFormatSymbols.FORMAT, width)[dayOfWeek];
}
/**
@@ -227,7 +230,8 @@ public class DateUtils
*/
@Deprecated
public static String getAMPMString(int ampm) {
- return LocaleData.get(Locale.getDefault()).amPm[ampm - Calendar.AM];
+ String[] amPm = DateFormat.getIcuDateFormatSymbols(Locale.getDefault()).getAmPmStrings();
+ return amPm[ampm - Calendar.AM];
}
/**
@@ -243,17 +247,23 @@ public class DateUtils
*/
@Deprecated
public static String getMonthString(int month, int abbrev) {
- LocaleData d = LocaleData.get(Locale.getDefault());
- String[] names;
+ DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.getDefault());
+ final int width;
switch (abbrev) {
- case LENGTH_LONG: names = d.longMonthNames; break;
- case LENGTH_MEDIUM: names = d.shortMonthNames; break;
- case LENGTH_SHORT: names = d.shortMonthNames; break;
- case LENGTH_SHORTER: names = d.shortMonthNames; break;
- case LENGTH_SHORTEST: names = d.tinyMonthNames; break;
- default: names = d.shortMonthNames; break;
+ case LENGTH_LONG:
+ width = DateFormatSymbols.WIDE;
+ break;
+ case LENGTH_SHORTEST:
+ width = DateFormatSymbols.NARROW;
+ break;
+ case LENGTH_MEDIUM:
+ case LENGTH_SHORT:
+ case LENGTH_SHORTER:
+ default:
+ width = DateFormatSymbols.ABBREVIATED;
+ break;
}
- return names[month];
+ return dfs.getMonths(DateFormatSymbols.FORMAT, width)[month];
}
/**
diff --git a/core/java/android/text/format/DateUtilsBridge.java b/core/java/android/text/format/DateUtilsBridge.java
new file mode 100644
index 000000000000..92ec9cf6d736
--- /dev/null
+++ b/core/java/android/text/format/DateUtilsBridge.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import static android.text.format.DateUtils.FORMAT_12HOUR;
+import static android.text.format.DateUtils.FORMAT_24HOUR;
+import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
+import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY;
+import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY;
+import static android.text.format.DateUtils.FORMAT_NO_YEAR;
+import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
+import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY;
+import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.icu.util.ULocale;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Common methods and constants for the various ICU formatters used to support {@link
+ * android.text.format.DateUtils}.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public final class DateUtilsBridge {
+
+ /**
+ * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time
+ * of writing the libcore implementation is faster but restricted to 1902 - 2038. Callers must
+ * not modify the {@code tz} after calling this method.
+ */
+ public static TimeZone icuTimeZone(java.util.TimeZone tz) {
+ TimeZone icuTimeZone = TimeZone.getTimeZone(tz.getID());
+ icuTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply.
+ return icuTimeZone;
+ }
+
+ /**
+ * Create a GregorianCalendar based on the arguments
+ */
+ public static Calendar createIcuCalendar(TimeZone icuTimeZone, ULocale icuLocale,
+ long timeInMillis) {
+ Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale);
+ calendar.setTimeInMillis(timeInMillis);
+ return calendar;
+ }
+
+ public static String toSkeleton(Calendar calendar, int flags) {
+ return toSkeleton(calendar, calendar, flags);
+ }
+
+ public static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) {
+ if ((flags & FORMAT_ABBREV_ALL) != 0) {
+ flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY;
+ }
+
+ String monthPart = "MMMM";
+ if ((flags & FORMAT_NUMERIC_DATE) != 0) {
+ monthPart = "M";
+ } else if ((flags & FORMAT_ABBREV_MONTH) != 0) {
+ monthPart = "MMM";
+ }
+
+ String weekPart = "EEEE";
+ if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) {
+ weekPart = "EEE";
+ }
+
+ String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale.
+ if ((flags & FORMAT_24HOUR) != 0) {
+ timePart = "H";
+ } else if ((flags & FORMAT_12HOUR) != 0) {
+ timePart = "h";
+ }
+
+ // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it
+ // never makes sense to leave out the minutes), include minutes. This gets us times like
+ // "4 PM" while avoiding times like "16" (for "16:00").
+ if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) {
+ timePart += "m";
+ } else {
+ // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes
+ // if they're not both "00".
+ if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) {
+ timePart = timePart + "m";
+ }
+ }
+
+ if (fallOnDifferentDates(startCalendar, endCalendar)) {
+ flags |= FORMAT_SHOW_DATE;
+ }
+
+ if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) {
+ flags &= (~FORMAT_SHOW_WEEKDAY);
+ flags &= (~FORMAT_SHOW_TIME);
+ }
+
+ if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) {
+ flags |= FORMAT_SHOW_DATE;
+ }
+
+ // If we've been asked to show the date, work out whether we think we should show the year.
+ if ((flags & FORMAT_SHOW_DATE) != 0) {
+ if ((flags & FORMAT_SHOW_YEAR) != 0) {
+ // The caller explicitly wants us to show the year.
+ } else if ((flags & FORMAT_NO_YEAR) != 0) {
+ // The caller explicitly doesn't want us to show the year, even if we otherwise
+ // would.
+ } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) {
+ flags |= FORMAT_SHOW_YEAR;
+ }
+ }
+
+ StringBuilder builder = new StringBuilder();
+ if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) {
+ if ((flags & FORMAT_SHOW_YEAR) != 0) {
+ builder.append("y");
+ }
+ builder.append(monthPart);
+ if ((flags & FORMAT_NO_MONTH_DAY) == 0) {
+ builder.append("d");
+ }
+ }
+ if ((flags & FORMAT_SHOW_WEEKDAY) != 0) {
+ builder.append(weekPart);
+ }
+ if ((flags & FORMAT_SHOW_TIME) != 0) {
+ builder.append(timePart);
+ }
+ return builder.toString();
+ }
+
+ public static int dayDistance(Calendar c1, Calendar c2) {
+ return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY);
+ }
+
+ /**
+ * Returns whether the argument will be displayed as if it were midnight, using any of the
+ * skeletons provided by {@link #toSkeleton}.
+ */
+ public static boolean isDisplayMidnightUsingSkeleton(Calendar c) {
+ // All the skeletons returned by toSkeleton have minute precision (they may abbreviate
+ // 4:00 PM to 4 PM but will still show the following minute as 4:01 PM).
+ return c.get(Calendar.HOUR_OF_DAY) == 0 && c.get(Calendar.MINUTE) == 0;
+ }
+
+ private static boolean onTheHour(Calendar c) {
+ return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
+ }
+
+ private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) {
+ return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR)
+ || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH)
+ || c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
+ }
+
+ private static boolean fallInSameMonth(Calendar c1, Calendar c2) {
+ return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
+ }
+
+ private static boolean fallInSameYear(Calendar c1, Calendar c2) {
+ return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR);
+ }
+
+ private static boolean isThisYear(Calendar c) {
+ Calendar now = (Calendar) c.clone();
+ now.setTimeInMillis(System.currentTimeMillis());
+ return c.get(Calendar.YEAR) == now.get(Calendar.YEAR);
+ }
+}
diff --git a/core/java/android/text/format/OWNERS b/core/java/android/text/format/OWNERS
new file mode 100644
index 000000000000..32adc12bb901
--- /dev/null
+++ b/core/java/android/text/format/OWNERS
@@ -0,0 +1,3 @@
+# Inherits OWNERS from parent directory, plus the following
+
+vichang@google.com
diff --git a/core/java/android/text/format/RelativeDateTimeFormatter.java b/core/java/android/text/format/RelativeDateTimeFormatter.java
new file mode 100644
index 000000000000..9096469699c1
--- /dev/null
+++ b/core/java/android/text/format/RelativeDateTimeFormatter.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
+import static android.text.format.DateUtils.FORMAT_NO_YEAR;
+import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
+import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.icu.text.DisplayContext;
+import android.icu.util.Calendar;
+import android.icu.util.ULocale;
+import android.util.LruCache;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Locale;
+
+/**
+ * Exposes icu4j's RelativeDateTimeFormatter.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public final class RelativeDateTimeFormatter {
+
+ public static final long SECOND_IN_MILLIS = 1000;
+ public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
+ public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
+ public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
+ public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
+ // YEAR_IN_MILLIS considers 364 days as a year. However, since this
+ // constant comes from public API in DateUtils, it cannot be fixed here.
+ public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52;
+
+ private static final int DAY_IN_MS = 24 * 60 * 60 * 1000;
+ private static final int EPOCH_JULIAN_DAY = 2440588;
+
+ private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
+
+ static class FormatterCache
+ extends LruCache<String, android.icu.text.RelativeDateTimeFormatter> {
+ FormatterCache() {
+ super(8);
+ }
+ }
+
+ private RelativeDateTimeFormatter() {
+ }
+
+ /**
+ * This is the internal API that implements the functionality of DateUtils
+ * .getRelativeTimeSpanString(long,
+ * long, long, int), which is to return a string describing 'time' as a time relative to 'now'
+ * such as '5 minutes ago', or 'In 2 days'. More examples can be found in DateUtils' doc.
+ * <p>
+ * In the implementation below, it selects the appropriate time unit based on the elapsed time
+ * between time' and 'now', e.g. minutes, days and etc. Callers may also specify the desired
+ * minimum resolution to show in the result. For example, '45 minutes ago' will become '0 hours
+ * ago' when minResolution is HOUR_IN_MILLIS. Once getting the quantity and unit to display, it
+ * calls icu4j's RelativeDateTimeFormatter to format the actual string according to the given
+ * locale.
+ * <p>
+ * Note that when minResolution is set to DAY_IN_MILLIS, it returns the result depending on the
+ * actual date difference. For example, it will return 'Yesterday' even if 'time' was less than
+ * 24 hours ago but falling onto a different calendar day.
+ * <p>
+ * It takes two additional parameters of Locale and TimeZone than the DateUtils' API. Caller
+ * must specify the locale and timezone. FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set
+ * in 'flags' to get the abbreviated forms when available. When 'time' equals to 'now', it
+ * always // returns a string like '0 seconds/minutes/... ago' according to minResolution.
+ */
+ public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
+ long now, long minResolution, int flags) {
+ // Android has been inconsistent about capitalization in the past. e.g. bug
+ // http://b/20247811.
+ // Now we capitalize everything consistently.
+ final DisplayContext displayContext =
+ DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
+ return getRelativeTimeSpanString(locale, tz, time, now, minResolution, flags,
+ displayContext);
+ }
+
+ public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
+ long now, long minResolution, int flags, DisplayContext displayContext) {
+ if (locale == null) {
+ throw new NullPointerException("locale == null");
+ }
+ if (tz == null) {
+ throw new NullPointerException("tz == null");
+ }
+ ULocale icuLocale = ULocale.forLocale(locale);
+ android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+ return getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution, flags,
+ displayContext);
+ }
+
+ private static String getRelativeTimeSpanString(ULocale icuLocale,
+ android.icu.util.TimeZone icuTimeZone, long time, long now, long minResolution,
+ int flags,
+ DisplayContext displayContext) {
+
+ long duration = Math.abs(now - time);
+ boolean past = (now >= time);
+
+ android.icu.text.RelativeDateTimeFormatter.Style style;
+ if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
+ style = android.icu.text.RelativeDateTimeFormatter.Style.SHORT;
+ } else {
+ style = android.icu.text.RelativeDateTimeFormatter.Style.LONG;
+ }
+
+ android.icu.text.RelativeDateTimeFormatter.Direction direction;
+ if (past) {
+ direction = android.icu.text.RelativeDateTimeFormatter.Direction.LAST;
+ } else {
+ direction = android.icu.text.RelativeDateTimeFormatter.Direction.NEXT;
+ }
+
+ // 'relative' defaults to true as we are generating relative time span
+ // string. It will be set to false when we try to display strings without
+ // a quantity, such as 'Yesterday', etc.
+ boolean relative = true;
+ int count;
+ android.icu.text.RelativeDateTimeFormatter.RelativeUnit unit;
+ android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit aunit = null;
+
+ if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) {
+ count = (int) (duration / SECOND_IN_MILLIS);
+ unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.SECONDS;
+ } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) {
+ count = (int) (duration / MINUTE_IN_MILLIS);
+ unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.MINUTES;
+ } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) {
+ // Even if 'time' actually happened yesterday, we don't format it as
+ // "Yesterday" in this case. Unless the duration is longer than a day,
+ // or minResolution is specified as DAY_IN_MILLIS by user.
+ count = (int) (duration / HOUR_IN_MILLIS);
+ unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.HOURS;
+ } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
+ count = Math.abs(dayDistance(icuTimeZone, time, now));
+ unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.DAYS;
+
+ if (count == 2) {
+ // Some locales have special terms for "2 days ago". Return them if
+ // available. Note that we cannot set up direction and unit here and
+ // make it fall through to use the call near the end of the function,
+ // because for locales that don't have special terms for "2 days ago",
+ // icu4j returns an empty string instead of falling back to strings
+ // like "2 days ago".
+ String str;
+ if (past) {
+ synchronized (CACHED_FORMATTERS) {
+ str = getFormatter(icuLocale, style, displayContext).format(
+ android.icu.text.RelativeDateTimeFormatter.Direction.LAST_2,
+ android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
+ }
+ } else {
+ synchronized (CACHED_FORMATTERS) {
+ str = getFormatter(icuLocale, style, displayContext).format(
+ android.icu.text.RelativeDateTimeFormatter.Direction.NEXT_2,
+ android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
+ }
+ }
+ if (str != null && !str.isEmpty()) {
+ return str;
+ }
+ // Fall back to show something like "2 days ago".
+ } else if (count == 1) {
+ // Show "Yesterday / Tomorrow" instead of "1 day ago / In 1 day".
+ aunit = android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
+ relative = false;
+ } else if (count == 0) {
+ // Show "Today" if time and now are on the same day.
+ aunit = android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
+ direction = android.icu.text.RelativeDateTimeFormatter.Direction.THIS;
+ relative = false;
+ }
+ } else if (minResolution == WEEK_IN_MILLIS) {
+ count = (int) (duration / WEEK_IN_MILLIS);
+ unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.WEEKS;
+ } else {
+ Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
+ // The duration is longer than a week and minResolution is not
+ // WEEK_IN_MILLIS. Return the absolute date instead of relative time.
+
+ // Bug 19822016:
+ // If user doesn't supply the year display flag, we need to explicitly
+ // set that to show / hide the year based on time and now. Otherwise
+ // formatDateRange() would determine that based on the current system
+ // time and may give wrong results.
+ if ((flags & (FORMAT_NO_YEAR | FORMAT_SHOW_YEAR)) == 0) {
+ Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale,
+ now);
+
+ if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+ flags |= FORMAT_SHOW_YEAR;
+ } else {
+ flags |= FORMAT_NO_YEAR;
+ }
+ }
+ return DateTimeFormat.format(icuLocale, timeCalendar, flags, displayContext);
+ }
+
+ synchronized (CACHED_FORMATTERS) {
+ android.icu.text.RelativeDateTimeFormatter formatter =
+ getFormatter(icuLocale, style, displayContext);
+ if (relative) {
+ return formatter.format(count, direction, unit);
+ } else {
+ return formatter.format(direction, aunit);
+ }
+ }
+ }
+
+ /**
+ * This is the internal API that implements DateUtils.getRelativeDateTimeString(long, long,
+ * long, long, int), which is to return a string describing 'time' as a time relative to 'now',
+ * formatted like '[relative time/date], [time]'. More examples can be found in DateUtils' doc.
+ * <p>
+ * The function is similar to getRelativeTimeSpanString, but it always appends the absolute time
+ * to the relative time string to return '[relative time/date clause], [absolute time clause]'.
+ * It also takes an extra parameter transitionResolution to determine the format of the date
+ * clause. When the elapsed time is less than the transition resolution, it displays the
+ * relative time string. Otherwise, it gives the absolute numeric date string as the date
+ * clause. With the date and time clauses, it relies on icu4j's
+ * RelativeDateTimeFormatter::combineDateAndTime()
+ * to concatenate the two.
+ * <p>
+ * It takes two additional parameters of Locale and TimeZone than the DateUtils' API. Caller
+ * must specify the locale and timezone. FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set
+ * in 'flags' to get the abbreviated forms when they are available.
+ * <p>
+ * Bug 5252772: Since the absolute time will always be part of the result, minResolution will be
+ * set to at least DAY_IN_MILLIS to correctly indicate the date difference. For example, when
+ * it's 1:30 AM, it will return 'Yesterday, 11:30 PM' for getRelativeDateTimeString(null, null,
+ * now - 2 hours, now, HOUR_IN_MILLIS, DAY_IN_MILLIS, 0), instead of '2 hours ago, 11:30 PM'
+ * even with minResolution being HOUR_IN_MILLIS.
+ */
+ public static String getRelativeDateTimeString(Locale locale, java.util.TimeZone tz, long time,
+ long now, long minResolution, long transitionResolution, int flags) {
+
+ if (locale == null) {
+ throw new NullPointerException("locale == null");
+ }
+ if (tz == null) {
+ throw new NullPointerException("tz == null");
+ }
+ ULocale icuLocale = ULocale.forLocale(locale);
+ android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+
+ long duration = Math.abs(now - time);
+ // It doesn't make much sense to have results like: "1 week ago, 10:50 AM".
+ if (transitionResolution > WEEK_IN_MILLIS) {
+ transitionResolution = WEEK_IN_MILLIS;
+ }
+ android.icu.text.RelativeDateTimeFormatter.Style style;
+ if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
+ style = android.icu.text.RelativeDateTimeFormatter.Style.SHORT;
+ } else {
+ style = android.icu.text.RelativeDateTimeFormatter.Style.LONG;
+ }
+
+ Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
+ Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now);
+
+ int days = Math.abs(DateUtilsBridge.dayDistance(timeCalendar, nowCalendar));
+
+ // Now get the date clause, either in relative format or the actual date.
+ String dateClause;
+ if (duration < transitionResolution) {
+ // This is to fix bug 5252772. If there is any date difference, we should
+ // promote the minResolution to DAY_IN_MILLIS so that it can display the
+ // date instead of "x hours/minutes ago, [time]".
+ if (days > 0 && minResolution < DAY_IN_MILLIS) {
+ minResolution = DAY_IN_MILLIS;
+ }
+ dateClause = getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution,
+ flags, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+ } else {
+ // We always use fixed flags to format the date clause. User-supplied
+ // flags are ignored.
+ if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+ // Different years
+ flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
+ } else {
+ // Default
+ flags = FORMAT_SHOW_DATE | FORMAT_NO_YEAR | FORMAT_ABBREV_MONTH;
+ }
+
+ dateClause = DateTimeFormat.format(icuLocale, timeCalendar, flags,
+ DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+ }
+
+ String timeClause = DateTimeFormat.format(icuLocale, timeCalendar, FORMAT_SHOW_TIME,
+ DisplayContext.CAPITALIZATION_NONE);
+
+ // icu4j also has other options available to control the capitalization. We are currently
+ // using
+ // the _NONE option only.
+ DisplayContext capitalizationContext = DisplayContext.CAPITALIZATION_NONE;
+
+ // Combine the two clauses, such as '5 days ago, 10:50 AM'.
+ synchronized (CACHED_FORMATTERS) {
+ return getFormatter(icuLocale, style, capitalizationContext)
+ .combineDateAndTime(dateClause, timeClause);
+ }
+ }
+
+ /**
+ * getFormatter() caches the RelativeDateTimeFormatter instances based on the combination of
+ * localeName, sytle and capitalizationContext. It should always be used along with the action
+ * of the formatter in a synchronized block, because otherwise the formatter returned by
+ * getFormatter() may have been evicted by the time of the call to formatter->action().
+ */
+ private static android.icu.text.RelativeDateTimeFormatter getFormatter(
+ ULocale locale, android.icu.text.RelativeDateTimeFormatter.Style style,
+ DisplayContext displayContext) {
+ String key = locale + "\t" + style + "\t" + displayContext;
+ android.icu.text.RelativeDateTimeFormatter formatter = CACHED_FORMATTERS.get(key);
+ if (formatter == null) {
+ formatter = android.icu.text.RelativeDateTimeFormatter.getInstance(
+ locale, null, style, displayContext);
+ CACHED_FORMATTERS.put(key, formatter);
+ }
+ return formatter;
+ }
+
+ // Return the date difference for the two times in a given timezone.
+ private static int dayDistance(android.icu.util.TimeZone icuTimeZone, long startTime,
+ long endTime) {
+ return julianDay(icuTimeZone, endTime) - julianDay(icuTimeZone, startTime);
+ }
+
+ private static int julianDay(android.icu.util.TimeZone icuTimeZone, long time) {
+ long utcMs = time + icuTimeZone.getOffset(time);
+ return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY;
+ }
+}
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index f1a8565efde1..5b5c8548f281 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -18,8 +18,8 @@ package android.text.format;
import android.util.TimeFormatException;
-import libcore.timezone.ZoneInfoDb;
-import libcore.util.ZoneInfo;
+import com.android.i18n.timezone.ZoneInfoData;
+import com.android.i18n.timezone.ZoneInfoDb;
import java.util.Locale;
import java.util.TimeZone;
@@ -361,7 +361,7 @@ public class Time {
*/
@Override
public String toString() {
- // toString() uses its own TimeCalculator rather than the shared one. Otherwise crazy stuff
+ // toString() uses its own TimeCalculator rather than the shared one. Otherwise weird stuff
// happens during debugging when the debugger calls toString().
TimeCalculator calculator = new TimeCalculator(this.timezone);
calculator.copyFieldsFromTime(this);
@@ -1070,15 +1070,15 @@ public class Time {
* to the enclosing object, but others do not: thus separate state is retained.
*/
private static class TimeCalculator {
- public final ZoneInfo.WallTime wallTime;
+ public final ZoneInfoData.WallTime wallTime;
public String timezone;
// Information about the current timezone.
- private ZoneInfo zoneInfo;
+ private ZoneInfoData mZoneInfoData;
public TimeCalculator(String timezoneId) {
- this.zoneInfo = lookupZoneInfo(timezoneId);
- this.wallTime = new ZoneInfo.WallTime();
+ this.mZoneInfoData = lookupZoneInfoData(timezoneId);
+ this.wallTime = new ZoneInfoData.WallTime();
}
public long toMillis(boolean ignoreDst) {
@@ -1086,7 +1086,7 @@ public class Time {
wallTime.setIsDst(-1);
}
- int r = wallTime.mktime(zoneInfo);
+ int r = wallTime.mktime(mZoneInfoData);
if (r == -1) {
return -1;
}
@@ -1098,7 +1098,7 @@ public class Time {
int intSeconds = (int) (millis / 1000);
updateZoneInfoFromTimeZone();
- wallTime.localtime(intSeconds, zoneInfo);
+ wallTime.localtime(intSeconds, mZoneInfoData);
}
public String format(String format) {
@@ -1106,31 +1106,31 @@ public class Time {
format = "%c";
}
TimeFormatter formatter = new TimeFormatter();
- return formatter.format(format, wallTime, zoneInfo);
+ return formatter.format(format, wallTime, mZoneInfoData);
}
private void updateZoneInfoFromTimeZone() {
- if (!zoneInfo.getID().equals(timezone)) {
- this.zoneInfo = lookupZoneInfo(timezone);
+ if (!mZoneInfoData.getID().equals(timezone)) {
+ this.mZoneInfoData = lookupZoneInfoData(timezone);
}
}
- private static ZoneInfo lookupZoneInfo(String timezoneId) {
- ZoneInfo zoneInfo = ZoneInfoDb.getInstance().makeTimeZone(timezoneId);
- if (zoneInfo == null) {
- zoneInfo = ZoneInfoDb.getInstance().makeTimeZone("GMT");
+ private static ZoneInfoData lookupZoneInfoData(String timezoneId) {
+ ZoneInfoData zoneInfoData = ZoneInfoDb.getInstance().makeZoneInfoData(timezoneId);
+ if (zoneInfoData == null) {
+ zoneInfoData = ZoneInfoDb.getInstance().makeZoneInfoData("GMT");
}
- if (zoneInfo == null) {
+ if (zoneInfoData == null) {
throw new AssertionError("GMT not found: \"" + timezoneId + "\"");
}
- return zoneInfo;
+ return zoneInfoData;
}
public void switchTimeZone(String timezone) {
- int seconds = wallTime.mktime(zoneInfo);
+ int seconds = wallTime.mktime(mZoneInfoData);
this.timezone = timezone;
updateZoneInfoFromTimeZone();
- wallTime.localtime(seconds, zoneInfo);
+ wallTime.localtime(seconds, mZoneInfoData);
}
public String format2445(boolean hasTime) {
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
index f7fd89d7d819..c71dfbbafd40 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -21,9 +21,10 @@
package android.text.format;
import android.content.res.Resources;
+import android.icu.text.DateFormatSymbols;
+import android.icu.text.DecimalFormatSymbols;
-import libcore.icu.LocaleData;
-import libcore.util.ZoneInfo;
+import com.android.i18n.timezone.ZoneInfoData;
import java.nio.CharBuffer;
import java.time.Instant;
@@ -51,15 +52,17 @@ class TimeFormatter {
private static final int DAYSPERNYEAR = 365;
/**
- * The Locale for which the cached LocaleData and formats have been loaded.
+ * The Locale for which the cached symbols and formats have been loaded.
*/
private static Locale sLocale;
- private static LocaleData sLocaleData;
+ private static DateFormatSymbols sDateFormatSymbols;
+ private static DecimalFormatSymbols sDecimalFormatSymbols;
private static String sTimeOnlyFormat;
private static String sDateOnlyFormat;
private static String sDateTimeFormat;
- private final LocaleData localeData;
+ private final DateFormatSymbols dateFormatSymbols;
+ private final DecimalFormatSymbols decimalFormatSymbols;
private final String dateTimeFormat;
private final String timeOnlyFormat;
private final String dateOnlyFormat;
@@ -73,7 +76,8 @@ class TimeFormatter {
if (sLocale == null || !(locale.equals(sLocale))) {
sLocale = locale;
- sLocaleData = LocaleData.get(locale);
+ sDateFormatSymbols = DateFormat.getIcuDateFormatSymbols(locale);
+ sDecimalFormatSymbols = DecimalFormatSymbols.getInstance(locale);
Resources r = Resources.getSystem();
sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
@@ -81,10 +85,11 @@ class TimeFormatter {
sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
}
+ this.dateFormatSymbols = sDateFormatSymbols;
+ this.decimalFormatSymbols = sDecimalFormatSymbols;
this.dateTimeFormat = sDateTimeFormat;
this.timeOnlyFormat = sTimeOnlyFormat;
this.dateOnlyFormat = sDateOnlyFormat;
- localeData = sLocaleData;
}
}
@@ -94,8 +99,8 @@ class TimeFormatter {
* incorrect digit localization behavior.
*/
String formatMillisWithFixedFormat(long timeMillis) {
- // This method is deliberately not a general purpose replacement for
- // format(String, ZoneInfo.WallTime, ZoneInfo): It hard-codes the pattern used; many of the
+ // This method is deliberately not a general purpose replacement for format(String,
+ // ZoneInfoData.WallTime, ZoneInfoData): It hard-codes the pattern used; many of the
// pattern characters supported by Time.format() have unusual behavior which would make
// using java.time.format or similar packages difficult. It would be a lot of work to share
// behavior and many internal Android usecases can be covered by this common pattern
@@ -144,7 +149,8 @@ class TimeFormatter {
/**
* Format the specified {@code wallTime} using {@code pattern}. The output is returned.
*/
- public String format(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
+ public String format(String pattern, ZoneInfoData.WallTime wallTime,
+ ZoneInfoData zoneInfoData) {
try {
StringBuilder stringBuilder = new StringBuilder();
@@ -153,7 +159,7 @@ class TimeFormatter {
// and locale sensitive strings are output directly using outputBuilder.
numberFormatter = new Formatter(stringBuilder, Locale.US);
- formatInternal(pattern, wallTime, zoneInfo);
+ formatInternal(pattern, wallTime, zoneInfoData);
String result = stringBuilder.toString();
// The localizeDigits() behavior is the source of a bug since some formats are defined
// as being in ASCII and not localized.
@@ -165,12 +171,12 @@ class TimeFormatter {
}
private String localizeDigits(String s) {
- if (localeData.zeroDigit == '0') {
+ if (decimalFormatSymbols.getZeroDigit() == '0') {
return s;
}
int length = s.length();
- int offsetToLocalizedDigits = localeData.zeroDigit - '0';
+ int offsetToLocalizedDigits = decimalFormatSymbols.getZeroDigit() - '0';
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; ++i) {
char ch = s.charAt(i);
@@ -186,13 +192,14 @@ class TimeFormatter {
* Format the specified {@code wallTime} using {@code pattern}. The output is written to
* {@link #outputBuilder}.
*/
- private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
+ private void formatInternal(String pattern, ZoneInfoData.WallTime wallTime,
+ ZoneInfoData zoneInfoData) {
CharBuffer formatBuffer = CharBuffer.wrap(pattern);
while (formatBuffer.remaining() > 0) {
boolean outputCurrentChar = true;
char currentChar = formatBuffer.get(formatBuffer.position());
if (currentChar == '%') {
- outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfo);
+ outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfoData);
}
if (outputCurrentChar) {
outputBuilder.append(formatBuffer.get(formatBuffer.position()));
@@ -201,8 +208,8 @@ class TimeFormatter {
}
}
- private boolean handleToken(CharBuffer formatBuffer, ZoneInfo.WallTime wallTime,
- ZoneInfo zoneInfo) {
+ private boolean handleToken(CharBuffer formatBuffer, ZoneInfoData.WallTime wallTime,
+ ZoneInfoData zoneInfoData) {
// The char at formatBuffer.position() is expected to be '%' at this point.
int modifier = 0;
@@ -212,45 +219,54 @@ class TimeFormatter {
char currentChar = formatBuffer.get(formatBuffer.position());
switch (currentChar) {
case 'A':
- modifyAndAppend((wallTime.getWeekDay() < 0
- || wallTime.getWeekDay() >= DAYSPERWEEK)
- ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1],
+ modifyAndAppend(
+ (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?"
+ : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.WIDE)[wallTime.getWeekDay() + 1],
modifier);
return false;
case 'a':
- modifyAndAppend((wallTime.getWeekDay() < 0
- || wallTime.getWeekDay() >= DAYSPERWEEK)
- ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1],
+ modifyAndAppend(
+ (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?"
+ : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.ABBREVIATED)[wallTime.getWeekDay() + 1],
modifier);
return false;
case 'B':
if (modifier == '-') {
- modifyAndAppend((wallTime.getMonth() < 0
- || wallTime.getMonth() >= MONSPERYEAR)
- ? "?"
- : localeData.longStandAloneMonthNames[wallTime.getMonth()],
+ modifyAndAppend(
+ (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?"
+ : dateFormatSymbols.getMonths(DateFormatSymbols.STANDALONE,
+ DateFormatSymbols.WIDE)[wallTime.getMonth()],
modifier);
} else {
- modifyAndAppend((wallTime.getMonth() < 0
- || wallTime.getMonth() >= MONSPERYEAR)
- ? "?" : localeData.longMonthNames[wallTime.getMonth()],
+ modifyAndAppend(
+ (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?"
+ : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.WIDE)[wallTime.getMonth()],
modifier);
}
return false;
case 'b':
case 'h':
modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
- ? "?" : localeData.shortMonthNames[wallTime.getMonth()],
+ ? "?"
+ : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.ABBREVIATED)[wallTime.getMonth()],
modifier);
return false;
case 'C':
outputYear(wallTime.getYear(), true, false, modifier);
return false;
case 'c':
- formatInternal(dateTimeFormat, wallTime, zoneInfo);
+ formatInternal(dateTimeFormat, wallTime, zoneInfoData);
return false;
case 'D':
- formatInternal("%m/%d/%y", wallTime, zoneInfo);
+ formatInternal("%m/%d/%y", wallTime, zoneInfoData);
return false;
case 'd':
numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
@@ -272,7 +288,7 @@ class TimeFormatter {
wallTime.getMonthDay());
return false;
case 'F':
- formatInternal("%Y-%m-%d", wallTime, zoneInfo);
+ formatInternal("%Y-%m-%d", wallTime, zoneInfoData);
return false;
case 'H':
numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
@@ -307,29 +323,31 @@ class TimeFormatter {
outputBuilder.append('\n');
return false;
case 'p':
- modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
- : localeData.amPm[0], modifier);
+ modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2))
+ ? dateFormatSymbols.getAmPmStrings()[1]
+ : dateFormatSymbols.getAmPmStrings()[0], modifier);
return false;
case 'P':
- modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
- : localeData.amPm[0], FORCE_LOWER_CASE);
+ modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2))
+ ? dateFormatSymbols.getAmPmStrings()[1]
+ : dateFormatSymbols.getAmPmStrings()[0], FORCE_LOWER_CASE);
return false;
case 'R':
- formatInternal("%H:%M", wallTime, zoneInfo);
+ formatInternal("%H:%M", wallTime, zoneInfoData);
return false;
case 'r':
- formatInternal("%I:%M:%S %p", wallTime, zoneInfo);
+ formatInternal("%I:%M:%S %p", wallTime, zoneInfoData);
return false;
case 'S':
numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getSecond());
return false;
case 's':
- int timeInSeconds = wallTime.mktime(zoneInfo);
+ int timeInSeconds = wallTime.mktime(zoneInfoData);
outputBuilder.append(Integer.toString(timeInSeconds));
return false;
case 'T':
- formatInternal("%H:%M:%S", wallTime, zoneInfo);
+ formatInternal("%H:%M:%S", wallTime, zoneInfoData);
return false;
case 't':
outputBuilder.append('\t');
@@ -383,7 +401,7 @@ class TimeFormatter {
return false;
}
case 'v':
- formatInternal("%e-%b-%Y", wallTime, zoneInfo);
+ formatInternal("%e-%b-%Y", wallTime, zoneInfoData);
return false;
case 'W':
int n = (wallTime.getYearDay() + DAYSPERWEEK - (
@@ -395,10 +413,10 @@ class TimeFormatter {
numberFormatter.format("%d", wallTime.getWeekDay());
return false;
case 'X':
- formatInternal(timeOnlyFormat, wallTime, zoneInfo);
+ formatInternal(timeOnlyFormat, wallTime, zoneInfoData);
return false;
case 'x':
- formatInternal(dateOnlyFormat, wallTime, zoneInfo);
+ formatInternal(dateOnlyFormat, wallTime, zoneInfoData);
return false;
case 'y':
outputYear(wallTime.getYear(), false, true, modifier);
@@ -411,7 +429,8 @@ class TimeFormatter {
return false;
}
boolean isDst = wallTime.getIsDst() != 0;
- modifyAndAppend(zoneInfo.getDisplayName(isDst, TimeZone.SHORT), modifier);
+ modifyAndAppend(TimeZone.getTimeZone(zoneInfoData.getID())
+ .getDisplayName(isDst, TimeZone.SHORT), modifier);
return false;
case 'z': {
if (wallTime.getIsDst() < 0) {
@@ -432,7 +451,7 @@ class TimeFormatter {
return false;
}
case '+':
- formatInternal("%a %b %e %H:%M:%S %Z %Y", wallTime, zoneInfo);
+ formatInternal("%a %b %e %H:%M:%S %Z %Y", wallTime, zoneInfoData);
return false;
case '%':
// If conversion char is undefined, behavior is undefined. Print out the
diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java
index d40015ee17a8..2b038dd11348 100644
--- a/core/java/android/text/method/NumberKeyListener.java
+++ b/core/java/android/text/method/NumberKeyListener.java
@@ -29,8 +29,6 @@ import android.text.format.DateFormat;
import android.view.KeyEvent;
import android.view.View;
-import libcore.icu.LocaleData;
-
import java.util.Collection;
import java.util.Locale;
@@ -228,7 +226,7 @@ public abstract class NumberKeyListener extends BaseKeyListener
if (locale == null) {
return false;
}
- final String[] amPm = LocaleData.get(locale).amPm;
+ final String[] amPm = DateFormat.getIcuDateFormatSymbols(locale).getAmPmStrings();
for (int i = 0; i < amPm.length; i++) {
for (int j = 0; j < amPm[i].length(); j++) {
final char ch = amPm[i].charAt(j);
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index 8fd303b8a64c..44d140242409 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -40,9 +40,9 @@ public final class CountryTimeZones {
public static final class TimeZoneMapping {
@NonNull
- private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate;
+ private com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping mDelegate;
- TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) {
+ TimeZoneMapping(com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping delegate) {
this.mDelegate = Objects.requireNonNull(delegate);
}
@@ -147,9 +147,9 @@ public final class CountryTimeZones {
}
@NonNull
- private final libcore.timezone.CountryTimeZones mDelegate;
+ private final com.android.i18n.timezone.CountryTimeZones mDelegate;
- CountryTimeZones(libcore.timezone.CountryTimeZones delegate) {
+ CountryTimeZones(com.android.i18n.timezone.CountryTimeZones delegate) {
mDelegate = delegate;
}
@@ -221,7 +221,7 @@ public final class CountryTimeZones {
@Nullable
public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias,
int totalOffsetMillis, boolean isDst) {
- libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
+ com.android.i18n.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
mDelegate.lookupByOffsetWithBias(
whenMillis, bias, totalOffsetMillis, isDst);
return delegateOffsetResult == null ? null :
@@ -244,7 +244,7 @@ public final class CountryTimeZones {
@Nullable
public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias,
int totalOffsetMillis) {
- libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
+ com.android.i18n.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
mDelegate.lookupByOffsetWithBias(whenMillis, bias, totalOffsetMillis);
return delegateOffsetResult == null ? null :
new OffsetResult(
@@ -260,11 +260,12 @@ public final class CountryTimeZones {
*/
@NonNull
public List<TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long whenMillis) {
- List<libcore.timezone.CountryTimeZones.TimeZoneMapping> delegateList =
+ List<com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping> delegateList =
mDelegate.getEffectiveTimeZoneMappingsAt(whenMillis);
List<TimeZoneMapping> toReturn = new ArrayList<>(delegateList.size());
- for (libcore.timezone.CountryTimeZones.TimeZoneMapping delegateMapping : delegateList) {
+ for (com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping delegateMapping
+ : delegateList) {
toReturn.add(new TimeZoneMapping(delegateMapping));
}
return Collections.unmodifiableList(toReturn);
diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java
index a4c3fbd33410..c97bf28cebfe 100644
--- a/core/java/android/timezone/TelephonyLookup.java
+++ b/core/java/android/timezone/TelephonyLookup.java
@@ -41,16 +41,17 @@ public final class TelephonyLookup {
public static TelephonyLookup getInstance() {
synchronized (sLock) {
if (sInstance == null) {
- sInstance = new TelephonyLookup(libcore.timezone.TelephonyLookup.getInstance());
+ sInstance = new TelephonyLookup(com.android.i18n.timezone.TelephonyLookup
+ .getInstance());
}
return sInstance;
}
}
@NonNull
- private final libcore.timezone.TelephonyLookup mDelegate;
+ private final com.android.i18n.timezone.TelephonyLookup mDelegate;
- private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) {
+ private TelephonyLookup(@NonNull com.android.i18n.timezone.TelephonyLookup delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
@@ -60,7 +61,7 @@ public final class TelephonyLookup {
*/
@Nullable
public TelephonyNetworkFinder getTelephonyNetworkFinder() {
- libcore.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate =
+ com.android.i18n.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate =
mDelegate.getTelephonyNetworkFinder();
return telephonyNetworkFinderDelegate != null
? new TelephonyNetworkFinder(telephonyNetworkFinderDelegate) : null;
diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java
index 823cd251fbf0..3b65c6ffc379 100644
--- a/core/java/android/timezone/TelephonyNetwork.java
+++ b/core/java/android/timezone/TelephonyNetwork.java
@@ -28,9 +28,9 @@ import java.util.Objects;
public final class TelephonyNetwork {
@NonNull
- private final libcore.timezone.TelephonyNetwork mDelegate;
+ private final com.android.i18n.timezone.TelephonyNetwork mDelegate;
- TelephonyNetwork(@NonNull libcore.timezone.TelephonyNetwork delegate) {
+ TelephonyNetwork(@NonNull com.android.i18n.timezone.TelephonyNetwork delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java
index 4bfeff8a73ad..c69ddf86d3f8 100644
--- a/core/java/android/timezone/TelephonyNetworkFinder.java
+++ b/core/java/android/timezone/TelephonyNetworkFinder.java
@@ -29,9 +29,9 @@ import java.util.Objects;
public final class TelephonyNetworkFinder {
@NonNull
- private final libcore.timezone.TelephonyNetworkFinder mDelegate;
+ private final com.android.i18n.timezone.TelephonyNetworkFinder mDelegate;
- TelephonyNetworkFinder(libcore.timezone.TelephonyNetworkFinder delegate) {
+ TelephonyNetworkFinder(com.android.i18n.timezone.TelephonyNetworkFinder delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
@@ -45,7 +45,7 @@ public final class TelephonyNetworkFinder {
Objects.requireNonNull(mcc);
Objects.requireNonNull(mnc);
- libcore.timezone.TelephonyNetwork telephonyNetworkDelegate =
+ com.android.i18n.timezone.TelephonyNetwork telephonyNetworkDelegate =
mDelegate.findNetworkByMccMnc(mcc, mnc);
return telephonyNetworkDelegate != null
? new TelephonyNetwork(telephonyNetworkDelegate) : null;
diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java
index 03f5013f230c..bf4275fb5b56 100644
--- a/core/java/android/timezone/TimeZoneFinder.java
+++ b/core/java/android/timezone/TimeZoneFinder.java
@@ -41,16 +41,17 @@ public final class TimeZoneFinder {
public static TimeZoneFinder getInstance() {
synchronized (sLock) {
if (sInstance == null) {
- sInstance = new TimeZoneFinder(libcore.timezone.TimeZoneFinder.getInstance());
+ sInstance = new TimeZoneFinder(com.android.i18n.timezone.TimeZoneFinder
+ .getInstance());
}
}
return sInstance;
}
@NonNull
- private final libcore.timezone.TimeZoneFinder mDelegate;
+ private final com.android.i18n.timezone.TimeZoneFinder mDelegate;
- private TimeZoneFinder(@NonNull libcore.timezone.TimeZoneFinder delegate) {
+ private TimeZoneFinder(@NonNull com.android.i18n.timezone.TimeZoneFinder delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
@@ -70,7 +71,8 @@ public final class TimeZoneFinder {
*/
@Nullable
public CountryTimeZones lookupCountryTimeZones(@NonNull String countryIso) {
- libcore.timezone.CountryTimeZones delegate = mDelegate.lookupCountryTimeZones(countryIso);
+ com.android.i18n.timezone.CountryTimeZones delegate = mDelegate
+ .lookupCountryTimeZones(countryIso);
return delegate == null ? null : new CountryTimeZones(delegate);
}
}
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
index f993012aeb1c..e1fb932b977d 100644
--- a/core/java/android/timezone/TzDataSetVersion.java
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -50,14 +50,14 @@ public final class TzDataSetVersion {
* Returns the major tz data format version supported by this device.
*/
public static int currentFormatMajorVersion() {
- return libcore.timezone.TzDataSetVersion.currentFormatMajorVersion();
+ return com.android.i18n.timezone.TzDataSetVersion.currentFormatMajorVersion();
}
/**
* Returns the minor tz data format version supported by this device.
*/
public static int currentFormatMinorVersion() {
- return libcore.timezone.TzDataSetVersion.currentFormatMinorVersion();
+ return com.android.i18n.timezone.TzDataSetVersion.currentFormatMinorVersion();
}
/**
@@ -65,7 +65,7 @@ public final class TzDataSetVersion {
* with the current system image, and set of active modules.
*/
public static boolean isCompatibleWithThisDevice(TzDataSetVersion tzDataSetVersion) {
- return libcore.timezone.TzDataSetVersion.isCompatibleWithThisDevice(
+ return com.android.i18n.timezone.TzDataSetVersion.isCompatibleWithThisDevice(
tzDataSetVersion.mDelegate);
}
@@ -76,8 +76,8 @@ public final class TzDataSetVersion {
public static TzDataSetVersion read() throws IOException, TzDataSetException {
try {
return new TzDataSetVersion(
- libcore.timezone.TzDataSetVersion.readTimeZoneModuleVersion());
- } catch (libcore.timezone.TzDataSetVersion.TzDataSetException e) {
+ com.android.i18n.timezone.TzDataSetVersion.readTimeZoneModuleVersion());
+ } catch (com.android.i18n.timezone.TzDataSetVersion.TzDataSetException e) {
throw new TzDataSetException(e.getMessage(), e);
}
}
@@ -100,9 +100,9 @@ public final class TzDataSetVersion {
}
@NonNull
- private final libcore.timezone.TzDataSetVersion mDelegate;
+ private final com.android.i18n.timezone.TzDataSetVersion mDelegate;
- private TzDataSetVersion(@NonNull libcore.timezone.TzDataSetVersion delegate) {
+ private TzDataSetVersion(@NonNull com.android.i18n.timezone.TzDataSetVersion delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
diff --git a/core/java/android/timezone/ZoneInfoDb.java b/core/java/android/timezone/ZoneInfoDb.java
index 9354a695812d..65d6eaddf824 100644
--- a/core/java/android/timezone/ZoneInfoDb.java
+++ b/core/java/android/timezone/ZoneInfoDb.java
@@ -41,16 +41,16 @@ public final class ZoneInfoDb {
public static ZoneInfoDb getInstance() {
synchronized (sLock) {
if (sInstance == null) {
- sInstance = new ZoneInfoDb(libcore.timezone.ZoneInfoDb.getInstance());
+ sInstance = new ZoneInfoDb(com.android.i18n.timezone.ZoneInfoDb.getInstance());
}
}
return sInstance;
}
@NonNull
- private final libcore.timezone.ZoneInfoDb mDelegate;
+ private final com.android.i18n.timezone.ZoneInfoDb mDelegate;
- private ZoneInfoDb(libcore.timezone.ZoneInfoDb delegate) {
+ private ZoneInfoDb(com.android.i18n.timezone.ZoneInfoDb delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 537498c44d5e..e338fd977f27 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,7 +41,6 @@ public class FeatureFlagUtils {
public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
- public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
/** @hide */
public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
"settings_do_not_restore_preserved";
@@ -52,7 +51,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS = new HashMap<>();
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
- DEFAULT_FLAGS.put(SETTINGS_FUSE_FLAG, "true");
DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index e8d345997022..e0b8d52aa132 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -23,10 +23,10 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.SystemClock;
-import libcore.timezone.CountryTimeZones;
-import libcore.timezone.CountryTimeZones.TimeZoneMapping;
-import libcore.timezone.TimeZoneFinder;
-import libcore.timezone.ZoneInfoDb;
+import com.android.i18n.timezone.CountryTimeZones;
+import com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping;
+import com.android.i18n.timezone.TimeZoneFinder;
+import com.android.i18n.timezone.ZoneInfoDb;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 346fe293d7ae..6e34666aea84 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -24,7 +24,6 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
-import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.util.ArrayMap;
@@ -213,11 +212,9 @@ public class ApkSignatureSchemeV2Verifier {
verityDigest, apk.length(), signatureInfo);
}
- byte[] digest = pickBestDigestForV4(contentDigests);
-
return new VerifiedSigner(
signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
- verityRootHash, digest);
+ verityRootHash, contentDigests);
}
private static X509Certificate[] verifySigner(
@@ -339,8 +336,7 @@ public class ApkSignatureSchemeV2Verifier {
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
}
- certificate = new VerbatimX509Certificate(
- certificate, encodedCert);
+ certificate = new VerbatimX509Certificate(certificate, encodedCert);
certs.add(certificate);
}
@@ -434,12 +430,15 @@ public class ApkSignatureSchemeV2Verifier {
public final X509Certificate[][] certs;
public final byte[] verityRootHash;
- public final byte[] digest;
+ // Algorithm -> digest map of signed digests in the signature.
+ // All these are verified if requested.
+ public final Map<Integer, byte[]> contentDigests;
- public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
+ public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash,
+ Map<Integer, byte[]> contentDigests) {
this.certs = certs;
this.verityRootHash = verityRootHash;
- this.digest = digest;
+ this.contentDigests = contentDigests;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 4ab541b616ed..93572857796c 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -24,7 +24,6 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
-import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.os.Build;
@@ -161,7 +160,7 @@ public class ApkSignatureSchemeV3Verifier {
boolean doVerifyIntegrity) throws SecurityException, IOException {
int signerCount = 0;
Map<Integer, byte[]> contentDigests = new ArrayMap<>();
- VerifiedSigner result = null;
+ Pair<X509Certificate[], VerifiedProofOfRotation> result = null;
CertificateFactory certFactory;
try {
certFactory = CertificateFactory.getInstance("X.509");
@@ -206,18 +205,17 @@ public class ApkSignatureSchemeV3Verifier {
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
+ byte[] verityRootHash = null;
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
- result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
+ verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
verityDigest, apk.length(), signatureInfo);
}
- result.digest = pickBestDigestForV4(contentDigests);
-
- return result;
+ return new VerifiedSigner(result.first, result.second, verityRootHash, contentDigests);
}
- private static VerifiedSigner verifySigner(
+ private static Pair<X509Certificate[], VerifiedProofOfRotation> verifySigner(
ByteBuffer signerBlock,
Map<Integer, byte[]> contentDigests,
CertificateFactory certFactory)
@@ -349,8 +347,7 @@ public class ApkSignatureSchemeV3Verifier {
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
}
- certificate = new VerbatimX509Certificate(
- certificate, encodedCert);
+ certificate = new VerbatimX509Certificate(certificate, encodedCert);
certs.add(certificate);
}
@@ -382,8 +379,9 @@ public class ApkSignatureSchemeV3Verifier {
private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c;
- private static VerifiedSigner verifyAdditionalAttributes(ByteBuffer attrs,
- List<X509Certificate> certs, CertificateFactory certFactory) throws IOException {
+ private static Pair<X509Certificate[], VerifiedProofOfRotation> verifyAdditionalAttributes(
+ ByteBuffer attrs, List<X509Certificate> certs, CertificateFactory certFactory)
+ throws IOException {
X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]);
VerifiedProofOfRotation por = null;
@@ -421,7 +419,7 @@ public class ApkSignatureSchemeV3Verifier {
break;
}
}
- return new VerifiedSigner(certChain, por);
+ return Pair.create(certChain, por);
}
private static VerifiedProofOfRotation verifyProofOfRotationStruct(
@@ -570,12 +568,17 @@ public class ApkSignatureSchemeV3Verifier {
public final X509Certificate[] certs;
public final VerifiedProofOfRotation por;
- public byte[] verityRootHash;
- public byte[] digest;
+ public final byte[] verityRootHash;
+ // Algorithm -> digest map of signed digests in the signature.
+ // All these are verified if requested.
+ public final Map<Integer, byte[]> contentDigests;
- public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
+ public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por,
+ byte[] verityRootHash, Map<Integer, byte[]> contentDigests) {
this.certs = certs;
this.por = por;
+ this.verityRootHash = verityRootHash;
+ this.contentDigests = contentDigests;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index d40efce0b3b3..844816c0d903 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -16,12 +16,14 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
import android.os.incremental.IncrementalManager;
import android.os.incremental.V4Signature;
+import android.util.ArrayMap;
import android.util.Pair;
import java.io.ByteArrayInputStream;
@@ -42,6 +44,7 @@ import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
+import java.util.Map;
/**
* APK Signature Scheme v4 verifier.
@@ -79,13 +82,20 @@ public class ApkSignatureSchemeV4Verifier {
throw new SignatureNotFoundException("Failed to read V4 signature.", e);
}
- final byte[] signedData = V4Signature.getSigningData(apk.length(), hashingInfo,
+ // Verify signed data and extract certificates and apk digest.
+ final byte[] signedData = V4Signature.getSignedData(apk.length(), hashingInfo,
signingInfo);
+ final Pair<Certificate, byte[]> result = verifySigner(signingInfo, signedData);
- return verifySigner(signingInfo, signedData);
+ // Populate digests enforced by IncFS driver.
+ Map<Integer, byte[]> contentDigests = new ArrayMap<>();
+ contentDigests.put(convertToContentDigestType(hashingInfo.hashAlgorithm),
+ hashingInfo.rawRootHash);
+
+ return new VerifiedSigner(new Certificate[]{result.first}, result.second, contentDigests);
}
- private static VerifiedSigner verifySigner(V4Signature.SigningInfo signingInfo,
+ private static Pair<Certificate, byte[]> verifySigner(V4Signature.SigningInfo signingInfo,
final byte[] signedData) throws SecurityException {
if (!isSupportedSignatureAlgorithm(signingInfo.signatureAlgorithmId)) {
throw new SecurityException("No supported signatures found");
@@ -145,21 +155,34 @@ public class ApkSignatureSchemeV4Verifier {
"Public key mismatch between certificate and signature record");
}
- return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
+ return Pair.create(certificate, signingInfo.apkDigest);
+ }
+
+ private static int convertToContentDigestType(int hashAlgorithm) throws SecurityException {
+ if (hashAlgorithm == V4Signature.HASHING_ALGORITHM_SHA256) {
+ return CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
+ }
+ throw new SecurityException("Unsupported hashAlgorithm: " + hashAlgorithm);
}
/**
- * Verified APK Signature Scheme v4 signer, including V3 digest.
+ * Verified APK Signature Scheme v4 signer, including V2/V3 digest.
*
* @hide for internal use only.
*/
public static class VerifiedSigner {
public final Certificate[] certs;
- public byte[] apkDigest;
+ public final byte[] apkDigest;
+
+ // Algorithm -> digest map of signed digests in the signature.
+ // These are continuously enforced by the IncFS driver.
+ public final Map<Integer, byte[]> contentDigests;
- public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
+ public VerifiedSigner(Certificate[] certs, byte[] apkDigest,
+ Map<Integer, byte[]> contentDigests) {
this.certs = certs;
this.apkDigest = apkDigest;
+ this.contentDigests = contentDigests;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index ab8f80d3d1a5..e0258f7657b2 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -45,6 +45,7 @@ import java.security.cert.CertificateEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.ZipEntry;
@@ -184,21 +185,21 @@ public class ApkSignatureVerifier {
Signature[] signerSigs = convertToSignatures(signerCerts);
if (verifyFull) {
- byte[] nonstreamingDigest = null;
- Certificate[][] nonstreamingCerts = null;
+ Map<Integer, byte[]> nonstreamingDigests;
+ Certificate[][] nonstreamingCerts;
try {
// v4 is an add-on and requires v2 or v3 signature to validate against its
// certificate and digest
ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
- nonstreamingDigest = v3Signer.digest;
+ nonstreamingDigests = v3Signer.contentDigests;
nonstreamingCerts = new Certificate[][]{v3Signer.certs};
} catch (SignatureNotFoundException e) {
try {
ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
ApkSignatureSchemeV2Verifier.verify(apkPath, false);
- nonstreamingDigest = v2Signer.digest;
+ nonstreamingDigests = v2Signer.contentDigests;
nonstreamingCerts = v2Signer.certs;
} catch (SignatureNotFoundException ee) {
throw new SecurityException(
@@ -220,8 +221,15 @@ public class ApkSignatureVerifier {
}
}
- if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
- vSigner.apkDigest.length)) {
+ boolean found = false;
+ for (byte[] nonstreamingDigest : nonstreamingDigests.values()) {
+ if (ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
+ vSigner.apkDigest.length)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
throw new SecurityException("APK digest in V4 signature does not match V2/V3");
}
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 6efe95cb9e92..990092caa833 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -577,21 +577,6 @@ final class ApkSigningBlockUtils {
}
/**
- * Returns the best digest from the map of available digests.
- * similarly to compareContentDigestAlgorithm.
- *
- * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils.
- */
- static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) {
- for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) {
- if (contentDigests.containsKey(algo)) {
- return contentDigests.get(algo);
- }
- }
- return null;
- }
-
- /**
* Returns new byte buffer whose content is a shared subsequence of this buffer's content
* between the specified start (inclusive) and end (exclusive) positions. As opposed to
* {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 064bc6947fc4..713cfb48c95f 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -311,9 +311,6 @@ public class FocusFinder {
}
final int count = focusables.size();
- if (count < 2) {
- return null;
- }
switch (direction) {
case View.FOCUS_FORWARD:
return getNextFocusable(focused, focusables, count);
@@ -376,29 +373,29 @@ public class FocusFinder {
}
private static View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
- if (count < 2) {
- return null;
- }
if (focused != null) {
int position = focusables.lastIndexOf(focused);
if (position >= 0 && position + 1 < count) {
return focusables.get(position + 1);
}
}
- return focusables.get(0);
+ if (!focusables.isEmpty()) {
+ return focusables.get(0);
+ }
+ return null;
}
private static View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
- if (count < 2) {
- return null;
- }
if (focused != null) {
int position = focusables.indexOf(focused);
if (position > 0) {
return focusables.get(position - 1);
}
}
- return focusables.get(count - 1);
+ if (!focusables.isEmpty()) {
+ return focusables.get(count - 1);
+ }
+ return null;
}
private static View getNextKeyboardNavigationCluster(
diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl
index 429c3aeba9b3..a0d4a6587bcf 100644
--- a/core/java/android/view/IDisplayWindowInsetsController.aidl
+++ b/core/java/android/view/IDisplayWindowInsetsController.aidl
@@ -27,6 +27,13 @@ import android.view.InsetsState;
oneway interface IDisplayWindowInsetsController {
/**
+ * Called when top focused window changes to determine whether or not to take over insets
+ * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
+ * @param packageName: Passes the top package name
+ */
+ void topFocusedWindowChanged(String packageName);
+
+ /**
* @see IWindow#insetsChanged
*/
void insetsChanged(in InsetsState insetsState);
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index b6ce9f52e0c3..f49fee36ca7e 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -16,6 +16,7 @@
package android.view;
+import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
@@ -51,9 +52,9 @@ oneway interface IPinnedStackListener {
/**
* Called when the set of actions for the current PiP activity changes, or when the listener
- * is first registered to allow the listener to synchronized its state with the controller.
+ * is first registered to allow the listener to synchronize its state with the controller.
*/
- void onActionsChanged(in ParceledListSlice actions);
+ void onActionsChanged(in ParceledListSlice<RemoteAction> actions);
/**
* Called by the window manager to notify the listener that Activity (was or is in pinned mode)
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 58597cf3fb6d..00fc67214f75 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -388,16 +388,6 @@ interface IWindowManager
oneway void hideTransientBars(int displayId);
/**
- * When set to {@code true} the system bars will always be shown. This is true even if an app
- * requests to be fullscreen by setting the system ui visibility flags. The
- * functionality was added for the automotive case as a way to guarantee required content stays
- * on screen at all times.
- *
- * @hide
- */
- oneway void setForceShowSystemBars(boolean show);
-
- /**
* Called by System UI to notify of changes to the visibility of Recents.
*/
oneway void setRecentsVisibility(boolean visible);
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 825077ffd57a..92772c1d7a44 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -125,11 +125,11 @@ public final class ImeFocusController {
final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
onViewFocusChanged(viewForWindowFocus, true);
- // Skip starting input when the next focused view is same as served view and the served
- // input connection still exists.
- final boolean nextFocusIsServedView = mServedView != null && mServedView == focusedView;
- if (nextFocusIsServedView && immDelegate.isAcceptingText()) {
- forceFocus = false;
+ // Starting new input when the next focused view is same as served view but the currently
+ // active connection (if any) is not associated with it.
+ final boolean nextFocusIsServedView = mServedView == viewForWindowFocus;
+ if (nextFocusIsServedView && !immDelegate.hasActiveConnection(viewForWindowFocus)) {
+ forceFocus = true;
}
immDelegate.startInputAsyncOnWindowFocusGain(viewForWindowFocus,
@@ -254,7 +254,7 @@ public final class ImeFocusController {
void setCurrentRootView(ViewRootImpl rootView);
boolean isCurrentRootView(ViewRootImpl rootView);
boolean isRestartOnNextWindowFocus(boolean reset);
- boolean isAcceptingText();
+ boolean hasActiveConnection(View view);
}
public View getServedView() {
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index ef9d990168d2..c1998c6009cf 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -119,11 +119,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
// If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
// this code here means that we now got control, so we can start the animation immediately.
// If client window is trying to control IME and IME is already visible, it is immediate.
- if (fromIme || mState.getSource(getType()).isVisible()) {
+ if (fromIme || mState.getSource(getType()).isVisible() && getControl() != null) {
return ShowResult.SHOW_IMMEDIATELY;
}
- return getImm().requestImeShow(null /* resultReceiver */)
+ return getImm().requestImeShow(mController.getHost().getWindowToken())
? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED;
}
@@ -132,12 +132,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
*/
@Override
void notifyHidden() {
- getImm().notifyImeHidden();
+ getImm().notifyImeHidden(mController.getHost().getWindowToken());
}
@Override
public void removeSurface() {
- getImm().removeImeSurface();
+ final IBinder window = mController.getHost().getWindowToken();
+ if (window != null) {
+ getImm().removeImeSurface(window);
+ }
}
@Override
@@ -146,6 +149,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
super.setControl(control, showTypes, hideTypes);
if (control == null && !mIsRequestedVisibleAwaitingControl) {
hide();
+ removeSurface();
}
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 31da83ad5137..69220722fc05 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -308,7 +308,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
false /* isScreenRound */,
false /* alwaysConsumeSystemBars */, null /* displayCutout */,
LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/,
- 0 /* legacySystemUiFlags */, typeSideMap)
+ 0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, typeSideMap)
.getInsets(mTypes);
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c6be91fa1bf5..c383bc7a4d70 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -501,6 +501,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private PendingControlRequest mPendingImeControlRequest;
private int mLastLegacySoftInputMode;
+ private int mLastLegacyWindowFlags;
private int mLastLegacySystemUiFlags;
private DisplayCutout mLastDisplayCutout;
private boolean mStartingAnimation;
@@ -569,8 +570,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
- mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags,
- null /* typeSideMap */);
+ mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacyWindowFlags,
+ mLastLegacySystemUiFlags, null /* typeSideMap */);
mHost.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims);
if (DEBUG) {
for (WindowInsetsAnimation anim : mUnmodifiableTmpRunningAnims) {
@@ -706,13 +707,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
public WindowInsets calculateInsets(boolean isScreenRound,
boolean alwaysConsumeSystemBars, DisplayCutout cutout,
- int legacySoftInputMode, int legacySystemUiFlags) {
+ int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) {
mLastLegacySoftInputMode = legacySoftInputMode;
+ mLastLegacyWindowFlags = legacyWindowFlags;
mLastLegacySystemUiFlags = legacySystemUiFlags;
mLastDisplayCutout = cutout;
mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
isScreenRound, alwaysConsumeSystemBars, cutout,
- legacySoftInputMode, legacySystemUiFlags,
+ legacySoftInputMode, legacyWindowFlags, legacySystemUiFlags,
null /* typeSideMap */);
return mLastInsets;
}
@@ -737,7 +739,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
}
- final boolean hasControl = mTmpControlArray.size() > 0;
+ boolean requestedStateStale = false;
final int[] showTypes = new int[1];
final int[] hideTypes = new int[1];
@@ -754,9 +756,26 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// Ensure to create source consumers if not available yet.
for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
final InsetsSourceControl control = mTmpControlArray.valueAt(i);
- InsetsSourceConsumer consumer = getSourceConsumer(control.getType());
+ final @InternalInsetsType int type = control.getType();
+ final InsetsSourceConsumer consumer = getSourceConsumer(type);
consumer.setControl(control, showTypes, hideTypes);
+ if (!requestedStateStale) {
+ final boolean requestedVisible = consumer.isRequestedVisible();
+
+ // We might have changed our requested visibilities while we don't have the control,
+ // so we need to update our requested state once we have control. Otherwise, our
+ // requested state at the server side might be incorrect.
+ final boolean requestedVisibilityChanged =
+ requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type);
+
+ // The IME client visibility will be reset by insets source provider while updating
+ // control, so if IME is requested visible, we need to send the request to server.
+ final boolean imeRequestedVisible = type == ITYPE_IME && requestedVisible;
+
+ requestedStateStale = requestedVisibilityChanged || imeRequestedVisible;
+ }
+
}
mTmpControlArray.clear();
@@ -772,10 +791,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (hideTypes[0] != 0) {
applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
}
- if (hasControl && mRequestedState.hasSources()) {
- // We might have changed our requested visibilities while we don't have the control,
- // so we need to update our requested state once we have control. Otherwise, our
- // requested state at the server side might be incorrect.
+ if (requestedStateStale) {
updateRequestedState();
}
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 40e6f4b2fce8..700dc66fab55 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -153,6 +153,9 @@ public class InsetsSourceConsumer {
if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) {
applyHiddenToControl();
}
+ if (!requestedVisible && !mIsAnimationPending) {
+ removeSurface();
+ }
}
}
if (lastControl != null) {
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 2c2ecd504519..51b49214387a 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -45,7 +45,7 @@ public class InsetsSourceControl implements Parcelable {
public InsetsSourceControl(InsetsSourceControl other) {
mType = other.mType;
if (other.mLeash != null) {
- mLeash = new SurfaceControl(other.mLeash);
+ mLeash = new SurfaceControl(other.mLeash, "InsetsSourceControl");
} else {
mLeash = null;
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 9bf2e01a6bd1..6b0b509932a8 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -22,13 +22,14 @@ import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
-import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.indexOf;
import static android.view.WindowInsets.Type.isVisibleInsetsType;
+import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
@@ -38,7 +39,6 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
@@ -171,7 +171,7 @@ public class InsetsState implements Parcelable {
*/
public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout,
- int legacySoftInputMode, int legacySystemUiFlags,
+ int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags,
@Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
Insets[] typeInsetsMap = new Insets[Type.SIZE];
Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
@@ -218,10 +218,17 @@ public class InsetsState implements Parcelable {
}
}
final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST;
+
+ @InsetsType int compatInsetsTypes = systemBars() | displayCutout();
+ if (softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE) {
+ compatInsetsTypes |= ime();
+ }
+ if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
+ compatInsetsTypes &= ~statusBars();
+ }
+
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
- alwaysConsumeSystemBars, cutout, softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE
- ? systemBars() | displayCutout() | ime()
- : systemBars() | displayCutout(),
+ alwaysConsumeSystemBars, cutout, compatInsetsTypes,
sNewInsetsMode == NEW_INSETS_MODE_FULL
&& (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
}
@@ -427,8 +434,7 @@ public class InsetsState implements Parcelable {
if (copySources) {
for (int i = 0; i < SIZE; i++) {
InsetsSource source = other.mSources[i];
- if (source == null) continue;
- mSources[i] = new InsetsSource(source);
+ mSources[i] = source != null ? new InsetsSource(source) : null;
}
} else {
for (int i = 0; i < SIZE; i++) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 19eff72ca814..51b0c6b59f3c 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -487,6 +487,21 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int FLAG_TAINTED = 0x80000000;
/**
+ * Private flag indicating that this event was synthesized by the system and should be delivered
+ * to the accessibility focused view first. When being dispatched such an event is not handled
+ * by predecessors of the accessibility focused view and after the event reaches that view the
+ * flag is cleared and normal event dispatch is performed. This ensures that the platform can
+ * click on any view that has accessibility focus which is semantically equivalent to asking the
+ * view to perform a click accessibility action but more generic as views not implementing click
+ * action correctly can still be activated.
+ *
+ * @hide
+ * @see #isTargetAccessibilityFocus()
+ * @see #setTargetAccessibilityFocus(boolean)
+ */
+ public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000;
+
+ /**
* Flag indicating the motion event intersected the top edge of the screen.
*/
public static final int EDGE_TOP = 0x00000001;
@@ -2140,6 +2155,20 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/** @hide */
+ public boolean isTargetAccessibilityFocus() {
+ final int flags = getFlags();
+ return (flags & FLAG_TARGET_ACCESSIBILITY_FOCUS) != 0;
+ }
+
+ /** @hide */
+ public void setTargetAccessibilityFocus(boolean targetsFocus) {
+ final int flags = getFlags();
+ nativeSetFlags(mNativePtr, targetsFocus
+ ? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS
+ : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS);
+ }
+
+ /** @hide */
public final boolean isHoverExitPending() {
final int flags = getFlags();
return (flags & FLAG_HOVER_EXIT_PENDING) != 0;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 88c5469aa980..6beea8764ed1 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -49,7 +49,6 @@ import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.Trace;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseIntArray;
@@ -499,14 +498,12 @@ public final class SurfaceControl implements Parcelable {
private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
- private void assignNativeObject(long nativeObject) {
+ private void assignNativeObject(long nativeObject, String callsite) {
if (mNativeObject != 0) {
release();
}
if (nativeObject != 0) {
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "closeGuard");
- mCloseGuard.open("release");
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ mCloseGuard.openWithCallSite("release", callsite);
}
mNativeObject = nativeObject;
mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
@@ -515,12 +512,12 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public void copyFrom(@NonNull SurfaceControl other) {
+ public void copyFrom(@NonNull SurfaceControl other, String callsite) {
mName = other.mName;
mWidth = other.mWidth;
mHeight = other.mHeight;
mLocalOwnerView = other.mLocalOwnerView;
- assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject));
+ assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite);
}
/**
@@ -615,6 +612,7 @@ public final class SurfaceControl implements Parcelable {
private WeakReference<View> mLocalOwnerView;
private SurfaceControl mParent;
private SparseIntArray mMetadata;
+ private String mCallsite = "SurfaceControl.Builder";
/**
* Begin building a SurfaceControl with a given {@link SurfaceSession}.
@@ -648,7 +646,7 @@ public final class SurfaceControl implements Parcelable {
}
return new SurfaceControl(
mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata,
- mLocalOwnerView);
+ mLocalOwnerView, mCallsite);
}
/**
@@ -906,6 +904,18 @@ public final class SurfaceControl implements Parcelable {
return this;
}
+ /**
+ * Sets the callsite this SurfaceControl is constructed from.
+ *
+ * @param callsite String uniquely identifying callsite that created this object. Used for
+ * leakage tracking.
+ * @hide
+ */
+ public Builder setCallsite(String callsite) {
+ mCallsite = callsite;
+ return this;
+ }
+
private Builder setFlags(int flags, int mask) {
mFlags = (mFlags & ~mask) | flags;
return this;
@@ -937,10 +947,13 @@ public final class SurfaceControl implements Parcelable {
* @param h The surface initial height.
* @param flags The surface creation flags.
* @param metadata Initial metadata.
+ * @param callsite String uniquely identifying callsite that created this object. Used for
+ * leakage tracking.
* @throws throws OutOfResourcesException If the SurfaceControl cannot be created.
*/
private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
- SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView)
+ SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView,
+ String callsite)
throws OutOfResourcesException, IllegalArgumentException {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
@@ -972,18 +985,20 @@ public final class SurfaceControl implements Parcelable {
"Couldn't allocate SurfaceControl native object");
}
mNativeHandle = nativeGetHandle(mNativeObject);
- mCloseGuard.open("release");
+ mCloseGuard.openWithCallSite("release", callsite);
}
/**
* Copy constructor. Creates a new native object pointing to the same surface as {@code other}.
*
* @param other The object to copy the surface from.
+ * @param callsite String uniquely identifying callsite that created this object. Used for
+ * leakage tracking.
* @hide
*/
@TestApi
- public SurfaceControl(@NonNull SurfaceControl other) {
- copyFrom(other);
+ public SurfaceControl(@NonNull SurfaceControl other, @NonNull String callsite) {
+ copyFrom(other, callsite);
}
private SurfaceControl(Parcel in) {
@@ -1009,7 +1024,7 @@ public final class SurfaceControl implements Parcelable {
if (in.readInt() != 0) {
object = nativeReadFromParcel(in);
}
- assignNativeObject(object);
+ assignNativeObject(object, "readFromParcel");
}
@Override
@@ -1439,6 +1454,22 @@ public final class SurfaceControl implements Parcelable {
+ ", secure=" + secure
+ ", deviceProductInfo=" + deviceProductInfo + "}";
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DisplayInfo that = (DisplayInfo) o;
+ return isInternal == that.isInternal
+ && density == that.density
+ && secure == that.secure
+ && Objects.equals(deviceProductInfo, that.deviceProductInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(isInternal, density, secure, deviceProductInfo);
+ }
}
/**
@@ -2203,7 +2234,7 @@ public final class SurfaceControl implements Parcelable {
public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) {
long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject);
SurfaceControl sc = new SurfaceControl();
- sc.assignNativeObject(nativeObj);
+ sc.assignNativeObject(nativeObj, "mirrorSurface");
return sc;
}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 86a4fe170387..66ab3a32edfa 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -167,9 +167,10 @@ public class SurfaceControlViewHost {
public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display,
@Nullable IBinder hostToken) {
mSurfaceControl = new SurfaceControl.Builder()
- .setContainerLayer()
- .setName("SurfaceControlViewHost")
- .build();
+ .setContainerLayer()
+ .setName("SurfaceControlViewHost")
+ .setCallsite("SurfaceControlViewHost")
+ .build();
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
mViewRoot = new ViewRootImpl(context, display, mWm);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0d21eb5cf920..f937bc9e84a9 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -471,6 +471,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
private void performDrawFinished() {
+ if (mDeferredDestroySurfaceControl != null) {
+ synchronized (mSurfaceControlLock) {
+ mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply();
+ mDeferredDestroySurfaceControl = null;
+ }
+ }
+
if (mPendingReportDraws > 0) {
mDrawFinished = true;
if (mAttachedToWindow) {
@@ -993,6 +1000,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
.setFormat(mFormat)
.setParent(viewRoot.getBoundsLayer())
.setFlags(mSurfaceFlags)
+ .setCallsite("SurfaceView.updateSurface")
.build();
mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
.setName("Background for -" + name)
@@ -1000,6 +1008,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
.setOpaque(true)
.setColorLayer()
.setParent(mSurfaceControl)
+ .setCallsite("SurfaceView.updateSurface")
.build();
} else if (mSurfaceControl == null) {
@@ -1192,13 +1201,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
+ "finishedDrawing");
}
- if (mDeferredDestroySurfaceControl != null) {
- synchronized (mSurfaceControlLock) {
- mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply();
- mDeferredDestroySurfaceControl = null;
- }
- }
-
runOnUiThread(this::performDrawFinished);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a0a5d0a4d6c1..e951156ec4cc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3506,6 +3506,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 1 PFLAG4_AUTOFILL_HIDE_HIGHLIGHT
* 11 PFLAG4_SCROLL_CAPTURE_HINT_MASK
* 1 PFLAG4_ALLOW_CLICK_WHEN_DISABLED
+ * 1 PFLAG4_DETACHED
* |-------|-------|-------|-------|
*/
@@ -3567,6 +3568,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private static final int PFLAG4_ALLOW_CLICK_WHEN_DISABLED = 0x000001000;
+ /**
+ * Indicates if the view is just detached.
+ */
+ private static final int PFLAG4_DETACHED = 0x000002000;
+
/* End of masks for mPrivateFlags4 */
/** @hide */
@@ -4323,9 +4329,46 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
equals = STATUS_BAR_TRANSPARENT,
name = "STATUS_BAR_TRANSPARENT")
}, formatToHexString = true)
+ @SystemUiVisibility
int mSystemUiVisibility;
/**
+ * @hide
+ */
+ @IntDef(flag = true, prefix = "", value = {
+ SYSTEM_UI_FLAG_LOW_PROFILE,
+ SYSTEM_UI_FLAG_HIDE_NAVIGATION,
+ SYSTEM_UI_FLAG_FULLSCREEN,
+ SYSTEM_UI_FLAG_LAYOUT_STABLE,
+ SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,
+ SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
+ SYSTEM_UI_FLAG_IMMERSIVE,
+ SYSTEM_UI_FLAG_IMMERSIVE_STICKY,
+ SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ STATUS_BAR_DISABLE_EXPAND,
+ STATUS_BAR_DISABLE_NOTIFICATION_ICONS,
+ STATUS_BAR_DISABLE_NOTIFICATION_ALERTS,
+ STATUS_BAR_DISABLE_NOTIFICATION_TICKER,
+ STATUS_BAR_DISABLE_SYSTEM_INFO,
+ STATUS_BAR_DISABLE_HOME,
+ STATUS_BAR_DISABLE_BACK,
+ STATUS_BAR_DISABLE_CLOCK,
+ STATUS_BAR_DISABLE_RECENT,
+ STATUS_BAR_DISABLE_SEARCH,
+ STATUS_BAR_TRANSIENT,
+ NAVIGATION_BAR_TRANSIENT,
+ STATUS_BAR_UNHIDE,
+ NAVIGATION_BAR_UNHIDE,
+ STATUS_BAR_TRANSLUCENT,
+ NAVIGATION_BAR_TRANSLUCENT,
+ NAVIGATION_BAR_TRANSPARENT,
+ STATUS_BAR_TRANSPARENT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemUiVisibility {}
+
+ /**
* Reference count for transient state.
* @see #setHasTransientState(boolean)
*/
@@ -8330,7 +8373,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
boolean isWindowDisappearedEvent = isWindowStateChanged && ((event.getContentChangeTypes()
& AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) != 0);
- if (!isShown() && !isWindowDisappearedEvent) {
+ boolean detached = detached();
+ if (!isShown() && !isWindowDisappearedEvent && !detached) {
return;
}
onInitializeAccessibilityEvent(event);
@@ -8341,6 +8385,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
SendAccessibilityEventThrottle throttle = getThrottleForAccessibilityEvent(event);
if (throttle != null) {
throttle.post(event);
+ } else if (!isWindowDisappearedEvent && detached) {
+ // Views could be attached soon later. Accessibility events during this temporarily
+ // detached period should be sent too.
+ postDelayed(() -> {
+ if (AccessibilityManager.getInstance(mContext).isEnabled() && isShown()) {
+ requestParentSendAccessibilityEvent(event);
+ }
+ }, ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
} else {
requestParentSendAccessibilityEvent(event);
}
@@ -11126,6 +11178,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return false;
}
+ private boolean detached() {
+ View current = this;
+ //noinspection ConstantConditions
+ do {
+ if ((current.mPrivateFlags4 & PFLAG4_DETACHED) != 0) {
+ return true;
+ }
+ ViewParent parent = current.mParent;
+ if (parent == null) {
+ return false;
+ }
+ if (!(parent instanceof View)) {
+ return false;
+ }
+ current = (View) parent;
+ } while (current != null);
+
+ return false;
+ }
+
/**
* Called by the view hierarchy when the content insets for a window have
* changed, to allow it to adjust its content to fit within those windows.
@@ -13800,6 +13872,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ private void notifySubtreeAccessibilityStateChangedByParentIfNeeded() {
+ if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+ return;
+ }
+
+ final View sendA11yEventView = (View) getParentForAccessibility();
+ if (sendA11yEventView != null && sendA11yEventView.isShown()) {
+ sendA11yEventView.notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
+ }
+
/**
* Changes the visibility of this View without triggering any other changes. This should only
* be used by animation frameworks, such as {@link android.transition.Transition}, where
@@ -14308,6 +14391,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
+ if (event.isTargetAccessibilityFocus()) {
+ // We don't have focus or no virtual descendant has it, do not handle the event.
+ if (!isAccessibilityFocusedViewOrHost()) {
+ return false;
+ }
+ // We have focus and got the event, then use normal event dispatch.
+ event.setTargetAccessibilityFocus(false);
+ }
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
@@ -16229,7 +16320,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
((!(mParent instanceof ViewGroup)) || ((ViewGroup) mParent).isShown())) {
dispatchVisibilityAggregated(newVisibility == VISIBLE);
}
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ // If this view is invisible from visible, then sending the A11y event by its
+ // parent which is shown and has the accessibility important.
+ if ((old & VISIBILITY_MASK) == VISIBLE) {
+ notifySubtreeAccessibilityStateChangedByParentIfNeeded();
+ } else {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
}
}
@@ -26399,6 +26496,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
.setParent(root.getSurfaceControl())
.setBufferSize(shadowSize.x, shadowSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
+ .setCallsite("View.startDragAndDrop")
.build();
final Surface surface = new Surface();
surface.copyFrom(surfaceControl);
@@ -29442,7 +29540,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@Override
public void run() {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled() && isShown()) {
requestParentSendAccessibilityEvent(mAccessibilityEvent);
}
reset();
@@ -30431,4 +30529,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
}
+
+ /**
+ * Set the view to be detached or not detached.
+ *
+ * @param detached Whether the view is detached.
+ *
+ * @hide
+ */
+ protected void setDetached(boolean detached) {
+ if (detached) {
+ mPrivateFlags4 |= PFLAG4_DETACHED;
+ } else {
+ mPrivateFlags4 &= ~PFLAG4_DETACHED;
+ }
+ }
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 05b75e4f0d41..f648292d3118 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2048,8 +2048,26 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
+ View childWithAccessibilityFocus =
+ event.isTargetAccessibilityFocus()
+ ? findChildWithAccessibilityFocus()
+ : null;
+
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
+
+ // If there is a view that has accessibility focus we want it
+ // to get the event first and if not handled we will perform a
+ // normal dispatch. We may do a double iteration but this is
+ // safer given the timeframe.
+ if (childWithAccessibilityFocus != null) {
+ if (childWithAccessibilityFocus != child) {
+ continue;
+ }
+ childWithAccessibilityFocus = null;
+ i = childrenCount - 1;
+ }
+ event.setTargetAccessibilityFocus(false);
continue;
}
final PointerIcon pointerIcon =
@@ -2617,6 +2635,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
+ // If the event targets the accessibility focused view and this is it, start
+ // normal event dispatch. Maybe a descendant is what will handle the click.
+ if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
+ ev.setTargetAccessibilityFocus(false);
+ }
+
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
@@ -2647,6 +2671,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// so this view group continues to intercept touches.
intercepted = true;
}
+
+ // If intercepted, start normal event dispatch. Also if there is already
+ // a view that is handling the gesture, do normal event dispatch.
+ if (intercepted || mFirstTouchTarget != null) {
+ ev.setTargetAccessibilityFocus(false);
+ }
+
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
@@ -2658,6 +2689,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
+ // If the event is targeting accessibility focus we give it to the
+ // view that has accessibility focus and if it does not handle it
+ // we clear the flag and dispatch the event to all children as usual.
+ // We are looking up the accessibility focused host to avoid keeping
+ // state since these events are very rare.
+ View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
+ ? findChildWithAccessibilityFocus() : null;
+
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
@@ -2720,6 +2759,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
alreadyDispatchedToNewTouchTarget = true;
break;
}
+
+ // The accessibility focus didn't handle the event, so clear
+ // the flag and do a normal dispatch to all children.
+ ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
@@ -2803,6 +2846,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return buildOrderedChildList();
}
+ /**
+ * Finds the child which has accessibility focus.
+ *
+ * @return The child that has focus.
+ */
+ private View findChildWithAccessibilityFocus() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot == null) {
+ return null;
+ }
+
+ View current = viewRoot.getAccessibilityFocusedHost();
+ if (current == null) {
+ return null;
+ }
+
+ ViewParent parent = current.getParent();
+ while (parent instanceof View) {
+ if (parent == this) {
+ return current;
+ }
+ current = (View) parent;
+ parent = current.getParent();
+ }
+
+ return null;
+ }
+
/**
* Resets all touch state in preparation for a new cycle.
*/
@@ -3258,8 +3329,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
default:
throw new IllegalStateException("descendant focusability must be "
- + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
- + "but is " + descendantFocusability);
+ + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
+ + "but is " + descendantFocusability);
}
if (result && !isLayoutValid() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) {
mPrivateFlags |= PFLAG_WANTS_FOCUS;
@@ -4923,7 +4994,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
- throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
+ throw new IllegalArgumentException(
+ "generateDefaultLayoutParams() cannot return null ");
}
}
addView(child, index, params);
@@ -5811,6 +5883,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK
& ~PFLAG_DRAWING_CACHE_VALID)
| PFLAG_DRAWN | PFLAG_INVALIDATED;
+ child.setDetached(false);
this.mPrivateFlags |= PFLAG_INVALIDATED;
if (child.hasFocus()) {
@@ -5839,6 +5912,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @see #removeDetachedView(View, boolean)
*/
protected void detachViewFromParent(View child) {
+ child.setDetached(true);
removeFromArray(indexOfChild(child));
}
@@ -5860,6 +5934,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @see #removeDetachedView(View, boolean)
*/
protected void detachViewFromParent(int index) {
+ if (index >= 0 && index < mChildrenCount) {
+ mChildren[index].setDetached(true);
+ }
removeFromArray(index);
}
@@ -5882,6 +5959,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @see #removeDetachedView(View, boolean)
*/
protected void detachViewsFromParent(int start, int count) {
+ start = Math.max(0, start);
+ final int end = Math.min(mChildrenCount, start + count);
+ for (int i = start; i < end; i++) {
+ mChildren[i].setDetached(true);
+ }
removeFromArray(start, count);
}
@@ -5911,6 +5993,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
for (int i = count - 1; i >= 0; i--) {
children[i].mParent = null;
+ children[i].setDetached(true);
children[i] = null;
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a77c7ea17999..6552e30dde69 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1227,28 +1227,19 @@ public final class ViewRootImpl implements ViewParent,
}
}
- @UnsupportedAppUsage
- public void detachFunctor(long functor) {
- if (mAttachInfo.mThreadedRenderer != null) {
- // Fence so that any pending invokeFunctor() messages will be processed
- // before we return from detachFunctor.
- mAttachInfo.mThreadedRenderer.stopDrawing();
- }
- }
+ /**
+ * Does nothing; Here only because of @UnsupportedAppUsage
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
+ publicAlternatives = "Use {@link android.webkit.WebView} instead")
+ public void detachFunctor(long functor) { }
/**
- * Schedules the functor for execution in either kModeProcess or
- * kModeProcessNoContext, depending on whether or not there is an EGLContext.
- *
- * @param functor The native functor to invoke
- * @param waitForCompletion If true, this will not return until the functor
- * has invoked. If false, the functor may be invoked
- * asynchronously.
+ * Does nothing; Here only because of @UnsupportedAppUsage
*/
- @UnsupportedAppUsage
- public static void invokeFunctor(long functor, boolean waitForCompletion) {
- ThreadedRenderer.invokeFunctor(functor, waitForCompletion);
- }
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
+ publicAlternatives = "Use {@link android.webkit.WebView} instead")
+ public static void invokeFunctor(long functor, boolean waitForCompletion) { }
/**
* @param animator animator to register with the hardware renderer
@@ -1727,8 +1718,10 @@ public final class ViewRootImpl implements ViewParent,
destroySurface();
}
}
+ scheduleConsumeBatchedInputImmediately();
}
+
/** Register callbacks to be notified when the ViewRootImpl surface changes. */
interface SurfaceChangedCallback {
void surfaceCreated(Transaction t);
@@ -1781,6 +1774,7 @@ public final class ViewRootImpl implements ViewParent,
.setContainerLayer()
.setName("Bounds for - " + getTitle().toString())
.setParent(getRenderSurfaceControl())
+ .setCallsite("ViewRootImpl.getBoundsLayer")
.build();
setBoundsLayerCrop();
mTransaction.show(mBoundsLayer).apply();
@@ -1822,13 +1816,19 @@ public final class ViewRootImpl implements ViewParent,
/**
* Called after window layout to update the bounds surface. If the surface insets have changed
* or the surface has resized, update the bounds surface.
+ *
+ * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl.
*/
- private void updateBoundsLayer() {
+ private void updateBoundsLayer(boolean shouldReparent) {
if (mBoundsLayer != null) {
setBoundsLayerCrop();
- mTransaction.deferTransactionUntil(mBoundsLayer,
- getRenderSurfaceControl(), mSurface.getNextFrameNumber())
- .apply();
+ mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
+ mSurface.getNextFrameNumber());
+
+ if (shouldReparent) {
+ mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl());
+ }
+ mTransaction.apply();
}
}
@@ -2257,7 +2257,8 @@ public final class ViewRootImpl implements ViewParent,
mLastWindowInsets = mInsetsController.calculateInsets(
mContext.getResources().getConfiguration().isScreenRound(),
mAttachInfo.mAlwaysConsumeSystemBars, mPendingDisplayCutout.get(),
- mWindowAttributes.softInputMode, (mWindowAttributes.systemUiVisibility
+ mWindowAttributes.softInputMode, mWindowAttributes.flags,
+ (mWindowAttributes.systemUiVisibility
| mWindowAttributes.subtreeSystemUiVisibility));
Rect visibleInsets = mInsetsController.calculateVisibleInsets(
@@ -2723,7 +2724,6 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mThreadedRenderer.isEnabled()) {
mAttachInfo.mThreadedRenderer.destroy();
}
- notifySurfaceDestroyed();
} else if ((surfaceReplaced
|| surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged)
&& mSurfaceHolder == null
@@ -2911,7 +2911,16 @@ public final class ViewRootImpl implements ViewParent,
}
if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
- updateBoundsLayer();
+ // If the surface has been replaced, there's a chance the bounds layer is not parented
+ // to the new layer. When updating bounds layer, also reparent to the main VRI
+ // SurfaceControl to ensure it's correctly placed in the hierarchy.
+ //
+ // This needs to be done on the client side since WMS won't reparent the children to the
+ // new surface if it thinks the app is closing. WMS gets the signal that the app is
+ // stopping, but on the client side it doesn't get stopped since it's restarted quick
+ // enough. WMS doesn't want to keep around old children since they will leak when the
+ // client creates new children.
+ updateBoundsLayer(surfaceReplaced);
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
@@ -2954,6 +2963,10 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ if (surfaceDestroyed) {
+ notifySurfaceDestroyed();
+ }
+
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
@@ -4019,7 +4032,7 @@ public final class ViewRootImpl implements ViewParent,
yOffset -= surfaceInsets.top;
// Offset dirty rect for surface insets.
- dirty.offset(surfaceInsets.left, surfaceInsets.right);
+ dirty.offset(surfaceInsets.left, surfaceInsets.top);
}
boolean accessibilityFocusDirty = false;
@@ -8121,7 +8134,9 @@ public final class ViewRootImpl implements ViewParent,
}
void scheduleConsumeBatchedInput() {
- if (!mConsumeBatchedInputScheduled) {
+ // If anything is currently scheduled to consume batched input then there's no point in
+ // scheduling it again.
+ if (!mConsumeBatchedInputScheduled && !mConsumeBatchedInputImmediatelyScheduled) {
mConsumeBatchedInputScheduled = true;
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
mConsumedBatchedInputRunnable, null);
@@ -8144,22 +8159,15 @@ public final class ViewRootImpl implements ViewParent,
}
}
- void doConsumeBatchedInput(long frameTimeNanos) {
- if (mConsumeBatchedInputScheduled) {
- mConsumeBatchedInputScheduled = false;
- if (mInputEventReceiver != null) {
- if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
- && frameTimeNanos != -1) {
- // If we consumed a batch here, we want to go ahead and schedule the
- // consumption of batched input events on the next frame. Otherwise, we would
- // wait until we have more input events pending and might get starved by other
- // things occurring in the process. If the frame time is -1, however, then
- // we're in a non-batching mode, so there's no need to schedule this.
- scheduleConsumeBatchedInput();
- }
- }
- doProcessInputEvents();
+ boolean doConsumeBatchedInput(long frameTimeNanos) {
+ final boolean consumedBatches;
+ if (mInputEventReceiver != null) {
+ consumedBatches = mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos);
+ } else {
+ consumedBatches = false;
}
+ doProcessInputEvents();
+ return consumedBatches;
}
final class TraversalRunnable implements Runnable {
@@ -8203,8 +8211,11 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void onBatchedInputEventPending(int source) {
+ // mStopped: There will be no more choreographer callbacks if we are stopped,
+ // so we must consume all input immediately to prevent ANR
final boolean unbuffered = mUnbufferedInputDispatch
- || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE;
+ || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE
+ || mStopped;
if (unbuffered) {
if (mConsumeBatchedInputScheduled) {
unscheduleConsumeBatchedInput();
@@ -8232,7 +8243,14 @@ public final class ViewRootImpl implements ViewParent,
final class ConsumeBatchedInputRunnable implements Runnable {
@Override
public void run() {
- doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
+ mConsumeBatchedInputScheduled = false;
+ if (doConsumeBatchedInput(mChoreographer.getFrameTimeNanos())) {
+ // If we consumed a batch here, we want to go ahead and schedule the
+ // consumption of batched input events on the next frame. Otherwise, we would
+ // wait until we have more input events pending and might get starved by other
+ // things occurring in the process.
+ scheduleConsumeBatchedInput();
+ }
}
}
final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable =
@@ -8242,6 +8260,7 @@ public final class ViewRootImpl implements ViewParent,
final class ConsumeBatchedInputImmediatelyRunnable implements Runnable {
@Override
public void run() {
+ mConsumeBatchedInputImmediatelyScheduled = false;
doConsumeBatchedInput(-1);
}
}
@@ -9382,6 +9401,11 @@ public final class ViewRootImpl implements ViewParent,
return mInputEventReceiver.getToken();
}
+ @NonNull
+ public IBinder getWindowToken() {
+ return mAttachInfo.mWindowToken;
+ }
+
/**
* Class for managing the accessibility interaction connection
* based on the global accessibility state.
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 4d6b72f96aab..5e94758b003c 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -17,7 +17,6 @@
package android.view;
-import static android.view.WindowInsets.Type.CAPTION_BAR;
import static android.view.WindowInsets.Type.DISPLAY_CUTOUT;
import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.IME;
@@ -95,7 +94,7 @@ public final class WindowInsets {
private final boolean mStableInsetsConsumed;
private final boolean mDisplayCutoutConsumed;
- private final int mCompatInsetTypes;
+ private final int mCompatInsetsTypes;
private final boolean mCompatIgnoreVisibility;
/**
@@ -150,8 +149,8 @@ public final class WindowInsets {
@Nullable Insets[] typeMaxInsetsMap,
boolean[] typeVisibilityMap,
boolean isRound,
- boolean alwaysConsumeSystemBars, DisplayCutout displayCutout, int compatInsetTypes,
- boolean compatIgnoreVisibility) {
+ boolean alwaysConsumeSystemBars, DisplayCutout displayCutout,
+ @InsetsType int compatInsetsTypes, boolean compatIgnoreVisibility) {
mSystemWindowInsetsConsumed = typeInsetsMap == null;
mTypeInsetsMap = mSystemWindowInsetsConsumed
? new Insets[SIZE]
@@ -165,7 +164,7 @@ public final class WindowInsets {
mTypeVisibilityMap = typeVisibilityMap;
mIsRound = isRound;
mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
- mCompatInsetTypes = compatInsetTypes;
+ mCompatInsetsTypes = compatInsetsTypes;
mCompatIgnoreVisibility = compatIgnoreVisibility;
mDisplayCutoutConsumed = displayCutout == null;
@@ -183,7 +182,7 @@ public final class WindowInsets {
src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
src.mTypeVisibilityMap, src.mIsRound,
src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
- src.mCompatInsetTypes,
+ src.mCompatInsetsTypes,
src.mCompatIgnoreVisibility);
}
@@ -310,11 +309,11 @@ public final class WindowInsets {
@NonNull
public Insets getSystemWindowInsets() {
Insets result = mCompatIgnoreVisibility
- ? getInsetsIgnoringVisibility(mCompatInsetTypes & ~ime())
- : getInsets(mCompatInsetTypes);
+ ? getInsetsIgnoringVisibility(mCompatInsetsTypes & ~ime())
+ : getInsets(mCompatInsetsTypes);
// We can't query max insets for IME, so we need to add it manually after.
- if ((mCompatInsetTypes & ime()) != 0 && mCompatIgnoreVisibility) {
+ if ((mCompatInsetsTypes & ime()) != 0 && mCompatIgnoreVisibility) {
result = Insets.max(result, getInsets(ime()));
}
return result;
@@ -503,7 +502,7 @@ public final class WindowInsets {
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
null /* displayCutout */,
- mCompatInsetTypes, mCompatIgnoreVisibility);
+ mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -554,7 +553,7 @@ public final class WindowInsets {
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
displayCutoutCopyConstructorArgument(this),
- mCompatInsetTypes, mCompatIgnoreVisibility);
+ mCompatInsetsTypes, mCompatIgnoreVisibility);
}
// TODO(b/119190588): replace @code with @link below
@@ -627,7 +626,7 @@ public final class WindowInsets {
@Deprecated
@NonNull
public Insets getStableInsets() {
- return getInsets(mTypeMaxInsetsMap, mCompatInsetTypes);
+ return getInsets(mTypeMaxInsetsMap, mCompatInsetsTypes);
}
/**
@@ -939,7 +938,7 @@ public final class WindowInsets {
: mDisplayCutout == null
? DisplayCutout.NO_CUTOUT
: mDisplayCutout.inset(left, top, right, bottom),
- mCompatInsetTypes, mCompatIgnoreVisibility);
+ mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@Override
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 2fac35f57929..1d54d9251abb 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1229,32 +1229,54 @@ public interface WindowManager extends ViewManager {
* @hide
*/
@IntDef(prefix = "TYPE_", value = {
- TYPE_ACCESSIBILITY_OVERLAY,
+ TYPE_BASE_APPLICATION,
TYPE_APPLICATION,
- TYPE_APPLICATION_ATTACHED_DIALOG,
- TYPE_APPLICATION_MEDIA,
- TYPE_APPLICATION_OVERLAY,
- TYPE_APPLICATION_PANEL,
TYPE_APPLICATION_STARTING,
- TYPE_APPLICATION_SUB_PANEL,
- TYPE_BASE_APPLICATION,
TYPE_DRAWN_APPLICATION,
- TYPE_INPUT_METHOD,
- TYPE_INPUT_METHOD_DIALOG,
- TYPE_KEYGUARD,
- TYPE_KEYGUARD_DIALOG,
- TYPE_PHONE,
- TYPE_PRIORITY_PHONE,
- TYPE_PRIVATE_PRESENTATION,
- TYPE_SEARCH_BAR,
+ TYPE_APPLICATION_PANEL,
+ TYPE_APPLICATION_MEDIA,
+ TYPE_APPLICATION_SUB_PANEL,
+ TYPE_APPLICATION_ATTACHED_DIALOG,
+ TYPE_APPLICATION_MEDIA_OVERLAY,
+ TYPE_APPLICATION_ABOVE_SUB_PANEL,
TYPE_STATUS_BAR,
- TYPE_STATUS_BAR_PANEL,
+ TYPE_SEARCH_BAR,
+ TYPE_PHONE,
TYPE_SYSTEM_ALERT,
+ TYPE_KEYGUARD,
+ TYPE_TOAST,
+ TYPE_SYSTEM_OVERLAY,
+ TYPE_PRIORITY_PHONE,
TYPE_SYSTEM_DIALOG,
+ TYPE_KEYGUARD_DIALOG,
TYPE_SYSTEM_ERROR,
- TYPE_SYSTEM_OVERLAY,
- TYPE_TOAST,
+ TYPE_INPUT_METHOD,
+ TYPE_INPUT_METHOD_DIALOG,
TYPE_WALLPAPER,
+ TYPE_STATUS_BAR_PANEL,
+ TYPE_SECURE_SYSTEM_OVERLAY,
+ TYPE_DRAG,
+ TYPE_STATUS_BAR_SUB_PANEL,
+ TYPE_POINTER,
+ TYPE_NAVIGATION_BAR,
+ TYPE_VOLUME_OVERLAY,
+ TYPE_BOOT_PROGRESS,
+ TYPE_INPUT_CONSUMER,
+ TYPE_NAVIGATION_BAR_PANEL,
+ TYPE_DISPLAY_OVERLAY,
+ TYPE_MAGNIFICATION_OVERLAY,
+ TYPE_PRIVATE_PRESENTATION,
+ TYPE_VOICE_INTERACTION,
+ TYPE_ACCESSIBILITY_OVERLAY,
+ TYPE_VOICE_INTERACTION_STARTING,
+ TYPE_DOCK_DIVIDER,
+ TYPE_QS_DIALOG,
+ TYPE_SCREENSHOT,
+ TYPE_PRESENTATION,
+ TYPE_APPLICATION_OVERLAY,
+ TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+ TYPE_NOTIFICATION_SHADE,
+ TYPE_STATUS_BAR_ADDITIONAL
})
@Retention(RetentionPolicy.SOURCE)
public @interface WindowType {}
@@ -1444,11 +1466,22 @@ public interface WindowManager extends ViewManager {
@Deprecated
public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;
- /** Window flag: When set, input method can't interact with the focusable window
- * and can be placed to use more space and cover the input method.
- * Note: When combined with {@link #FLAG_NOT_FOCUSABLE}, this flag has no
- * effect since input method cannot interact with windows having {@link #FLAG_NOT_FOCUSABLE}
- * flag set.
+ /** Window flag: when set, inverts the input method focusability of the window.
+ *
+ * The effect of setting this flag depends on whether {@link #FLAG_NOT_FOCUSABLE} is set:
+ * <p>
+ * If {@link #FLAG_NOT_FOCUSABLE} is <em>not</em> set, i.e. when the window is focusable,
+ * setting this flag prevents this window from becoming the target of the input method.
+ * Consequently, it will <em>not</em> be able to interact with the input method,
+ * and will be layered above the input method (unless there is another input method
+ * target above it).
+ *
+ * <p>
+ * If {@link #FLAG_NOT_FOCUSABLE} <em>is</em> set, setting this flag requests for the window
+ * to be the input method target even though the window is <em>not</em> focusable.
+ * Consequently, it will be layered below the input method.
+ * Note: Windows that set {@link #FLAG_NOT_FOCUSABLE} cannot interact with the input method,
+ * regardless of this flag.
*/
public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;
@@ -1704,6 +1737,46 @@ public interface WindowManager extends ViewManager {
public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000;
/**
+ * @hide
+ */
+ @IntDef(flag = true, prefix = "FLAG_", value = {
+ FLAG_ALLOW_LOCK_WHILE_SCREEN_ON,
+ FLAG_DIM_BEHIND,
+ FLAG_BLUR_BEHIND,
+ FLAG_NOT_FOCUSABLE,
+ FLAG_NOT_TOUCHABLE,
+ FLAG_NOT_TOUCH_MODAL,
+ FLAG_TOUCHABLE_WHEN_WAKING,
+ FLAG_KEEP_SCREEN_ON,
+ FLAG_LAYOUT_IN_SCREEN,
+ FLAG_LAYOUT_NO_LIMITS,
+ FLAG_FULLSCREEN,
+ FLAG_FORCE_NOT_FULLSCREEN,
+ FLAG_DITHER,
+ FLAG_SECURE,
+ FLAG_SCALED,
+ FLAG_IGNORE_CHEEK_PRESSES,
+ FLAG_LAYOUT_INSET_DECOR,
+ FLAG_ALT_FOCUSABLE_IM,
+ FLAG_WATCH_OUTSIDE_TOUCH,
+ FLAG_SHOW_WHEN_LOCKED,
+ FLAG_SHOW_WALLPAPER,
+ FLAG_TURN_SCREEN_ON,
+ FLAG_DISMISS_KEYGUARD,
+ FLAG_SPLIT_TOUCH,
+ FLAG_HARDWARE_ACCELERATED,
+ FLAG_LAYOUT_IN_OVERSCAN,
+ FLAG_TRANSLUCENT_STATUS,
+ FLAG_TRANSLUCENT_NAVIGATION,
+ FLAG_LOCAL_FOCUS_MODE,
+ FLAG_SLIPPERY,
+ FLAG_LAYOUT_ATTACHED_IN_DECOR,
+ FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
+ /**
* Various behavioral options/flags. Default is none.
*
* @see #FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
@@ -1798,6 +1871,7 @@ public interface WindowManager extends ViewManager {
@ViewDebug.FlagToString(mask = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, equals = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
name = "DRAWS_SYSTEM_BAR_BACKGROUNDS")
}, formatToHexString = true)
+ @Flags
public int flags;
/**
@@ -2016,6 +2090,7 @@ public interface WindowManager extends ViewManager {
* @hide
*/
public static final int PRIVATE_FLAG_TRUSTED_OVERLAY = 0x20000000;
+
/**
* An internal annotation for flags that can be specified to {@link #softInputMode}.
*
@@ -2030,6 +2105,38 @@ public interface WindowManager extends ViewManager {
public @interface SystemFlags {}
/**
+ * @hide
+ */
+ @IntDef(flag = true, prefix="PRIVATE_FLAG_", value = {
+ PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED,
+ PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED,
+ PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS,
+ SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
+ PRIVATE_FLAG_NO_MOVE_ANIMATION,
+ PRIVATE_FLAG_COMPATIBLE_WINDOW,
+ PRIVATE_FLAG_SYSTEM_ERROR,
+ PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
+ PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR,
+ PRIVATE_FLAG_PRESERVE_GEOMETRY,
+ PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
+ PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH,
+ PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
+ PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS,
+ PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
+ PRIVATE_FLAG_IS_SCREEN_DECOR,
+ PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION,
+ PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
+ PRIVATE_FLAG_USE_BLAST,
+ PRIVATE_FLAG_APPEARANCE_CONTROLLED,
+ PRIVATE_FLAG_BEHAVIOR_CONTROLLED,
+ PRIVATE_FLAG_FIT_INSETS_CONTROLLED,
+ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ })
+ public @interface PrivateFlags {}
+
+ /**
* Control flags that are private to the platform.
* @hide
*/
@@ -2116,6 +2223,10 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
name = "COLOR_SPACE_AGNOSTIC"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_USE_BLAST,
+ equals = PRIVATE_FLAG_USE_BLAST,
+ name = "USE_BLAST"),
+ @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_APPEARANCE_CONTROLLED,
equals = PRIVATE_FLAG_APPEARANCE_CONTROLLED,
name = "APPEARANCE_CONTROLLED"),
@@ -2132,6 +2243,7 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_TRUSTED_OVERLAY,
name = "TRUSTED_OVERLAY")
})
+ @PrivateFlags
@TestApi
public int privateFlags;
@@ -2141,13 +2253,12 @@ public interface WindowManager extends ViewManager {
* focus. In particular, this checks the
* {@link #FLAG_NOT_FOCUSABLE} and {@link #FLAG_ALT_FOCUSABLE_IM}
* flags and returns true if the combination of the two corresponds
- * to a window that needs to be behind the input method so that the
- * user can type into it.
+ * to a window that can use the input method.
*
* @param flags The current window manager flags.
*
- * @return Returns {@code true} if such a window should be behind/interact
- * with an input method, {@code false} if not.
+ * @return Returns {@code true} if a window with the given flags would be able to
+ * use the input method, {@code false} if not.
*/
public static boolean mayUseInputMethod(int flags) {
return (flags & FLAG_NOT_FOCUSABLE) != FLAG_NOT_FOCUSABLE
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 28a18da37b3e..2fe7c021bb21 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -276,7 +276,7 @@ public final class WindowManagerImpl implements WindowManager {
if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
- SOFT_INPUT_ADJUST_NOTHING,
+ SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */);
} else {
return new WindowInsets.Builder()
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 492ab6f8a3d5..8c355202b63f 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -49,6 +49,13 @@ public interface WindowManagerPolicyConstants {
int PRESENCE_INTERNAL = 1 << 0;
int PRESENCE_EXTERNAL = 1 << 1;
+ // Alternate bars position values
+ int ALT_BAR_UNKNOWN = -1;
+ int ALT_BAR_LEFT = 1 << 0;
+ int ALT_BAR_RIGHT = 1 << 1;
+ int ALT_BAR_BOTTOM = 1 << 2;
+ int ALT_BAR_TOP = 1 << 3;
+
// Navigation bar position values
int NAV_BAR_INVALID = -1;
int NAV_BAR_LEFT = 1 << 0;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7bdc8f162614..060311ec3da8 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -137,7 +137,8 @@ public class WindowlessWindowManager implements IWindowSession {
.setParent(mRootSurface)
.setFormat(attrs.format)
.setBufferSize(getSurfaceWidth(attrs), getSurfaceHeight(attrs))
- .setName(attrs.getTitle().toString());
+ .setName(attrs.getTitle().toString())
+ .setCallsite("WindowlessWindowManager.addToDisplay");
final SurfaceControl sc = b.build();
if (((attrs.inputFeatures &
@@ -249,7 +250,7 @@ public class WindowlessWindowManager implements IWindowSession {
if (viewFlags == View.VISIBLE) {
t.setBufferSize(sc, getSurfaceWidth(attrs), getSurfaceHeight(attrs))
.setOpaque(sc, isOpaque(attrs)).show(sc).apply();
- outSurfaceControl.copyFrom(sc);
+ outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
} else {
t.hide(sc).apply();
outSurfaceControl.release();
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 5d50515a1227..708e27771ef2 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -1768,7 +1768,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* <strong>Note:</strong> The primary usage of this API is for UI test automation
* and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo}
* the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
- * flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
+ * flag when configuring their {@link android.accessibilityservice.AccessibilityService}.
* </p>
* <p>
* <strong>Note:</strong> If this view hierarchy has a {@link SurfaceView} embedding another
@@ -3206,7 +3206,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* <strong>Note:</strong> The primary usage of this API is for UI test automation
* and in order to report the source view id of an {@link AccessibilityNodeInfo} the
* client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
- * flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
+ * flag when configuring their {@link android.accessibilityservice.AccessibilityService}.
* </p>
* @return The id resource name.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index fbfeda6f0bcc..b398cf6c9cb3 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -727,16 +727,22 @@ public final class AutofillManager {
mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
if (mSessionId != NO_SESSION) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
final AutofillClient client = getClient();
if (client != null) {
final SyncResultReceiver receiver = new SyncResultReceiver(
SYNC_CALLS_TIMEOUT_MS);
try {
- mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
- mServiceClient.asBinder(), receiver);
- final boolean sessionWasRestored = receiver.getIntResult() == 1;
+ boolean sessionWasRestored = false;
+ if (clientAdded) {
+ mService.restoreSession(mSessionId,
+ client.autofillClientGetActivityToken(),
+ mServiceClient.asBinder(), receiver);
+ sessionWasRestored = receiver.getIntResult() == 1;
+ } else {
+ Log.w(TAG, "No service client for session " + mSessionId);
+ }
if (!sessionWasRestored) {
Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -850,8 +856,8 @@ public final class AutofillManager {
if (isDisabledByServiceLocked()) {
return false;
}
- ensureServiceClientAddedIfNeededLocked();
- return mEnabled;
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+ return clientAdded ? mEnabled : false;
}
}
@@ -1007,7 +1013,12 @@ public final class AutofillManager {
AutofillCallback callback = null;
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+ if (!clientAdded) {
+ if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+ return callback;
+ }
if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
@@ -1060,9 +1071,10 @@ public final class AutofillManager {
@GuardedBy("mLock")
void notifyViewExitedLocked(@NonNull View view) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
- if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+ if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+ && isActiveLocked()) {
// dont notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = view.getAutofillId();
@@ -1178,7 +1190,12 @@ public final class AutofillManager {
AutofillCallback callback = null;
if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+ if (!clientAdded) {
+ if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+ return callback;
+ }
if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
if (sVerbose) {
@@ -1241,9 +1258,10 @@ public final class AutofillManager {
@GuardedBy("mLock")
private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
- if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+ if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+ && isActiveLocked()) {
// don't notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view, virtualId);
@@ -1905,11 +1923,16 @@ public final class AutofillManager {
}
}
+ /**
+ * Tries to add AutofillManagerClient to service if it does not been added. Returns {@code true}
+ * if the AutofillManagerClient is added successfully or is already added. Otherwise,
+ * returns {@code false}.
+ */
@GuardedBy("mLock")
- private void ensureServiceClientAddedIfNeededLocked() {
+ private boolean tryAddServiceClientIfNeededLocked() {
final AutofillClient client = getClient();
if (client == null) {
- return;
+ return false;
}
if (mServiceClient == null) {
@@ -1924,7 +1947,10 @@ public final class AutofillManager {
flags = receiver.getIntResult();
} catch (SyncResultReceiver.TimeoutException e) {
Log.w(TAG, "Failed to initialize autofill: " + e);
- return;
+ // Reset the states initialized above.
+ mService.removeClient(mServiceClient, userId);
+ mServiceClient = null;
+ return false;
}
mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
@@ -1949,6 +1975,7 @@ public final class AutofillManager {
throw e.rethrowFromSystemServer();
}
}
+ return true;
}
@GuardedBy("mLock")
@@ -1962,12 +1989,13 @@ public final class AutofillManager {
&& view.isLaidOut()
&& view.isVisibleToUser()) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
if (sVerbose) {
- Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
+ Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled + " mServiceClient="
+ + mServiceClient);
}
- if (mEnabled && !isClientDisablingEnterExitEvent()) {
+ if (clientAdded && mEnabled && !isClientDisablingEnterExitEvent()) {
final AutofillId id = view.getAutofillId();
final AutofillValue value = view.getAutofillValue();
// Starts new session.
@@ -2692,6 +2720,7 @@ public final class AutofillManager {
pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
pw.print(pfx); pw.print("context: "); pw.println(mContext);
+ pw.print(pfx); pw.print("service client: "); pw.println(mServiceClient);
final AutofillClient client = getClient();
if (client != null) {
pw.print(pfx); pw.print("client: "); pw.print(client);
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 3f5ef5a2651d..64c59e54e85b 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -43,8 +43,7 @@ import java.security.SecureRandom;
import java.util.ArrayList;
/**
- * Session used when the Android a system-provided content capture service
- * about events associated with views.
+ * Session used when notifying the Android system about events associated with views.
*/
public abstract class ContentCaptureSession implements AutoCloseable {
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 07fef76961f9..c5f2299e4f83 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -27,6 +27,7 @@ import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Printer;
@@ -567,7 +568,8 @@ public class EditorInfo implements InputType, Parcelable {
* editor wants to trim out the first 10 chars, subTextStart should be 10.
*/
public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) {
- Objects.requireNonNull(subText);
+ CharSequence newSubText = Editable.Factory.getInstance().newEditable(subText);
+ Objects.requireNonNull(newSubText);
// Swap selection start and end if necessary.
final int subTextSelStart = initialSelStart > initialSelEnd
@@ -575,7 +577,7 @@ public class EditorInfo implements InputType, Parcelable {
final int subTextSelEnd = initialSelStart > initialSelEnd
? initialSelStart - subTextStart : initialSelEnd - subTextStart;
- final int subTextLength = subText.length();
+ final int subTextLength = newSubText.length();
// Unknown or invalid selection.
if (subTextStart < 0 || subTextSelStart < 0 || subTextSelEnd > subTextLength) {
mInitialSurroundingText = new InitialSurroundingText();
@@ -589,12 +591,12 @@ public class EditorInfo implements InputType, Parcelable {
}
if (subTextLength <= MEMORY_EFFICIENT_TEXT_LENGTH) {
- mInitialSurroundingText = new InitialSurroundingText(subText, subTextSelStart,
+ mInitialSurroundingText = new InitialSurroundingText(newSubText, subTextSelStart,
subTextSelEnd);
return;
}
- trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd);
+ trimLongSurroundingText(newSubText, subTextSelStart, subTextSelEnd);
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 864c40f8a740..793d94097862 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -19,8 +19,8 @@ package android.view.inputmethod;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR;
-import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR;
+import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION;
+import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
@@ -89,6 +89,7 @@ import com.android.internal.view.InputBindResult;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collections;
@@ -610,14 +611,13 @@ public final class InputMethodManager {
@Override
public void startInputAsyncOnWindowFocusGain(View focusedView,
@SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) {
- final boolean forceNewFocus1 = forceNewFocus;
final int startInputFlags = getStartInputFlags(focusedView, 0);
final ImeFocusController controller = getFocusController();
if (controller == null) {
return;
}
- if (controller.checkFocus(forceNewFocus1, false)) {
+ if (controller.checkFocus(forceNewFocus, false)) {
// We need to restart input on the current focus view. This
// should be done in conjunction with telling the system service
// about the window gaining focus, to help make the transition
@@ -633,20 +633,21 @@ public final class InputMethodManager {
// we'll just do a window focus gain and call it a day.
try {
View servedView = controller.getServedView();
- boolean nextFocusIsServedView = servedView != null && servedView == focusedView;
+ boolean nextFocusHasConnection = servedView != null && servedView == focusedView
+ && hasActiveConnection(focusedView);
if (DEBUG) {
Log.v(TAG, "Reporting focus gain, without startInput"
- + ", nextFocusIsServedView=" + nextFocusIsServedView);
+ + ", nextFocusIsServedView=" + nextFocusHasConnection);
}
final int startInputReason =
- nextFocusIsServedView ? WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR
- : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR;
+ nextFocusHasConnection ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION
+ : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION;
mService.startInputOrWindowGainedFocus(
startInputReason, mClient,
focusedView.getWindowToken(), startInputFlags, softInputMode,
windowFlags,
- nextFocusIsServedView ? mCurrentTextBoxAttribute : null,
- nextFocusIsServedView ? mServedInputConnectionWrapper : null,
+ null,
+ null,
0 /* missingMethodFlags */,
mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
@@ -671,10 +672,6 @@ public final class InputMethodManager {
@Override
public void setCurrentRootView(ViewRootImpl rootView) {
synchronized (mH) {
- if (mCurRootView != null) {
- // Restart the input when the next window focus state of the root view changed.
- mRestartOnNextWindowFocus = true;
- }
mCurRootView = rootView;
}
}
@@ -704,14 +701,24 @@ public final class InputMethodManager {
}
/**
- * For {@link ImeFocusController} to check if the currently served view is accepting full
- * text edits.
+ * Checks whether the active input connection (if any) is for the given view.
+ *
+ * TODO(b/160968797): Remove this method and move mServedInputConnectionWrapper to
+ * ImeFocusController.
+ *
+ * Note that this method is only intended for restarting input after focus gain
+ * (e.g. b/160391516), DO NOT leverage this method to do another check.
*/
@Override
- public boolean isAcceptingText() {
+ public boolean hasActiveConnection(View view) {
synchronized (mH) {
+ if (!hasServedByInputMethodLocked(view)) {
+ return false;
+ }
+
return mServedInputConnectionWrapper != null
- && mServedInputConnectionWrapper.getInputConnection() != null;
+ && mServedInputConnectionWrapper.isActive()
+ && mServedInputConnectionWrapper.mServedView.get() == view;
}
}
}
@@ -964,11 +971,13 @@ public final class InputMethodManager {
private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
private final InputMethodManager mParentInputMethodManager;
+ private final WeakReference<View> mServedView;
- public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn,
- final InputMethodManager inputMethodManager) {
+ ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn,
+ InputMethodManager inputMethodManager, View servedView) {
super(mainLooper, conn);
mParentInputMethodManager = inputMethodManager;
+ mServedView = new WeakReference<>(servedView);
}
@Override
@@ -991,6 +1000,7 @@ public final class InputMethodManager {
+ "connection=" + getInputConnection()
+ " finished=" + isFinished()
+ " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
+ + " mServedView=" + mServedView.get()
+ "}";
}
}
@@ -1171,7 +1181,8 @@ public final class InputMethodManager {
mMainLooper = looper;
mH = new H(looper);
mDisplayId = displayId;
- mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this);
+ mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this,
+ null);
}
/**
@@ -1954,7 +1965,7 @@ public final class InputMethodManager {
icHandler = ic.getHandler();
}
servedContext = new ControlledInputConnectionWrapper(
- icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this);
+ icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view);
} else {
servedContext = null;
missingMethodFlags = 0;
@@ -2111,28 +2122,36 @@ public final class InputMethodManager {
/**
* Call showSoftInput with currently focused view.
- * @return {@code true} if IME can be shown.
+ *
+ * @param windowToken the window from which this request originates. If this doesn't match the
+ * currently served view, the request is ignored and returns {@code false}.
+ *
+ * @return {@code true} if IME can (eventually) be shown, {@code false} otherwise.
* @hide
*/
- public boolean requestImeShow(ResultReceiver resultReceiver) {
+ public boolean requestImeShow(IBinder windowToken) {
synchronized (mH) {
final View servedView = getServedViewLocked();
- if (servedView == null) {
+ if (servedView == null || servedView.getWindowToken() != windowToken) {
return false;
}
- showSoftInput(servedView, 0 /* flags */, resultReceiver);
+ showSoftInput(servedView, 0 /* flags */, null /* resultReceiver */);
return true;
}
}
/**
* Notify IME directly that it is no longer visible.
+ *
+ * @param windowToken the window from which this request originates. If this doesn't match the
+ * currently served view, the request is ignored.
* @hide
*/
- public void notifyImeHidden() {
+ public void notifyImeHidden(IBinder windowToken) {
synchronized (mH) {
try {
- if (mCurMethod != null) {
+ if (mCurMethod != null && mCurRootView != null
+ && mCurRootView.getWindowToken() == windowToken) {
mCurMethod.notifyImeHidden();
}
} catch (RemoteException re) {
@@ -2142,15 +2161,15 @@ public final class InputMethodManager {
/**
* Notify IME directly to remove surface as it is no longer visible.
+ * @param windowToken The client window token that requests the IME to remove its surface.
* @hide
*/
- public void removeImeSurface() {
+ public void removeImeSurface(IBinder windowToken) {
synchronized (mH) {
try {
- if (mCurMethod != null) {
- mCurMethod.removeImeSurface();
- }
- } catch (RemoteException re) {
+ mService.removeImeSurfaceFromWindow(windowToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f9a713ac7fcc..052bca57d77c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -55,6 +55,7 @@ import android.view.ViewGroup;
import android.view.ViewHierarchyEncoder;
import android.view.ViewStructure;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -1510,7 +1511,7 @@ public class WebView extends AbsoluteLayout
*
* @param hosts the list of hosts
* @param callback will be called with {@code true} if hosts are successfully added to the
- * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
+ * allowlist. It will be called with {@code false} if any hosts are malformed. The callback
* will be run on the UI thread
*/
public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
@@ -2449,6 +2450,14 @@ public class WebView extends AbsoluteLayout
WebView.super.startActivityForResult(intent, requestCode);
}
+ /**
+ * @see View#onApplyWindowInsets(WindowInsets)
+ */
+ @Nullable
+ public WindowInsets super_onApplyWindowInsets(@Nullable WindowInsets insets) {
+ return WebView.super.onApplyWindowInsets(insets);
+ }
+
// ---- Access to non-public methods ----
public void overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
@@ -3078,4 +3087,11 @@ public class WebView extends AbsoluteLayout
encoder.addProperty("webview:url", mProvider.getUrl());
encoder.addProperty("webview:originalUrl", mProvider.getOriginalUrl());
}
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ WindowInsets result = mProvider.getViewDelegate().onApplyWindowInsets(insets);
+ if (result == null) return super.onApplyWindowInsets(insets);
+ return result;
+ }
}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 150fa88a36e3..7b6e1a370479 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -173,8 +173,9 @@ public class WebViewClient {
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
- * Browsing checks. If this is undesired, whitelist the URL with {@link
- * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+ * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+ * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+ * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
@@ -211,8 +212,9 @@ public class WebViewClient {
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
- * Browsing checks. If this is undesired, whitelist the URL with {@link
- * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+ * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+ * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+ * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index df86926a95dc..950dc7300ba9 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -77,73 +77,41 @@ public final class WebViewDelegate {
}
/**
- * Returns {@code true} if the draw GL functor can be invoked (see {@link #invokeDrawGlFunctor})
- * and {@code false} otherwise.
- *
+ * Throws {@link UnsupportedOperationException}
* @deprecated Use {@link #drawWebViewFunctor(Canvas, int)}
*/
@Deprecated
public boolean canInvokeDrawGlFunctor(View containerView) {
- return true;
+ throw new UnsupportedOperationException();
}
/**
- * Invokes the draw GL functor. If waitForCompletion is {@code false} the functor
- * may be invoked asynchronously.
- *
- * @param nativeDrawGLFunctor the pointer to the native functor that implements
- * system/core/include/utils/Functor.h
+ * Throws {@link UnsupportedOperationException}
* @deprecated Use {@link #drawWebViewFunctor(Canvas, int)}
*/
@Deprecated
public void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor,
boolean waitForCompletion) {
- ViewRootImpl.invokeFunctor(nativeDrawGLFunctor, waitForCompletion);
+ throw new UnsupportedOperationException();
}
/**
- * Calls the function specified with the nativeDrawGLFunctor functor pointer. This
- * functionality is used by the WebView for calling into their renderer from the
- * framework display lists.
- *
- * @param canvas a hardware accelerated canvas (see {@link Canvas#isHardwareAccelerated()})
- * @param nativeDrawGLFunctor the pointer to the native functor that implements
- * system/core/include/utils/Functor.h
- * @throws IllegalArgumentException if the canvas is not hardware accelerated
+ * Throws {@link UnsupportedOperationException}
* @deprecated Use {@link #drawWebViewFunctor(Canvas, int)}
*/
@Deprecated
public void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor) {
- if (!(canvas instanceof RecordingCanvas)) {
- // Canvas#isHardwareAccelerated() is only true for subclasses of HardwareCanvas.
- throw new IllegalArgumentException(canvas.getClass().getName()
- + " is not a DisplayList canvas");
- }
- ((RecordingCanvas) canvas).drawGLFunctor2(nativeDrawGLFunctor, null);
+ throw new UnsupportedOperationException();
}
/**
- * Calls the function specified with the nativeDrawGLFunctor functor pointer. This
- * functionality is used by the WebView for calling into their renderer from the
- * framework display lists.
- *
- * @param canvas a hardware accelerated canvas (see {@link Canvas#isHardwareAccelerated()})
- * @param nativeDrawGLFunctor the pointer to the native functor that implements
- * system/core/include/utils/Functor.h
- * @param releasedRunnable Called when this nativeDrawGLFunctor is no longer referenced by this
- * canvas, so is safe to be destroyed.
- * @throws IllegalArgumentException if the canvas is not hardware accelerated
+ * Throws {@link UnsupportedOperationException}
* @deprecated Use {@link #drawWebViewFunctor(Canvas, int)}
*/
@Deprecated
public void callDrawGlFunction(@NonNull Canvas canvas, long nativeDrawGLFunctor,
@Nullable Runnable releasedRunnable) {
- if (!(canvas instanceof RecordingCanvas)) {
- // Canvas#isHardwareAccelerated() is only true for subclasses of HardwareCanvas.
- throw new IllegalArgumentException(canvas.getClass().getName()
- + " is not a DisplayList canvas");
- }
- ((RecordingCanvas) canvas).drawGLFunctor2(nativeDrawGLFunctor, releasedRunnable);
+ throw new UnsupportedOperationException();
}
/**
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 010c0b7bd4ab..18a110b2be0f 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -39,6 +39,7 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -448,6 +449,12 @@ public interface WebViewProvider {
default boolean onCheckIsTextEditor() {
return false;
}
+
+ @SuppressWarnings("unused")
+ @Nullable
+ default WindowInsets onApplyWindowInsets(@Nullable WindowInsets insets) {
+ return null;
+ }
}
interface ScrollDelegate {
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 1b899dbf6d03..33e64f4d37e9 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -38,8 +38,6 @@ import android.view.ViewGroup;
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.util.Locale;
/**
@@ -264,7 +262,7 @@ class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelega
mShowWeekNumber = a.getBoolean(R.styleable.CalendarView_showWeekNumber,
DEFAULT_SHOW_WEEK_NUMBER);
mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+ Calendar.getInstance().getFirstDayOfWeek());
final String minDate = a.getString(R.styleable.CalendarView_minDate);
if (!CalendarView.parseDate(minDate, mMinDate)) {
CalendarView.parseDate(DEFAULT_MIN_DATE, mMinDate);
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 5820f4bdafad..135ff9fcd989 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -183,14 +183,14 @@ public abstract class CompoundButton extends Button implements Checkable {
public void setStateDescription(@Nullable CharSequence stateDescription) {
mCustomStateDescription = stateDescription;
if (stateDescription == null) {
- setDefaultStateDescritption();
+ setDefaultStateDescription();
} else {
super.setStateDescription(stateDescription);
}
}
/** @hide **/
- protected void setDefaultStateDescritption() {
+ protected void setDefaultStateDescription() {
if (mCustomStateDescription == null) {
super.setStateDescription(getButtonStateDescription());
}
@@ -210,6 +210,8 @@ public abstract class CompoundButton extends Button implements Checkable {
// Avoid infinite recursions if setChecked() is called from a listener
if (mBroadcasting) {
+ // setStateDescription will not send out event if the description is unchanged.
+ setDefaultStateDescription();
return;
}
@@ -228,7 +230,7 @@ public abstract class CompoundButton extends Button implements Checkable {
mBroadcasting = false;
}
// setStateDescription will not send out event if the description is unchanged.
- setDefaultStateDescritption();
+ setDefaultStateDescription();
}
/**
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index 096e6ea52c8a..fd89b2e09131 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -34,8 +34,6 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.DatePicker.AbstractDatePickerDelegate;
import android.widget.NumberPicker.OnValueChangeListener;
-import libcore.icu.ICU;
-
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -459,7 +457,7 @@ class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate {
// We use numeric spinners for year and day, but textual months. Ask icu4c what
// order the user's locale uses for that combination. http://b/7207103.
String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd");
- char[] order = ICU.getDateFormatOrder(pattern);
+ char[] order = DateFormat.getDateFormatOrder(pattern);
final int spinnerCount = order.length;
for (int i = 0; i < spinnerCount; i++) {
switch (order[i]) {
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 67fef13d23f2..7de2bd10482f 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -33,10 +33,6 @@ import com.android.internal.R;
import com.android.internal.widget.ViewPager;
import com.android.internal.widget.ViewPager.OnPageChangeListener;
-import libcore.icu.LocaleData;
-
-import java.util.Locale;
-
class DayPickerView extends ViewGroup {
private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material;
private static final int DEFAULT_START_YEAR = 1900;
@@ -86,7 +82,7 @@ class DayPickerView extends ViewGroup {
attrs, a, defStyleAttr, defStyleRes);
final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+ Calendar.getInstance().getFirstDayOfWeek());
final String minDate = a.getString(R.styleable.CalendarView_minDate);
final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 346bbb6fd409..c4eb39626d8b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6053,9 +6053,8 @@ public class Editor {
return trueLine;
}
- final int lineHeight = layout.getLineBottom(prevLine) - layout.getLineTop(prevLine);
- int slop = (int)(mLineSlopRatio
- * (layout.getLineBottom(trueLine) - layout.getLineTop(trueLine)));
+ final int lineHeight = mTextView.getLineHeight();
+ int slop = (int)(mLineSlopRatio * lineHeight);
slop = Math.max(mLineChangeSlopMin,
Math.min(mLineChangeSlopMax, lineHeight + slop)) - lineHeight;
slop = Math.max(0, slop);
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index ff3ac0732aa2..9eb63087a66e 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -174,12 +174,9 @@ public class EditorTouchState {
int touchSlop = config.getScaledTouchSlop();
mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
if (mMovedEnoughForDrag) {
- // If the direction of the swipe motion is within 30 degrees of vertical, it is
- // considered a vertical drag. We don't actually have to compute the angle to
- // implement the check though. When the angle is exactly 30 degrees from
- // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from
- // vertical, 2*deltaX < distance.
- mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared;
+ // If the direction of the swipe motion is within 45 degrees of vertical, it is
+ // considered a vertical drag.
+ mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY);
}
}
} else if (action == MotionEvent.ACTION_CANCEL) {
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index c63e20f1a010..89206fda39f3 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -998,6 +998,7 @@ public final class Magnifier {
.setName("magnifier surface")
.setFlags(SurfaceControl.HIDDEN)
.setParent(parentSurfaceControl)
+ .setCallsite("InternalPopupWindow")
.build();
mSurface = new Surface();
mSurface.copyFrom(mSurfaceControl);
@@ -1141,7 +1142,7 @@ public final class Magnifier {
bitmapRenderNode.setOutline(outline);
bitmapRenderNode.setClipToOutline(true);
- // Create a dummy draw, which will be replaced later with real drawing.
+ // Create a placeholder draw, which will be replaced later with real drawing.
final RecordingCanvas canvas = bitmapRenderNode.beginRecording(
mContentWidth, mContentHeight);
try {
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index baaf2a763487..3b482a8b2d54 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -34,6 +34,7 @@ import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.icu.text.DecimalFormatSymbols;
import android.os.Build;
import android.os.Bundle;
import android.text.InputFilter;
@@ -61,8 +62,6 @@ import android.view.inputmethod.InputMethodManager;
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -209,7 +208,7 @@ public class NumberPicker extends LinearLayout {
}
private static char getZeroDigit(Locale locale) {
- return LocaleData.get(locale).zeroDigit;
+ return DecimalFormatSymbols.getInstance(locale).getZeroDigit();
}
private java.util.Formatter createFormatter(Locale locale) {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 4b32e10d083b..35605c4c6f6e 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -69,6 +69,7 @@ import com.android.internal.R;
import java.text.NumberFormat;
import java.util.ArrayList;
+import java.util.Locale;
/**
* <p>
@@ -249,6 +250,9 @@ public class ProgressBar extends View {
private ObjectAnimator mLastProgressAnimator;
+ private NumberFormat mPercentFormat;
+ private Locale mCachedLocale;
+
/**
* Create a new progress bar with range 0...100 and initial progress of 0.
* @param context the application environment
@@ -1591,8 +1595,15 @@ public class ProgressBar extends View {
* @return state description based on progress
*/
private CharSequence formatStateDescription(int progress) {
- return NumberFormat.getPercentInstance(mContext.getResources().getConfiguration().locale)
- .format(getPercent(progress));
+ // Cache the locale-appropriate NumberFormat. Configuration locale is guaranteed
+ // non-null, so the first time this is called we will always get the appropriate
+ // NumberFormat, then never regenerate it unless the locale changes on the fly.
+ final Locale curLocale = mContext.getResources().getConfiguration().getLocales().get(0);
+ if (!curLocale.equals(mCachedLocale)) {
+ mCachedLocale = curLocale;
+ mPercentFormat = NumberFormat.getPercentInstance(curLocale);
+ }
+ return mPercentFormat.format(getPercent(progress));
}
/**
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 843700cef55e..32c68bdd0491 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -108,7 +108,7 @@ public final class SelectionActionModeHelper {
*
* @return the swap result, index 0 is the start index and index 1 is the end index.
*/
- private static int[] sortSelctionIndices(int selectionStart, int selectionEnd) {
+ private static int[] sortSelectionIndices(int selectionStart, int selectionEnd) {
if (selectionStart < selectionEnd) {
return new int[]{selectionStart, selectionEnd};
}
@@ -122,11 +122,11 @@ public final class SelectionActionModeHelper {
* @param textView the selected TextView.
* @return the swap result, index 0 is the start index and index 1 is the end index.
*/
- private static int[] sortSelctionIndicesFromTextView(TextView textView) {
+ private static int[] sortSelectionIndicesFromTextView(TextView textView) {
int selectionStart = textView.getSelectionStart();
int selectionEnd = textView.getSelectionEnd();
- return sortSelctionIndices(selectionStart, selectionEnd);
+ return sortSelectionIndices(selectionStart, selectionEnd);
}
/**
@@ -135,7 +135,7 @@ public final class SelectionActionModeHelper {
public void startSelectionActionModeAsync(boolean adjustSelection) {
// Check if the smart selection should run for editable text.
adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled();
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onOriginalSelection(
getText(mTextView),
@@ -165,7 +165,7 @@ public final class SelectionActionModeHelper {
* Starts Link ActionMode.
*/
public void startLinkActionModeAsync(int start, int end) {
- int[] indexResult = sortSelctionIndices(start, end);
+ int[] indexResult = sortSelectionIndices(start, end);
mSelectionTracker.onOriginalSelection(getText(mTextView), indexResult[0], indexResult[1],
true /*isLink*/);
cancelAsyncTask();
@@ -201,21 +201,21 @@ public final class SelectionActionModeHelper {
/** Reports a selection action event. */
public void onSelectionAction(int menuItemId, @Nullable String actionLabel) {
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionAction(
sortedSelectionIndices[0], sortedSelectionIndices[1],
getActionType(menuItemId), actionLabel, mTextClassification);
}
public void onSelectionDrag() {
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionAction(
sortedSelectionIndices[0], sortedSelectionIndices[1],
SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification);
}
public void onTextChanged(int start, int end) {
- int[] sortedSelectionIndices = sortSelctionIndices(start, end);
+ int[] sortedSelectionIndices = sortSelectionIndices(start, end);
mSelectionTracker.onTextChanged(sortedSelectionIndices[0], sortedSelectionIndices[1],
mTextClassification);
}
@@ -334,7 +334,7 @@ public final class SelectionActionModeHelper {
startSelectionActionMode(startSelectionResult);
};
// TODO do not trigger the animation if the change included only non-printable characters
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
final boolean didSelectionChange =
result != null && (sortedSelectionIndices[0] != result.mStart
|| sortedSelectionIndices[1] != result.mEnd);
@@ -486,7 +486,7 @@ public final class SelectionActionModeHelper {
if (actionMode != null) {
actionMode.invalidate();
}
- final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionUpdated(
sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification);
mTextClassificationAsyncTask = null;
@@ -495,7 +495,7 @@ public final class SelectionActionModeHelper {
private void resetTextClassificationHelper(int selectionStart, int selectionEnd) {
if (selectionStart < 0 || selectionEnd < 0) {
// Use selection indices
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
selectionStart = sortedSelectionIndices[0];
selectionEnd = sortedSelectionIndices[1];
}
@@ -637,7 +637,7 @@ public final class SelectionActionModeHelper {
mAllowReset = false;
boolean selected = editor.selectCurrentWord();
if (selected) {
- final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(textView);
+ final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(textView);
mSelectionStart = sortedSelectionIndices[0];
mSelectionEnd = sortedSelectionIndices[1];
mLogger.logSelectionAction(
@@ -1215,7 +1215,7 @@ public final class SelectionActionModeHelper {
SelectionResult(int start, int end,
@Nullable TextClassification classification, @Nullable TextSelection selection) {
- int[] sortedIndices = sortSelctionIndices(start, end);
+ int[] sortedIndices = sortSelectionIndices(start, end);
mStart = sortedIndices[0];
mEnd = sortedIndices[1];
mClassification = classification;
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 61c77bc2f90e..695a253a04e0 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -27,6 +27,7 @@ import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.icu.text.DateFormatSymbols;
import android.icu.text.DisplayContext;
import android.icu.text.RelativeDateTimeFormatter;
import android.icu.text.SimpleDateFormat;
@@ -50,8 +51,6 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
import com.android.internal.widget.ExploreByTouchHelper;
-import libcore.icu.LocaleData;
-
import java.text.NumberFormat;
import java.util.Locale;
@@ -194,7 +193,8 @@ class SimpleMonthView extends View {
private void updateDayOfWeekLabels() {
// Use tiny (e.g. single-character) weekday names from ICU. The indices
// for this list correspond to Calendar days, e.g. SUNDAY is index 1.
- final String[] tinyWeekdayNames = LocaleData.get(mLocale).tinyWeekdayNames;
+ final String[] tinyWeekdayNames = DateFormatSymbols.getInstance(mLocale)
+ .getWeekdays(DateFormatSymbols.FORMAT, DateFormatSymbols.NARROW);
for (int i = 0; i < DAYS_IN_WEEK; i++) {
mDayOfWeekLabels[i] = tinyWeekdayNames[(mWeekStart + i - 1) % DAYS_IN_WEEK + 1];
}
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index e1fd77624a29..5bca56f14add 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -311,7 +311,7 @@ public class Switch extends CompoundButton {
refreshDrawableState();
// Default state is derived from on/off-text, so state has to be updated when on/off-text
// are updated.
- setDefaultStateDescritption();
+ setDefaultStateDescription();
setChecked(isChecked());
}
@@ -856,7 +856,7 @@ public class Switch extends CompoundButton {
requestLayout();
// Default state is derived from on/off-text, so state has to be updated when on/off-text
// are updated.
- setDefaultStateDescritption();
+ setDefaultStateDescription();
}
/**
@@ -879,7 +879,7 @@ public class Switch extends CompoundButton {
requestLayout();
// Default state is derived from on/off-text, so state has to be updated when on/off-text
// are updated.
- setDefaultStateDescritption();
+ setDefaultStateDescription();
}
/**
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 6432438b6630..95c0e8658c57 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -30,6 +30,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.database.ContentObserver;
+import android.icu.text.DateTimePatternGenerator;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
@@ -43,8 +44,6 @@ import android.view.inspector.InspectableProperty;
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.util.Calendar;
import java.util.TimeZone;
@@ -262,14 +261,11 @@ public class TextClock extends TextView {
}
private void init() {
- if (mFormat12 == null || mFormat24 == null) {
- LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
- if (mFormat12 == null) {
- mFormat12 = ld.timeFormat_hm;
- }
- if (mFormat24 == null) {
- mFormat24 = ld.timeFormat_Hm;
- }
+ if (mFormat12 == null) {
+ mFormat12 = getBestDateTimePattern("hm");
+ }
+ if (mFormat24 == null) {
+ mFormat24 = getBestDateTimePattern("Hm");
}
createTime(mTimeZone);
@@ -510,13 +506,11 @@ public class TextClock extends TextView {
private void chooseFormat() {
final boolean format24Requested = is24HourModeEnabled();
- LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
-
if (format24Requested) {
- mFormat = abc(mFormat24, mFormat12, ld.timeFormat_Hm);
+ mFormat = abc(mFormat24, mFormat12, getBestDateTimePattern("Hm"));
mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat);
} else {
- mFormat = abc(mFormat12, mFormat24, ld.timeFormat_hm);
+ mFormat = abc(mFormat12, mFormat24, getBestDateTimePattern("hm"));
mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat);
}
@@ -529,6 +523,12 @@ public class TextClock extends TextView {
}
}
+ private String getBestDateTimePattern(String skeleton) {
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+ getContext().getResources().getConfiguration().locale);
+ return dtpg.getBestPattern(skeleton);
+ }
+
/**
* Returns a if not null, else return b if not null, else return c.
*/
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index eaa4c573b0e4..6f14dfb89e6b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11003,17 +11003,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* not among the handles.
*/
boolean isFromPrimePointer(MotionEvent event, boolean fromHandleView) {
+ boolean res = true;
if (mPrimePointerId == NO_POINTER_ID) {
mPrimePointerId = event.getPointerId(0);
mIsPrimePointerFromHandleView = fromHandleView;
} else if (mPrimePointerId != event.getPointerId(0)) {
- return mIsPrimePointerFromHandleView && fromHandleView;
+ res = mIsPrimePointerFromHandleView && fromHandleView;
}
if (event.getActionMasked() == MotionEvent.ACTION_UP
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
mPrimePointerId = -1;
}
- return true;
+ return res;
}
@Override
diff --git a/core/java/android/widget/TextViewRichContentReceiver.java b/core/java/android/widget/TextViewRichContentReceiver.java
index 5678a1e177d8..4f2d95466997 100644
--- a/core/java/android/widget/TextViewRichContentReceiver.java
+++ b/core/java/android/widget/TextViewRichContentReceiver.java
@@ -116,9 +116,6 @@ final class TextViewRichContentReceiver implements RichContentReceiver<TextView>
private static boolean onReceiveForAutofill(@NonNull TextView textView, @NonNull ClipData clip,
@Flags int flags) {
final CharSequence text = coerceToText(clip, textView.getContext(), flags);
- if (text.length() == 0) {
- return false;
- }
// First autofill it...
textView.setText(text);
// ...then move cursor to the end.
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 51b18473f1ac..1c219eb95479 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -24,9 +24,11 @@ import android.annotation.Widget;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
+import android.icu.text.DateFormatSymbols;
import android.icu.util.Calendar;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
@@ -39,8 +41,6 @@ import android.view.inspector.InspectableProperty;
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
@@ -421,11 +421,13 @@ public class TimePicker extends FrameLayout {
static String[] getAmPmStrings(Context context) {
final Locale locale = context.getResources().getConfiguration().locale;
- final LocaleData d = LocaleData.get(locale);
+ DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(locale);
+ String[] amPm = dfs.getAmPmStrings();
+ String[] narrowAmPm = dfs.getAmpmNarrowStrings();
final String[] result = new String[2];
- result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0];
- result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1];
+ result[0] = amPm[0].length() > 4 ? narrowAmPm[0] : amPm[0];
+ result[1] = amPm[1].length() > 4 ? narrowAmPm[1] : amPm[1];
return result;
}
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 83c86d5ce36b..bd2fa5965bc9 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -35,8 +35,6 @@ import android.view.inputmethod.InputMethodManager;
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.util.Calendar;
/**
@@ -143,7 +141,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
// Get the localized am/pm strings and use them in the spinner.
- mAmPmStrings = getAmPmStrings(context);
+ mAmPmStrings = TimePicker.getAmPmStrings(context);
// am/pm
final View amPmView = mDelegator.findViewById(R.id.amPm);
@@ -574,12 +572,4 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
target.setContentDescription(mContext.getString(contDescResId));
}
}
-
- public static String[] getAmPmStrings(Context context) {
- String[] result = new String[2];
- LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
- result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0];
- result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1];
- return result;
- }
}
diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index 59e0c16cde37..24061a2ab05b 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -60,7 +60,7 @@ public class ToggleButton extends CompoundButton {
syncTextState();
// Default state is derived from on/off-text, so state has to be updated when on/off-text
// are updated.
- setDefaultStateDescritption();
+ setDefaultStateDescription();
a.recycle();
}
@@ -111,7 +111,7 @@ public class ToggleButton extends CompoundButton {
mTextOn = textOn;
// Default state is derived from on/off-text, so state has to be updated when on/off-text
// are updated.
- setDefaultStateDescritption();
+ setDefaultStateDescription();
}
/**
@@ -133,7 +133,7 @@ public class ToggleButton extends CompoundButton {
mTextOff = textOff;
// Default state is derived from on/off-text, so state has to be updated when on/off-text
// are updated.
- setDefaultStateDescritption();
+ setDefaultStateDescription();
}
/**
diff --git a/core/java/android/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java
index 1b666aa67e90..9712311aab7c 100644
--- a/core/java/android/widget/inline/InlineContentView.java
+++ b/core/java/android/widget/inline/InlineContentView.java
@@ -21,8 +21,8 @@ import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl;
@@ -156,7 +156,8 @@ public class InlineContentView extends ViewGroup {
@Override
public void onDraw() {
computeParentPositionAndScale();
- mSurfaceView.setVisibility(VISIBLE);
+ final int visibility = InlineContentView.this.isShown() ? VISIBLE : GONE;
+ mSurfaceView.setVisibility(visibility);
}
};
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 3daf6d371450..f035d36a0f71 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -68,6 +68,14 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
public static final int FEATURE_WINDOWED_MAGNIFICATION = FEATURE_SYSTEM_FIRST + 4;
/**
+ * Display area that can be magnified in
+ * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN}. This is different from
+ * {@link #FEATURE_WINDOWED_MAGNIFICATION} that the whole display will be magnified.
+ * @hide
+ */
+ public static final int FEATURE_FULLSCREEN_MAGNIFICATION = FEATURE_SYSTEM_FIRST + 5;
+
+ /**
* The last boundary of display area for system features
*/
public static final int FEATURE_SYSTEM_LAST = 10_000;
diff --git a/core/java/android/window/TaskEmbedder.java b/core/java/android/window/TaskEmbedder.java
index ca6c568c2668..0687a037df32 100644
--- a/core/java/android/window/TaskEmbedder.java
+++ b/core/java/android/window/TaskEmbedder.java
@@ -164,6 +164,7 @@ public abstract class TaskEmbedder {
.setContainerLayer()
.setParent(parent)
.setName(name)
+ .setCallsite("TaskEmbedder.initialize")
.build();
if (!onInitialize()) {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a30c3c52f42c..3a89dcd96487 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -819,7 +819,8 @@ public class ChooserActivity extends ResolverActivity implements
if (chooserListAdapter.getCount() == 0) {
return;
}
- if (resultList.isEmpty()) {
+ if (resultList.isEmpty()
+ && shouldQueryShortcutManager(chooserListAdapter.getUserHandle())) {
// APS may be disabled, so try querying targets ourselves.
queryDirectShareTargets(chooserListAdapter, true);
return;
@@ -2025,6 +2026,29 @@ public class ChooserActivity extends ResolverActivity implements
});
}
+ /**
+ * Returns {@code false} if {@code userHandle} is the work profile and it's either
+ * in quiet mode or not running.
+ */
+ private boolean shouldQueryShortcutManager(UserHandle userHandle) {
+ if (!shouldShowTabs()) {
+ return true;
+ }
+ if (!getWorkProfileUserHandle().equals(userHandle)) {
+ return true;
+ }
+ if (!isUserRunning(userHandle)) {
+ return false;
+ }
+ if (!isUserUnlocked(userHandle)) {
+ return false;
+ }
+ if (isQuietModeEnabled(userHandle)) {
+ return false;
+ }
+ return true;
+ }
+
private void sendChooserTargetRankingScore(List<AppTarget> chooserTargetScores,
UserHandle userHandle) {
final Message msg = Message.obtain();
@@ -2616,7 +2640,10 @@ public class ChooserActivity extends ResolverActivity implements
}
RecyclerView recyclerView = mChooserMultiProfilePagerAdapter.getActiveAdapterView();
ChooserGridAdapter gridAdapter = mChooserMultiProfilePagerAdapter.getCurrentRootAdapter();
- if (gridAdapter == null || recyclerView == null) {
+ // Skip height calculation if recycler view was scrolled to prevent it inaccurately
+ // calculating the height, as the logic below does not account for the scrolled offset.
+ if (gridAdapter == null || recyclerView == null
+ || recyclerView.computeVerticalScrollOffset() != 0) {
return;
}
@@ -2839,8 +2866,8 @@ public class ChooserActivity extends ResolverActivity implements
return;
}
- // no need to query direct share for work profile when its turned off
- if (isQuietModeEnabled(chooserListAdapter.getUserHandle())) {
+ // no need to query direct share for work profile when its locked or disabled
+ if (!shouldQueryShortcutManager(chooserListAdapter.getUserHandle())) {
getChooserActivityLogger().logSharesheetAppLoadComplete();
return;
}
@@ -2865,6 +2892,18 @@ public class ChooserActivity extends ResolverActivity implements
}
@VisibleForTesting
+ protected boolean isUserRunning(UserHandle userHandle) {
+ UserManager userManager = getSystemService(UserManager.class);
+ return userManager.isUserRunning(userHandle);
+ }
+
+ @VisibleForTesting
+ protected boolean isUserUnlocked(UserHandle userHandle) {
+ UserManager userManager = getSystemService(UserManager.class);
+ return userManager.isUserUnlocked(userHandle);
+ }
+
+ @VisibleForTesting
protected boolean isQuietModeEnabled(UserHandle userHandle) {
UserManager userManager = getSystemService(UserManager.class);
return userManager.isQuietModeEnabled(userHandle);
@@ -3091,6 +3130,11 @@ public class ChooserActivity extends ResolverActivity implements
ChooserGridAdapter currentRootAdapter =
mChooserMultiProfilePagerAdapter.getCurrentRootAdapter();
currentRootAdapter.updateDirectShareExpansion();
+ // This fixes an edge case where after performing a variety of gestures, vertical scrolling
+ // ends up disabled. That's because at some point the old tab's vertical scrolling is
+ // disabled and the new tab's is enabled. For context, see b/159997845
+ setVerticalScrollEnabled(true);
+ mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
}
@Override
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 5efd46c4c64a..00b5cb646bca 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -95,6 +95,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
mSelectableTargetInfoCommunicator;
private int mNumShortcutResults = 0;
+ private Map<DisplayResolveInfo, LoadIconTask> mIconLoaders = new HashMap<>();
// Reserve spots for incoming direct share targets by adding placeholders
private ChooserTargetInfo
@@ -239,11 +240,42 @@ public class ChooserListAdapter extends ResolverListAdapter {
@Override
protected void onBindView(View view, TargetInfo info, int position) {
- super.onBindView(view, info, position);
- if (info == null) return;
+ final ViewHolder holder = (ViewHolder) view.getTag();
+ if (info == null) {
+ holder.icon.setImageDrawable(
+ mContext.getDrawable(R.drawable.resolver_icon_placeholder));
+ return;
+ }
+
+ if (!(info instanceof DisplayResolveInfo)) {
+ holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
+ holder.bindIcon(info);
+
+ if (info instanceof SelectableTargetInfo) {
+ // direct share targets should append the application name for a better readout
+ DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
+ CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
+ CharSequence extendedInfo = info.getExtendedInfo();
+ String contentDescription = String.join(" ", info.getDisplayLabel(),
+ extendedInfo != null ? extendedInfo : "", appName);
+ holder.updateContentDescription(contentDescription);
+ }
+ } else {
+ DisplayResolveInfo dri = (DisplayResolveInfo) info;
+ holder.bindLabel(dri.getDisplayLabel(), dri.getExtendedInfo(), alwaysShowSubLabel());
+ LoadIconTask task = mIconLoaders.get(dri);
+ if (task == null) {
+ task = new LoadIconTask(dri, holder);
+ mIconLoaders.put(dri, task);
+ task.execute();
+ } else {
+ // The holder was potentially changed as the underlying items were
+ // reshuffled, so reset the target holder
+ task.setViewHolder(holder);
+ }
+ }
// If target is loading, show a special placeholder shape in the label, make unclickable
- final ViewHolder holder = (ViewHolder) view.getTag();
if (info instanceof ChooserActivity.PlaceHolderTargetInfo) {
final int maxWidth = mContext.getResources().getDimensionPixelSize(
R.dimen.chooser_direct_share_label_placeholder_max_width);
@@ -561,7 +593,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
mChooserTargetScores.put(componentName, new HashMap<>());
}
mChooserTargetScores.get(componentName).put(shortcutInfo.getShortLabel().toString(),
- shortcutInfo.getRank());
+ target.getRank());
}
mChooserTargetScores.keySet().forEach(key -> rankTargetsWithinComponent(key));
}
@@ -885,6 +917,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
if (getAppPredictor() != null) {
getAppPredictor().unregisterPredictionUpdates(mAppPredictorCallback);
getAppPredictor().destroy();
+ setAppPredictor(null);
}
}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index ffa6041721c6..3a65a324f9d6 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -252,8 +252,10 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
@Override
protected void setupContainerPadding(View container) {
+ int initialBottomPadding = getContext().getResources().getDimensionPixelSize(
+ R.dimen.resolver_empty_state_container_padding_bottom);
container.setPadding(container.getPaddingLeft(), container.getPaddingTop(),
- container.getPaddingRight(), container.getPaddingBottom() + mBottomOffset);
+ container.getPaddingRight(), initialBottomPadding + mBottomOffset);
}
class ChooserProfileDescriptor extends ProfileDescriptor {
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 2a7eae626795..986bbc8628ec 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -55,6 +55,10 @@ import org.json.JSONObject;
public class PlatLogoActivity extends Activity {
private static final boolean WRITE_SETTINGS = true;
+ private static final String R_EGG_UNLOCK_SETTING = "egg_mode_r";
+
+ private static final int UNLOCK_TRIES = 3;
+
BigDialView mDialView;
@Override
@@ -77,8 +81,10 @@ public class PlatLogoActivity extends Activity {
mDialView = new BigDialView(this, null);
if (Settings.System.getLong(getContentResolver(),
- "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) {
- mDialView.setUnlockTries(3);
+ R_EGG_UNLOCK_SETTING, 0) == 0) {
+ mDialView.setUnlockTries(UNLOCK_TRIES);
+ } else {
+ mDialView.setUnlockTries(0);
}
final FrameLayout layout = new FrameLayout(this);
@@ -91,18 +97,16 @@ public class PlatLogoActivity extends Activity {
private void launchNextStage(boolean locked) {
final ContentResolver cr = getContentResolver();
- if (Settings.System.getLong(cr, "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) {
- // For posterity: the moment this user unlocked the easter egg
- try {
- if (WRITE_SETTINGS) {
- Settings.System.putLong(cr,
- "egg_mode", // Settings.System.EGG_MODE,
- locked ? 0 : System.currentTimeMillis());
- }
- } catch (RuntimeException e) {
- Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e);
+ try {
+ if (WRITE_SETTINGS) {
+ Settings.System.putLong(cr,
+ R_EGG_UNLOCK_SETTING,
+ locked ? 0 : System.currentTimeMillis());
}
+ } catch (RuntimeException e) {
+ Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e);
}
+
try {
startActivity(new Intent(Intent.ACTION_MAIN)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -235,8 +239,8 @@ public class PlatLogoActivity extends Activity {
}
return true;
case MotionEvent.ACTION_UP:
- if (mWasLocked && !mDialDrawable.isLocked()) {
- launchNextStage(false);
+ if (mWasLocked != mDialDrawable.isLocked()) {
+ launchNextStage(mDialDrawable.isLocked());
}
return true;
}
@@ -404,6 +408,8 @@ public class PlatLogoActivity extends Activity {
if (isLocked() && oldUserLevel != STEPS - 1 && getUserLevel() == STEPS - 1) {
mUnlockTries--;
+ } else if (!isLocked() && getUserLevel() == 0) {
+ mUnlockTries = UNLOCK_TRIES;
}
if (!isLocked()) {
diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
index 81036f7ecba8..719c79b2540f 100644
--- a/core/java/com/android/internal/app/ProcessMap.java
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -58,4 +58,8 @@ public class ProcessMap<E> {
public int size() {
return mMap.size();
}
+
+ public void clear() {
+ mMap.clear();
+ }
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 86c13a0581c2..c3b570331671 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -20,9 +20,6 @@ import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.PermissionChecker.PID_UNKNOWN;
-import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
-import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_WORK;
-
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UiThread;
@@ -159,6 +156,9 @@ public class ResolverActivity extends Activity implements
protected static final String METRICS_CATEGORY_RESOLVER = "intent_resolver";
protected static final String METRICS_CATEGORY_CHOOSER = "intent_chooser";
+ /** Tracks if we should ignore future broadcasts telling us the work profile is enabled */
+ private boolean mWorkProfileHasBeenEnabled = false;
+
@VisibleForTesting
public static boolean ENABLE_TABBED_VIEW = true;
private static final String TAB_TAG_PERSONAL = "personal";
@@ -825,12 +825,23 @@ public class ResolverActivity extends Activity implements
if (shouldShowTabs()) {
mWorkProfileStateReceiver = createWorkProfileStateReceiver();
registerWorkProfileStateReceiver();
+
+ mWorkProfileHasBeenEnabled = isWorkProfileEnabled();
}
}
+ private boolean isWorkProfileEnabled() {
+ UserHandle workUserHandle = getWorkProfileUserHandle();
+ UserManager userManager = getSystemService(UserManager.class);
+
+ return !userManager.isQuietModeEnabled(workUserHandle)
+ && userManager.isUserUnlocked(workUserHandle);
+ }
+
private void registerWorkProfileStateReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
registerReceiverAsUser(mWorkProfileStateReceiver, UserHandle.ALL, filter, null, null);
}
@@ -1191,7 +1202,7 @@ public class ResolverActivity extends Activity implements
final PackageManager pm = getPackageManager();
// Set the preferred Activity
- pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
+ pm.addUniquePreferredActivity(filter, bestMatch, set, intent.getComponent());
if (ri.handleAllWebDataURI) {
// Set default Browser if needed
@@ -1268,13 +1279,17 @@ public class ResolverActivity extends Activity implements
}
private void safelyStartActivityInternal(TargetInfo cti) {
- if (mPersonalPackageMonitor != null) {
- mPersonalPackageMonitor.unregister();
- }
- if (mWorkPackageMonitor != null) {
- mWorkPackageMonitor.unregister();
+ // If the target is suspended, the activity will not be successfully launched.
+ // Do not unregister from package manager updates in this case
+ if (!cti.isSuspended()) {
+ if (mPersonalPackageMonitor != null) {
+ mPersonalPackageMonitor.unregister();
+ }
+ if (mWorkPackageMonitor != null) {
+ mWorkPackageMonitor.unregister();
+ }
+ mRegistered = false;
}
- mRegistered = false;
// If needed, show that intent is forwarded
// from managed profile to owner or other way around.
if (mProfileSwitchMessageId != -1) {
@@ -1961,17 +1976,29 @@ public class ResolverActivity extends Activity implements
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)
- && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
+ && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+ && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_AVAILABLE)) {
return;
}
- int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)
- && userHandle != getWorkProfileUserHandle().getIdentifier()) {
+
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+
+ if (userId != getWorkProfileUserHandle().getIdentifier()) {
return;
}
- if (TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)) {
+
+ if (isWorkProfileEnabled()) {
+ if (mWorkProfileHasBeenEnabled) {
+ return;
+ }
+
+ mWorkProfileHasBeenEnabled = true;
mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
+ } else {
+ // Must be an UNAVAILABLE broadcast, so we watch for the next availability
+ mWorkProfileHasBeenEnabled = false;
}
+
if (mMultiProfilePagerAdapter.getCurrentUserHandle()
.equals(getWorkProfileUserHandle())) {
mMultiProfilePagerAdapter.rebuildActiveTab(true);
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 094fb1e2f23c..c16ee42f4926 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -54,7 +54,6 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
-import com.android.internal.app.chooser.SelectableTargetInfo;
import com.android.internal.app.chooser.TargetInfo;
import java.util.ArrayList;
@@ -68,7 +67,7 @@ public class ResolverListAdapter extends BaseAdapter {
private final List<ResolveInfo> mBaseResolveList;
private final PackageManager mPm;
protected final Context mContext;
- private final ColorMatrixColorFilter mSuspendedMatrixColorFilter;
+ private static ColorMatrixColorFilter sSuspendedMatrixColorFilter;
private final int mIconDpi;
protected ResolveInfo mLastChosen;
private DisplayResolveInfo mOtherProfile;
@@ -103,7 +102,6 @@ public class ResolverListAdapter extends BaseAdapter {
mDisplayList = new ArrayList<>();
mFilterLastUsed = filterLastUsed;
mResolverListController = resolverListController;
- mSuspendedMatrixColorFilter = createSuspendedColorMatrix();
mResolverListCommunicator = resolverListCommunicator;
mIsAudioCaptureDevice = isAudioCaptureDevice;
final ActivityManager am = (ActivityManager) mContext.getSystemService(ACTIVITY_SERVICE);
@@ -541,28 +539,13 @@ public class ResolverListAdapter extends BaseAdapter {
getLoadLabelTask((DisplayResolveInfo) info, holder).execute();
} else {
holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
- if (info instanceof SelectableTargetInfo) {
- // direct share targets should append the application name for a better readout
- DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
- CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
- CharSequence extendedInfo = info.getExtendedInfo();
- String contentDescription = String.join(" ", info.getDisplayLabel(),
- extendedInfo != null ? extendedInfo : "", appName);
- holder.updateContentDescription(contentDescription);
- }
- }
-
- if (info.isSuspended()) {
- holder.icon.setColorFilter(mSuspendedMatrixColorFilter);
- } else {
- holder.icon.setColorFilter(null);
}
if (info instanceof DisplayResolveInfo
&& !((DisplayResolveInfo) info).hasDisplayIcon()) {
- new ResolverListAdapter.LoadIconTask((DisplayResolveInfo) info, holder.icon).execute();
+ new LoadIconTask((DisplayResolveInfo) info, holder).execute();
} else {
- holder.icon.setImageDrawable(info.getDisplayIcon(mContext));
+ holder.bindIcon(info);
}
}
@@ -580,23 +563,27 @@ public class ResolverListAdapter extends BaseAdapter {
}
}
- private ColorMatrixColorFilter createSuspendedColorMatrix() {
- int grayValue = 127;
- float scale = 0.5f; // half bright
+ private static ColorMatrixColorFilter getSuspendedColorMatrix() {
+ if (sSuspendedMatrixColorFilter == null) {
+
+ int grayValue = 127;
+ float scale = 0.5f; // half bright
- ColorMatrix tempBrightnessMatrix = new ColorMatrix();
- float[] mat = tempBrightnessMatrix.getArray();
- mat[0] = scale;
- mat[6] = scale;
- mat[12] = scale;
- mat[4] = grayValue;
- mat[9] = grayValue;
- mat[14] = grayValue;
+ ColorMatrix tempBrightnessMatrix = new ColorMatrix();
+ float[] mat = tempBrightnessMatrix.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = grayValue;
+ mat[9] = grayValue;
+ mat[14] = grayValue;
- ColorMatrix matrix = new ColorMatrix();
- matrix.setSaturation(0.0f);
- matrix.preConcat(tempBrightnessMatrix);
- return new ColorMatrixColorFilter(matrix);
+ ColorMatrix matrix = new ColorMatrix();
+ matrix.setSaturation(0.0f);
+ matrix.preConcat(tempBrightnessMatrix);
+ sSuspendedMatrixColorFilter = new ColorMatrixColorFilter(matrix);
+ }
+ return sSuspendedMatrixColorFilter;
}
ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo ai) {
@@ -615,7 +602,17 @@ public class ResolverListAdapter extends BaseAdapter {
void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) {
final DisplayResolveInfo iconInfo = getFilteredItem();
if (iconView != null && iconInfo != null) {
- new LoadIconTask(iconInfo, iconView).execute();
+ new AsyncTask<Void, Void, Drawable>() {
+ @Override
+ protected Drawable doInBackground(Void... params) {
+ return loadIconForResolveInfo(iconInfo.getResolveInfo());
+ }
+
+ @Override
+ protected void onPostExecute(Drawable d) {
+ iconView.setImageDrawable(d);
+ }
+ }.execute();
}
}
@@ -708,6 +705,15 @@ public class ResolverListAdapter extends BaseAdapter {
public void updateContentDescription(String description) {
itemView.setContentDescription(description);
}
+
+ public void bindIcon(TargetInfo info) {
+ icon.setImageDrawable(info.getDisplayIcon(itemView.getContext()));
+ if (info.isSuspended()) {
+ icon.setColorFilter(getSuspendedColorMatrix());
+ } else {
+ icon.setColorFilter(null);
+ }
+ }
}
protected class LoadLabelTask extends AsyncTask<Void, Void, CharSequence[]> {
@@ -761,14 +767,14 @@ public class ResolverListAdapter extends BaseAdapter {
}
class LoadIconTask extends AsyncTask<Void, Void, Drawable> {
- protected final com.android.internal.app.chooser.DisplayResolveInfo mDisplayResolveInfo;
+ protected final DisplayResolveInfo mDisplayResolveInfo;
private final ResolveInfo mResolveInfo;
- private final ImageView mTargetView;
+ private ViewHolder mHolder;
- LoadIconTask(DisplayResolveInfo dri, ImageView target) {
+ LoadIconTask(DisplayResolveInfo dri, ViewHolder holder) {
mDisplayResolveInfo = dri;
mResolveInfo = dri.getResolveInfo();
- mTargetView = target;
+ mHolder = holder;
}
@Override
@@ -782,9 +788,14 @@ public class ResolverListAdapter extends BaseAdapter {
mResolverListCommunicator.updateProfileViewButton();
} else {
mDisplayResolveInfo.setDisplayIcon(d);
- mTargetView.setImageDrawable(d);
+ mHolder.bindIcon(mDisplayResolveInfo);
}
}
+
+ public void setViewHolder(ViewHolder holder) {
+ mHolder = holder;
+ mHolder.bindIcon(mDisplayResolveInfo);
+ }
}
/**
@@ -822,7 +833,7 @@ public class ResolverListAdapter extends BaseAdapter {
String getAppSubLabelInternal() {
// Will default to app name if no intent filter or activity label set, make sure to
// check if subLabel matches label before final display
- return (String) mRi.loadLabel(mPm);
+ return mRi.loadLabel(mPm).toString();
}
}
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 0589baa76b8a..d8eaeda2b549 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -26,6 +26,7 @@ import android.Manifest;
import android.annotation.Nullable;
import android.app.AlertDialog;
import android.app.AppGlobals;
+import android.app.KeyguardManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
@@ -208,9 +209,32 @@ public class SuspendedAppActivity extends AlertActivity
ap.mPositiveButtonText = getString(android.R.string.ok);
ap.mNeutralButtonText = resolveNeutralButtonText();
ap.mPositiveButtonListener = ap.mNeutralButtonListener = this;
+
+ requestDismissKeyguardIfNeeded(ap.mMessage);
+
setupAlert();
}
+ private void requestDismissKeyguardIfNeeded(CharSequence dismissMessage) {
+ final KeyguardManager km = getSystemService(KeyguardManager.class);
+ if (km.isKeyguardLocked()) {
+ km.requestDismissKeyguard(this, dismissMessage,
+ new KeyguardManager.KeyguardDismissCallback() {
+ @Override
+ public void onDismissError() {
+ Slog.e(TAG, "Error while dismissing keyguard."
+ + " Keeping the dialog visible.");
+ }
+
+ @Override
+ public void onDismissCancelled() {
+ Slog.w(TAG, "Keyguard dismiss was cancelled. Finishing.");
+ SuspendedAppActivity.this.finish();
+ }
+ });
+ }
+ }
+
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index d238d0eb916d..ea3d2de13ce6 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -120,6 +120,13 @@ public final class SystemUiDeviceConfigFlags {
*/
public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";
+ // Flag related to Privacy Indicators
+
+ /**
+ * Whether the Permissions Hub is showing.
+ */
+ public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled";
+
// Flags related to Assistant
/**
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 476198b5311a..bbfd07b64ccf 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -358,6 +358,8 @@ public class NativeLibraryHelper {
createNativeLibrarySubdir(subDir);
}
+ // Even if extractNativeLibs is false, we still need to check if the native libs in the APK
+ // are valid. This is done in the native code.
int copyRet = copyNativeBinaries(handle, subDir, supportedAbi);
if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
return copyRet;
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 085cdfcf9462..37f68233db53 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -46,10 +46,10 @@ public final class InputMethodDebug {
return "UNSPECIFIED";
case StartInputReason.WINDOW_FOCUS_GAIN:
return "WINDOW_FOCUS_GAIN";
- case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR:
- return "WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR";
- case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR:
- return "WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR";
+ case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION:
+ return "WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION";
+ case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION:
+ return "WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION";
case StartInputReason.APP_CALLED_RESTART_INPUT_API:
return "APP_CALLED_RESTART_INPUT_API";
case StartInputReason.CHECK_FOCUS:
diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java
index 946ce858c12d..2ba708dd9312 100644
--- a/core/java/com/android/internal/inputmethod/StartInputReason.java
+++ b/core/java/com/android/internal/inputmethod/StartInputReason.java
@@ -30,8 +30,8 @@ import java.lang.annotation.Retention;
@IntDef(value = {
StartInputReason.UNSPECIFIED,
StartInputReason.WINDOW_FOCUS_GAIN,
- StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR,
- StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR,
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION,
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION,
StartInputReason.APP_CALLED_RESTART_INPUT_API,
StartInputReason.CHECK_FOCUS,
StartInputReason.BOUND_TO_IMMS,
@@ -54,13 +54,13 @@ public @interface StartInputReason {
* view and its input connection remains. {@link android.view.inputmethod.InputMethodManager}
* just reports this window focus change event to sync IME input target for system.
*/
- int WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR = 2;
+ int WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION = 2;
/**
* {@link android.view.Window} gained focus but there is no {@link android.view.View} that is
* eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports
* this window focus change event for logging.
*/
- int WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR = 3;
+ int WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION = 3;
/**
* {@link android.view.inputmethod.InputMethodManager#restartInput(android.view.View)} is
* either explicitly called by the application or indirectly called by some Framework class
diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java
new file mode 100644
index 000000000000..9979e6056f50
--- /dev/null
+++ b/core/java/com/android/internal/listeners/ListenerExecutor.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.listeners;
+
+import android.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+/**
+ * Interface (trait) for executing listener style operations on an executor.
+ */
+public interface ListenerExecutor {
+
+ /**
+ * An listener operation to perform.
+ *
+ * @param <TListener> listener type
+ */
+ interface ListenerOperation<TListener> {
+ /**
+ * Performs the operation on the given listener.
+ */
+ void operate(TListener listener) throws Exception;
+
+ /**
+ * Called before this operation is to be run. Some operations may be canceled before they
+ * are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
+ * will only be run if this method was run. This callback is invoked on the calling thread.
+ */
+ default void onPreExecute() {}
+
+ /**
+ * Called if the operation fails while running. Will not be invoked in the event of a
+ * RuntimeException, which will propagate normally. Implementations of
+ * {@link ListenerExecutor} have the option to override
+ * {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
+ * intercept failures at the class level. This callback is invoked on the executor thread.
+ */
+ default void onFailure(Exception e) {
+ // implementations should handle any exceptions that may be thrown
+ throw new AssertionError(e);
+ }
+
+ /**
+ * Called after the operation is run. This method will always be called if
+ * {@link #onPreExecute()} is called. Success implies that the operation was run to
+ * completion with no failures. This callback may be invoked on the calling thread or
+ * executor thread.
+ */
+ default void onPostExecute(boolean success) {}
+
+ /**
+ * Called after this operation is complete (which does not imply that it was necessarily
+ * run). Will always be called once per operation, no matter if the operation was run or
+ * not. Success implies that the operation was run to completion with no failures. This
+ * callback may be invoked on the calling thread or executor thread.
+ */
+ default void onComplete(boolean success) {}
+ }
+
+ /**
+ * May be override to handle operation failures at a class level. Will not be invoked in the
+ * event of a RuntimeException, which will propagate normally. This callback is invoked on the
+ * executor thread.
+ */
+ default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
+ Exception exception) {
+ operation.onFailure(exception);
+ }
+
+ /**
+ * Executes the given listener operation on the given executor, using the provided listener
+ * supplier. If the supplier returns a null value, or a value during the operation that does not
+ * match the value prior to the operation, then the operation is considered canceled. If a null
+ * operation is supplied, nothing happens.
+ */
+ default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
+ @Nullable ListenerOperation<TListener> operation) {
+ if (operation == null) {
+ return;
+ }
+
+ boolean executing = false;
+ boolean preexecute = false;
+ try {
+ TListener listener = listenerSupplier.get();
+ if (listener == null) {
+ return;
+ }
+
+ operation.onPreExecute();
+ preexecute = true;
+ executor.execute(() -> {
+ boolean success = false;
+ try {
+ if (listener == listenerSupplier.get()) {
+ operation.operate(listener);
+ success = true;
+ }
+ } catch (Exception e) {
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else {
+ onOperationFailure(operation, e);
+ }
+ } finally {
+ operation.onPostExecute(success);
+ operation.onComplete(success);
+ }
+ });
+ executing = true;
+ } finally {
+ if (!executing) {
+ if (preexecute) {
+ operation.onPostExecute(false);
+ }
+ operation.onComplete(false);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/listeners/ListenerTransport.java b/core/java/com/android/internal/listeners/ListenerTransport.java
index 2a52179ff33d..9d6210e5dcc8 100644
--- a/core/java/com/android/internal/listeners/ListenerTransport.java
+++ b/core/java/com/android/internal/listeners/ListenerTransport.java
@@ -45,10 +45,6 @@ public class ListenerTransport<TListener> {
mListener = listener;
}
- final boolean isRegistered() {
- return mListener != null;
- }
-
/**
* Prevents any listener invocations that happen-after this call.
*/
diff --git a/core/java/com/android/internal/listeners/ListenerTransportManager.java b/core/java/com/android/internal/listeners/ListenerTransportManager.java
deleted file mode 100644
index daef88a0f862..000000000000
--- a/core/java/com/android/internal/listeners/ListenerTransportManager.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.listeners;
-
-import android.annotation.NonNull;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.util.Objects;
-
-/**
- * A listener manager which tracks listeners along with their keys. This class enforces proper use
- * of transport objects and ensure unregistration race conditions are handled properly. If listeners
- * should be multiplexed before being sent to the server, see {@link ListenerTransportMultiplexer}
- * instead.
- *
- * @param <TTransport> transport type
- */
-public abstract class ListenerTransportManager<TTransport extends ListenerTransport<?>> {
-
- @GuardedBy("mTransports")
- private final ArrayMap<Object, TTransport> mTransports = new ArrayMap<>();
-
- /**
- * Should be implemented to register the transport with the server.
- *
- * @see #reregisterWithServer(ListenerTransport, ListenerTransport)
- */
- protected abstract void registerWithServer(TTransport transport) throws RemoteException;
-
- /**
- * Invoked when the server already has a transport registered for a key, and it is being
- * replaced with a new transport. The default implementation unregisters the old transport, then
- * registers the new transport, but this may be overridden by subclasses in order to reregister
- * more efficiently.
- */
- protected void reregisterWithServer(TTransport oldTransport, TTransport newTransport)
- throws RemoteException {
- unregisterWithServer(oldTransport);
- registerWithServer(newTransport);
- }
-
- /**
- * Should be implemented to unregister the transport from the server.
- */
- protected abstract void unregisterWithServer(TTransport transport) throws RemoteException;
-
- /**
- * Adds a new transport with the given key and makes a call to add the transport server side. If
- * a transport already exists with that key, it will be replaced by the new transport and
- * {@link #reregisterWithServer(ListenerTransport, ListenerTransport)} will be invoked to
- * replace the old transport with the new transport server side. If no transport exists with
- * that key, it will be added server side via {@link #registerWithServer(ListenerTransport)}.
- */
- protected void registerListener(@NonNull Object key, @NonNull TTransport transport) {
- Objects.requireNonNull(key);
- Objects.requireNonNull(transport);
-
- synchronized (mTransports) {
- TTransport oldTransport = mTransports.put(key, transport);
- if (oldTransport != null) {
- oldTransport.unregister();
- }
-
- Preconditions.checkState(transport.isRegistered());
-
- boolean registered = false;
- try {
- if (oldTransport == null) {
- registerWithServer(transport);
- } else {
- reregisterWithServer(oldTransport, transport);
- }
- registered = true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } finally {
- if (!registered) {
- transport.unregister();
- mTransports.remove(key);
- }
- }
- }
- }
-
- /**
- * Removes the transport with the given key, and makes a call to remove the transport server
- * side via {@link #unregisterWithServer(ListenerTransport)}.
- */
- protected void unregisterListener(@NonNull Object key) {
- Objects.requireNonNull(key);
-
- synchronized (mTransports) {
- TTransport transport = mTransports.remove(key);
- if (transport == null) {
- return;
- }
-
- transport.unregister();
- try {
- unregisterWithServer(transport);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
- * Removes the given transport with the given key if such a mapping exists. This only removes
- * the client registration, it does not make any calls to remove the transport server side. The
- * intended use is for when the transport is already removed server side and only client side
- * cleanup is necessary.
- */
- protected void removeTransport(@NonNull Object key, @NonNull ListenerTransport<?> transport) {
- Objects.requireNonNull(key);
- Objects.requireNonNull(transport);
-
- synchronized (mTransports) {
- TTransport typedTransport = mTransports.get(key);
- if (typedTransport != transport) {
- return;
- }
-
- mTransports.remove(key);
- typedTransport.unregister();
- }
- }
-}
diff --git a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java b/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
index 1721a3e041d4..fc1d69f570ad 100644
--- a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
+++ b/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
@@ -21,13 +21,12 @@ import android.annotation.Nullable;
import android.os.Build;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
@@ -38,8 +37,7 @@ import java.util.function.Consumer;
* A listener multiplexer designed for use by client-side code. This class ensures that listeners
* are never invoked while a lock is held. This class is only useful for multiplexing listeners -
* if all client listeners can be combined into a single server request, and all server results will
- * be delivered to all clients. If this is not the case, see {@link ListenerTransportManager}
- * instead.
+ * be delivered to all clients.
*
* By default, the multiplexer will replace requests on the server simply by registering the new
* request and trusting the server to know this is replacing the old request. If the server needs to
@@ -229,9 +227,7 @@ public abstract class ListenerTransportMultiplexer<TRequest, TListener> {
/**
* Dumps debug information.
*/
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
-
+ public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> registrations;
synchronized (mLock) {
registrations = mRegistrations;
@@ -239,12 +235,12 @@ public abstract class ListenerTransportMultiplexer<TRequest, TListener> {
ipw.print("service: ");
if (mServiceRegistered) {
if (mCurrentRequest == null) {
- pw.print("request registered");
+ ipw.print("request registered");
} else {
- pw.print("request registered - " + mCurrentRequest);
+ ipw.print("request registered - " + mCurrentRequest);
}
} else {
- pw.print("unregistered");
+ ipw.print("unregistered");
}
ipw.println();
}
diff --git a/core/java/com/android/internal/logging/AndroidHandler.java b/core/java/com/android/internal/logging/AndroidHandler.java
index f55a31fcc986..119f3662a94f 100644
--- a/core/java/com/android/internal/logging/AndroidHandler.java
+++ b/core/java/com/android/internal/logging/AndroidHandler.java
@@ -17,9 +17,8 @@
package com.android.internal.logging;
import android.util.Log;
+
import com.android.internal.util.FastPrintWriter;
-import dalvik.system.DalvikLogging;
-import dalvik.system.DalvikLogHandler;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -82,7 +81,7 @@ import java.util.logging.Logger;
* </tr>
* </table>
*/
-public class AndroidHandler extends Handler implements DalvikLogHandler {
+public class AndroidHandler extends Handler {
/**
* Holds the formatter for all Android log handlers.
*/
@@ -121,10 +120,32 @@ public class AndroidHandler extends Handler implements DalvikLogHandler {
// No need to flush, but must implement abstract method.
}
+ /**
+ * Returns the short logger tag (up to 23 chars) for the given logger name.
+ * Traditionally loggers are named by fully-qualified Java classes; this
+ * method attempts to return a concise identifying part of such names.
+ */
+ private static String loggerNameToTag(String loggerName) {
+ // Anonymous logger.
+ if (loggerName == null) {
+ return "null";
+ }
+
+ int length = loggerName.length();
+ if (length <= 23) {
+ return loggerName;
+ }
+
+ int lastPeriod = loggerName.lastIndexOf(".");
+ return length - (lastPeriod + 1) <= 23
+ ? loggerName.substring(lastPeriod + 1)
+ : loggerName.substring(loggerName.length() - 23);
+ }
+
@Override
public void publish(LogRecord record) {
int level = getAndroidLevel(record.getLevel());
- String tag = DalvikLogging.loggerNameToTag(record.getLoggerName());
+ String tag = loggerNameToTag(record.getLoggerName());
if (!Log.isLoggable(tag, level)) {
return;
}
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 8ea5aa815a1c..b4727499d8ef 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -21,6 +21,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.net.Ikev2VpnProfile;
import android.net.PlatformVpnProfile;
import android.net.ProxyInfo;
+import android.net.Uri;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -287,7 +288,7 @@ public final class VpnProfile implements Cloneable, Parcelable {
profile.proxy = new ProxyInfo(host, port.isEmpty() ?
0 : Integer.parseInt(port), exclList);
} else if (!pacFileUrl.isEmpty()) {
- profile.proxy = new ProxyInfo(pacFileUrl);
+ profile.proxy = new ProxyInfo(Uri.parse(pacFileUrl));
}
} // else profile.proxy = null
diff --git a/core/java/com/android/internal/os/BinderCallHeavyHitterWatcher.java b/core/java/com/android/internal/os/BinderCallHeavyHitterWatcher.java
new file mode 100644
index 000000000000..7701761baac8
--- /dev/null
+++ b/core/java/com/android/internal/os/BinderCallHeavyHitterWatcher.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.HeavyHitterSketch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A watcher which makes stats on the incoming binder transaction, if the amount of some type of
+ * transactions exceeds the threshold, the listener will be notified.
+ */
+public final class BinderCallHeavyHitterWatcher {
+ private static final String TAG = "BinderCallHeavyHitterWatcher";
+
+ /**
+ * Whether or not this watcher is enabled.
+ */
+ @GuardedBy("mLock")
+ private boolean mEnabled;
+
+ /**
+ * The listener to be notified in case the amount of some type of transactions exceeds the
+ * threshold.
+ */
+ @GuardedBy("mLock")
+ private BinderCallHeavyHitterListener mListener;
+
+ /**
+ * The heavy hitter stats.
+ */
+ @GuardedBy("mLock")
+ private HeavyHitterSketch<Integer> mHeavyHitterSketch;
+
+ /**
+ * The candidates that could be the heavy hitters, so we track their hashcode and the actual
+ * containers in this map.
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<HeavyHitterContainer> mHeavyHitterCandiates = new SparseArray<>();
+
+ /**
+ * The cache to receive the list of candidates (consists of the hashcode of heavy hitters).
+ */
+ @GuardedBy("mLock")
+ private final ArrayList<Integer> mCachedCandidateList = new ArrayList<>();
+
+ /**
+ * The cache to receive the frequencies of each items in {@link #mCachedCandidateList}.
+ */
+ @GuardedBy("mLock")
+ private final ArrayList<Float> mCachedCandidateFrequencies = new ArrayList<>();
+
+ /**
+ * The cache set to host the candidates.
+ */
+ @GuardedBy("mLock")
+ private ArraySet<Integer> mCachedCandidateSet = new ArraySet<>();
+
+ /**
+ * The cache set to host the containers of candidates.
+ */
+ @GuardedBy("mLock")
+ private HeavyHitterContainer[] mCachedCandidateContainers;
+
+ /**
+ * The index to the {@link #mCachedCandidateContainers}, denote the first available slot
+ */
+ @GuardedBy("mLock")
+ private int mCachedCandidateContainersIndex;
+
+ /**
+ * The input size, should be {@link #mTotalInputSize} - validation size.
+ */
+ @GuardedBy("mLock")
+ private int mInputSize;
+
+ /**
+ * The total input size.
+ */
+ @GuardedBy("mLock")
+ private int mTotalInputSize;
+
+ /**
+ * The number of inputs so far
+ */
+ @GuardedBy("mLock")
+ private int mCurrentInputSize;
+
+ /**
+ * The threshold to be considered as heavy hitters
+ */
+ @GuardedBy("mLock")
+ private float mThreshold;
+
+ /**
+ * The timestamp of the start of current tracing.
+ */
+ @GuardedBy("mLock")
+ private long mBatchStartTimeStamp;
+
+ /**
+ * The lock object
+ */
+ private final Object mLock = new Object();
+
+ /**
+ * The tolerance within which is approximately equal
+ */
+ private static final float EPSILON = 0.00001f;
+
+ /**
+ * Callback interface when the amount of some type of transactions exceeds the threshold.
+ */
+ public interface BinderCallHeavyHitterListener {
+ /**
+ * @param heavyHitters The list of binder call heavy hitters
+ * @param totalBinderCalls The total binder calls
+ * @param threshold The threshold to be considered as heavy hitters
+ * @param timeSpan The toal time span of all these binder calls
+ */
+ void onHeavyHit(List<HeavyHitterContainer> heavyHitters,
+ int totalBinderCalls, float threshold, long timeSpan);
+ }
+
+ /**
+ * Container to hold the potential heavy hitters
+ */
+ public static final class HeavyHitterContainer {
+ /**
+ * The caller UID
+ */
+ public int mUid;
+
+ /**
+ * The class of the Binder object which is being hit heavily
+ */
+ public Class mClass;
+
+ /**
+ * The transaction code within the Binder object which is being hit heavily
+ */
+ public int mCode;
+
+ /**
+ * The frequency of this being hit (a number between 0...1)
+ */
+ public float mFrequency;
+
+ /**
+ * Default constructor
+ */
+ public HeavyHitterContainer() {
+ }
+
+ /**
+ * Copy constructor
+ */
+ public HeavyHitterContainer(@NonNull final HeavyHitterContainer other) {
+ this.mUid = other.mUid;
+ this.mClass = other.mClass;
+ this.mCode = other.mCode;
+ this.mFrequency = other.mFrequency;
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (other == null || !(other instanceof HeavyHitterContainer)) {
+ return false;
+ }
+ HeavyHitterContainer o = (HeavyHitterContainer) other;
+ return this.mUid == o.mUid && this.mClass == o.mClass && this.mCode == o.mCode
+ && Math.abs(this.mFrequency - o.mFrequency) < EPSILON;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode(mUid, mClass, mCode);
+ }
+
+ /**
+ * Compute the hashcode with given parameters.
+ */
+ static int hashCode(int uid, @NonNull Class clazz, int code) {
+ int hash = uid;
+ hash = 31 * hash + clazz.hashCode();
+ hash = 31 * hash + code;
+ return hash;
+ }
+ }
+
+ /**
+ * The static lock object
+ */
+ private static final Object sLock = new Object();
+
+ /**
+ * The default instance
+ */
+ @GuardedBy("sLock")
+ private static BinderCallHeavyHitterWatcher sInstance = null;
+
+ /**
+ * Return the instance of the watcher
+ */
+ public static BinderCallHeavyHitterWatcher getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new BinderCallHeavyHitterWatcher();
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Configure the parameters.
+ *
+ * @param enable Whether or not to enable the watcher
+ * @param batchSize The number of binder transactions it needs to receive before the conclusion
+ * @param threshold The threshold to determine if some type of transactions are too many, it
+ * should be a value between (0.0f, 1.0f]
+ * @param listener The callback interface
+ */
+ public void setConfig(final boolean enable, final int batchSize, final float threshold,
+ @Nullable final BinderCallHeavyHitterListener listener) {
+ synchronized (mLock) {
+ if (!enable) {
+ if (mEnabled) {
+ resetInternalLocked(null, null, 0, 0, 0.0f, 0);
+ mEnabled = false;
+ }
+ return;
+ }
+ mEnabled = true;
+ // Validate the threshold, which is expected to be within (0.0f, 1.0f]
+ if (threshold < EPSILON || threshold > 1.0f) {
+ return;
+ }
+
+ if (batchSize == mTotalInputSize && Math.abs(threshold - mThreshold) < EPSILON) {
+ // Shortcut: just update the listener, no need to reset the watcher itself.
+ mListener = listener;
+ return;
+ }
+
+ final int capacity = (int) (1.0f / threshold);
+ final HeavyHitterSketch<Integer> sketch = HeavyHitterSketch.<Integer>newDefault();
+ final float validationRatio = sketch.getRequiredValidationInputRatio();
+ int inputSize = batchSize;
+ if (!Float.isNaN(validationRatio)) {
+ inputSize = (int) (batchSize * (1 - validationRatio));
+ }
+ try {
+ sketch.setConfig(batchSize, capacity);
+ } catch (IllegalArgumentException e) {
+ // invalid parameter, ignore the config.
+ Log.w(TAG, "Invalid parameter to heavy hitter watcher: "
+ + batchSize + ", " + capacity);
+ return;
+ }
+ // Reset the watcher to start over with the new configuration.
+ resetInternalLocked(listener, sketch, inputSize, batchSize, threshold, capacity);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void resetInternalLocked(@Nullable final BinderCallHeavyHitterListener listener,
+ @Nullable final HeavyHitterSketch<Integer> sketch, final int inputSize,
+ final int batchSize, final float threshold, final int capacity) {
+ mListener = listener;
+ mHeavyHitterSketch = sketch;
+ mHeavyHitterCandiates.clear();
+ mCachedCandidateList.clear();
+ mCachedCandidateFrequencies.clear();
+ mCachedCandidateSet.clear();
+ mInputSize = inputSize;
+ mTotalInputSize = batchSize;
+ mCurrentInputSize = 0;
+ mThreshold = threshold;
+ mBatchStartTimeStamp = SystemClock.elapsedRealtime();
+ initCachedCandidateContainersLocked(capacity);
+ }
+
+ @GuardedBy("mLock")
+ private void initCachedCandidateContainersLocked(final int capacity) {
+ if (capacity > 0) {
+ mCachedCandidateContainers = new HeavyHitterContainer[capacity];
+ for (int i = 0; i < mCachedCandidateContainers.length; i++) {
+ mCachedCandidateContainers[i] = new HeavyHitterContainer();
+ }
+ } else {
+ mCachedCandidateContainers = null;
+ }
+ mCachedCandidateContainersIndex = 0;
+ }
+
+ @GuardedBy("mLock")
+ private @NonNull HeavyHitterContainer acquireHeavyHitterContainerLocked() {
+ return mCachedCandidateContainers[mCachedCandidateContainersIndex++];
+ }
+
+ @GuardedBy("mLock")
+ private void releaseHeavyHitterContainerLocked(@NonNull HeavyHitterContainer container) {
+ mCachedCandidateContainers[--mCachedCandidateContainersIndex] = container;
+ }
+
+ /**
+ * Called on incoming binder transaction
+ *
+ * @param callerUid The UID of the binder transaction's caller
+ * @param clazz The class of the Binder object serving the transaction
+ * @param code The binder transaction code
+ */
+ public void onTransaction(final int callerUid, @NonNull final Class clazz,
+ final int code) {
+ synchronized (mLock) {
+ if (!mEnabled) {
+ return;
+ }
+
+ final HeavyHitterSketch<Integer> sketch = mHeavyHitterSketch;
+ if (sketch == null) {
+ return;
+ }
+
+ // To reduce memory fragmentation, we only feed the hashcode to the sketch,
+ // and keep the mapping from the hashcode to the sketch locally.
+ // However, the mapping will not be built until the validation pass, by then
+ // we will know the potential heavy hitters, so the mapping can focus on
+ // those ones, which will significantly reduce the memory overhead.
+ final int hashCode = HeavyHitterContainer.hashCode(callerUid, clazz, code);
+
+ sketch.add(hashCode);
+ mCurrentInputSize++;
+ if (mCurrentInputSize == mInputSize) {
+ // Retrieve the candidates
+ sketch.getCandidates(mCachedCandidateList);
+ mCachedCandidateSet.addAll(mCachedCandidateList);
+ mCachedCandidateList.clear();
+ } else if (mCurrentInputSize > mInputSize && mCurrentInputSize < mTotalInputSize) {
+ // validation pass
+ if (mCachedCandidateSet.contains(hashCode)) {
+ // It's one of the candidates
+ final int index = mHeavyHitterCandiates.indexOfKey(hashCode);
+ if (index < 0) {
+ // We got another hit, now write down its information
+ final HeavyHitterContainer container =
+ acquireHeavyHitterContainerLocked();
+ container.mUid = callerUid;
+ container.mClass = clazz;
+ container.mCode = code;
+ mHeavyHitterCandiates.put(hashCode, container);
+ }
+ }
+ } else if (mCurrentInputSize == mTotalInputSize) {
+ // Reached the expected number of input, check top ones
+ if (mListener != null) {
+ final List<Integer> result = sketch.getTopHeavyHitters(0,
+ mCachedCandidateList, mCachedCandidateFrequencies);
+ if (result != null) {
+ final int size = result.size();
+ if (size > 0) {
+ final ArrayList<HeavyHitterContainer> hitters = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ final HeavyHitterContainer container = mHeavyHitterCandiates.get(
+ result.get(i));
+ if (container != null) {
+ final HeavyHitterContainer cont =
+ new HeavyHitterContainer(container);
+ cont.mFrequency = mCachedCandidateFrequencies.get(i);
+ hitters.add(cont);
+ }
+ }
+ mListener.onHeavyHit(hitters, mTotalInputSize, mThreshold,
+ SystemClock.elapsedRealtime() - mBatchStartTimeStamp);
+ }
+ }
+ }
+ // reset
+ mHeavyHitterSketch.reset();
+ mHeavyHitterCandiates.clear();
+ mCachedCandidateList.clear();
+ mCachedCandidateFrequencies.clear();
+ mCachedCandidateSet.clear();
+ mCachedCandidateContainersIndex = 0;
+ mCurrentInputSize = 0;
+ mBatchStartTimeStamp = SystemClock.elapsedRealtime();
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index cc55cff262a3..e09ef49acd10 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -27,6 +27,7 @@ import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -126,6 +127,11 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
};
+ private final Object mNativeTidsLock = new Object();
+ // @GuardedBy("mNativeTidsLock") // Cannot mark it as "GuardedBy" because it's read
+ // directly, as a volatile field.
+ private volatile IntArray mNativeTids = new IntArray(0);
+
/** Injector for {@link BinderCallsStats}. */
public static class Injector {
public Random getRandomGenerator() {
@@ -175,6 +181,8 @@ public class BinderCallsStats implements BinderInternal.Observer {
return null;
}
+ noteNativeThreadId();
+
final CallSession s = obtainCallSession();
s.binderClass = binder.getClass();
s.transactionCode = code;
@@ -312,6 +320,27 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
}
+ private void noteNativeThreadId() {
+ final int tid = getNativeTid();
+ int index = mNativeTids.binarySearch(tid);
+ if (index >= 0) {
+ return;
+ }
+
+ // Use the copy-on-write approach. The changes occur exceedingly infrequently, so
+ // this code path is exercised just a few times per boot
+ synchronized (mNativeTidsLock) {
+ IntArray nativeTids = mNativeTids;
+ index = nativeTids.binarySearch(tid);
+ if (index < 0) {
+ IntArray copyOnWriteArray = new IntArray(nativeTids.size() + 1);
+ copyOnWriteArray.addAll(nativeTids);
+ copyOnWriteArray.add(-index - 1, tid);
+ mNativeTids = copyOnWriteArray;
+ }
+ }
+ }
+
/**
* This method is expensive to call.
*/
@@ -505,6 +534,17 @@ public class BinderCallsStats implements BinderInternal.Observer {
return Binder.getCallingUid();
}
+ protected int getNativeTid() {
+ return Binder.getNativeTid();
+ }
+
+ /**
+ * Returns known Linux TIDs for threads taking incoming binder calls.
+ */
+ public int[] getNativeTids() {
+ return mNativeTids.toArray();
+ }
+
protected long getElapsedRealtimeMicro() {
return SystemClock.elapsedRealtimeNanos() / 1000;
}
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index a18943c264f5..f83c5bdc4e28 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -101,7 +101,7 @@ public class ClassLoaderFactory {
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
- parent, targetSdkVersion, isNamespaceShared, classLoaderName, null);
+ parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null);
}
@@ -111,18 +111,24 @@ public class ClassLoaderFactory {
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
classLoaderName, sharedLibraries);
+ String sonameList = "";
+ if (nativeSharedLibraries != null) {
+ sonameList = String.join(":", nativeSharedLibraries);
+ }
+
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
String errorMessage = createClassloaderNamespace(classLoader,
targetSdkVersion,
librarySearchPath,
libraryPermittedPath,
isNamespaceShared,
- dexPath);
+ dexPath,
+ sonameList);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (errorMessage != null) {
@@ -139,5 +145,6 @@ public class ClassLoaderFactory {
String librarySearchPath,
String libraryPermittedPath,
boolean isNamespaceShared,
- String dexPath);
+ String dexPath,
+ String sonameList);
}
diff --git a/core/java/com/android/internal/os/IDropBoxManagerService.aidl b/core/java/com/android/internal/os/IDropBoxManagerService.aidl
index 5e60394e5675..9141719d7a35 100644
--- a/core/java/com/android/internal/os/IDropBoxManagerService.aidl
+++ b/core/java/com/android/internal/os/IDropBoxManagerService.aidl
@@ -37,6 +37,10 @@ interface IDropBoxManagerService {
boolean isTagEnabled(String tag);
/** @see DropBoxManager#getNextEntry */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk=30,
+ publicAlternatives="Use {@link android.os.DropBoxManager#getNextEntry} instead")
DropBoxManager.Entry getNextEntry(String tag, long millis, String packageName);
+
+ DropBoxManager.Entry getNextEntryWithAttribution(String tag, long millis, String packageName,
+ String attributionTag);
}
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index cffb0ad9fdb9..3d35d2fbaa82 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -153,19 +153,32 @@ public class KernelWakelockReader {
}
/**
+ * Attempt to wait for suspend_control service if not immediately available.
+ */
+ private ISuspendControlService waitForSuspendControlService() throws ServiceNotFoundException {
+ final String name = "suspend_control";
+ final int numRetries = 5;
+ for (int i = 0; i < numRetries; i++) {
+ mSuspendControlService = ISuspendControlService.Stub.asInterface(
+ ServiceManager.getService(name));
+ if (mSuspendControlService != null) {
+ return mSuspendControlService;
+ }
+ }
+ throw new ServiceNotFoundException(name);
+ }
+
+ /**
* On success, returns the updated stats from SystemSupend, else returns null.
*/
private KernelWakelockStats getWakelockStatsFromSystemSuspend(
final KernelWakelockStats staleStats) {
WakeLockInfo[] wlStats = null;
- if (mSuspendControlService == null) {
- try {
- mSuspendControlService = ISuspendControlService.Stub.asInterface(
- ServiceManager.getServiceOrThrow("suspend_control"));
- } catch (ServiceNotFoundException e) {
- Slog.wtf(TAG, "Required service suspend_control not available", e);
- return null;
- }
+ try {
+ mSuspendControlService = waitForSuspendControlService();
+ } catch (ServiceNotFoundException e) {
+ Slog.wtf(TAG, "Required service suspend_control not available", e);
+ return null;
}
try {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 0b46658d5a06..0a3fe096279d 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -21,8 +21,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COM
import static android.view.View.SYSTEM_UI_LAYOUT_FLAGS;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowInsets.Type.ime;
-import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
@@ -34,8 +32,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -146,17 +142,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if ((view.getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) {
return new Pair<>(Insets.NONE, insets);
}
-
- boolean includeIme = (view.getViewRootImpl().mWindowAttributes.softInputMode
- & SOFT_INPUT_MASK_ADJUST)
- == SOFT_INPUT_ADJUST_RESIZE;
- Insets insetsToApply;
- if (ViewRootImpl.sNewInsetsMode == 0) {
- insetsToApply = insets.getSystemWindowInsets();
- } else {
- insetsToApply = insets.getInsets(systemBars() | (includeIme ? ime() : 0));
- }
- insets = insets.inset(insetsToApply);
+ Insets insetsToApply = insets.getSystemWindowInsets();
return new Pair<>(insetsToApply,
insets.inset(insetsToApply).consumeSystemWindowInsets());
};
diff --git a/core/java/com/android/internal/util/HeavyHitterSketch.java b/core/java/com/android/internal/util/HeavyHitterSketch.java
new file mode 100644
index 000000000000..e18acaf36797
--- /dev/null
+++ b/core/java/com/android/internal/util/HeavyHitterSketch.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>A utility which processes a sequence of input (stream) and output the heavy hitters
+ * (the frequent ones).</p>
+ *
+ * @param <T> The type of the input.
+ * @see <a href="https://en.wikipedia.org/wiki/Streaming_algorithm">Stream Algorithm</a> for
+ * the definion of heavy hitters and the list of algorithms for detecting it.
+ * <p>
+ * {@hide}
+ */
+public interface HeavyHitterSketch<T> {
+ /**
+ * Return the default implementation.
+ *
+ * @return The default implementation.
+ */
+ static <V> @NonNull HeavyHitterSketch<V> newDefault() {
+ return new HeavyHitterSketchImpl<V>();
+ }
+
+ /**
+ * Set the configuration with given parameters
+ *
+ * @param inputSize The amount of the input.
+ * @param capacity The maximum number of distinct input it should track; it defines the lower
+ * bound of the output.
+ */
+ void setConfig(int inputSize, int capacity);
+
+ /**
+ * Add a new input to the current sketch.
+ *
+ * @param newInstance The new input
+ */
+ void add(@Nullable T newInstance);
+
+ /**
+ * @param k The number of heavy hitters it should return, k &lt; capacity, a value of 0
+ * will be equivalent to capacity - 1
+ * @param holder The list array into which the elements of the tops are to be stored; a new list
+ * would be created and returned if this parameter is null.
+ * @param freqs Optional, the frequencies of each items in the returned result
+ * @return The top K heavy hitters(frequent input)
+ */
+ @Nullable
+ List<T> getTopHeavyHitters(int k, @Nullable List<T> holder, @Nullable List<Float> freqs);
+
+ /**
+ * @param holder The list array into which the elements of the candidates are to be stored; a
+ * new list would be created and returned if this parameter is null.
+ * @return The candidate heavy hitters so far, it could include false postives.
+ */
+ @Nullable
+ List<T> getCandidates(@Nullable List<T> holder);
+
+ /**
+ * Reset this heavy hitter counter
+ */
+ void reset();
+
+ /**
+ * @return The ratio of the input to be used as the validation data, Float.NaN means no
+ * validation is needed.
+ */
+ float getRequiredValidationInputRatio();
+
+ /**
+ * The default implementation of the {@link HeavyHitterSketch}.
+ *
+ * <p>Currently it consists of two passes: the first pass will take the input into
+ * the MG(Misra–Gries) summary; while the secondary pass will validate the output against the
+ * input in order to eliminate false postivies.</p>
+ *
+ * <p>For sure there are better approaches which takes only one pass, but also comes along with
+ * overheads in terms of cpu/memory cost; the MG summary would be a trivial and good enough
+ * pick.</p>
+ *
+ * @param <T> The type of the input.
+ * @see <a href="https://en.wikipedia.org/wiki/Misra%E2%80%93Gries_summary">Misra–Gries
+ * summary</a> for the detailed explanation of the algorithm.
+ */
+ final class HeavyHitterSketchImpl<T> implements HeavyHitterSketch<T> {
+ /**
+ * The array to track the current heavy hitters, its size &lt; {@link #mCapacity}.
+ */
+ private final SparseArray<T> mObjects = new SparseArray<>();
+
+ /**
+ * The frequencies of the current heavy hitters, its size &lt; {@link #mCapacity}.
+ */
+ private final SparseIntArray mFrequencies = new SparseIntArray();
+
+ /**
+ * The amount of the input of each pass
+ */
+ private int mPassSize;
+
+ /**
+ * The amount of the total input it expects
+ */
+ private int mTotalSize;
+
+ /**
+ * The maximum number of distinct input it should track
+ */
+ private int mCapacity;
+
+ /**
+ * The amount of inputs it already received.
+ */
+ private int mNumInputs;
+
+ /**
+ * Whether or not it's configured properly.
+ */
+ private boolean mConfigured;
+
+ /**
+ * Set the configuration with given parameters
+ *
+ * @param inputSize The amount of the input.
+ * @param capacity The maximum number of distinct input it should track; it defines the
+ * lower bound of the output.
+ */
+ public void setConfig(final int inputSize, final int capacity) {
+ if (inputSize < capacity || inputSize <= 1) {
+ mConfigured = false;
+ throw new IllegalArgumentException();
+ }
+ reset();
+ mTotalSize = inputSize;
+ mPassSize = inputSize >> 1;
+ mCapacity = capacity;
+ mConfigured = true;
+ }
+
+ /**
+ * Add a new input to the current sketch.
+ *
+ * @param newInstance The new input
+ */
+ @Override
+ public void add(@Nullable final T newInstance) {
+ if (!mConfigured) {
+ throw new IllegalStateException();
+ }
+ if (mNumInputs < mPassSize) {
+ addToMGSummary(newInstance);
+ } else if (mNumInputs < mTotalSize) {
+ // Validation pass
+ validate(newInstance);
+ }
+ }
+
+ /**
+ * Add an input to the MG summary.
+ *
+ * <p>Note the frequency in the result set is an estimation. Every (obj, freq') pair
+ * in the result set, will have the following property:
+ * <code>(freq - inputSize / capacity) &le; freq' &le; freq</code>
+ * The above freq' is the estimated frequency, while the freq is the actual frequency.
+ * </p>
+ */
+ private void addToMGSummary(@Nullable final T newInstance) {
+ final int hashCode = newInstance != null ? newInstance.hashCode() : 0;
+ final int index = mObjects.indexOfKey(hashCode);
+ // MG summary
+ if (index >= 0) {
+ mFrequencies.setValueAt(index, mFrequencies.valueAt(index) + 1);
+ } else if (mObjects.size() < mCapacity - 1) {
+ mObjects.put(hashCode, newInstance);
+ mFrequencies.put(hashCode, 1);
+ } else {
+ for (int i = mFrequencies.size() - 1; i >= 0; i--) {
+ final int val = mFrequencies.valueAt(i) - 1;
+ if (val == 0) {
+ mObjects.removeAt(i);
+ mFrequencies.removeAt(i);
+ } else {
+ mFrequencies.setValueAt(i, val);
+ }
+ }
+ }
+ if (++mNumInputs == mPassSize) {
+ // Clear all the frequencies as we are going to validate them next
+ for (int i = mFrequencies.size() - 1; i >= 0; i--) {
+ mFrequencies.setValueAt(i, 0);
+ }
+ }
+ }
+
+ /**
+ * Validate the results from MG summary; the ones with frequencies less than lower boundary
+ * will be removed from the set.
+ */
+ private void validate(@Nullable final T newInstance) {
+ final int hashCode = newInstance != null ? newInstance.hashCode() : 0;
+ final int index = mObjects.indexOfKey(hashCode);
+ if (index >= 0) {
+ mFrequencies.setValueAt(index, mFrequencies.valueAt(index) + 1);
+ }
+ if (++mNumInputs == mTotalSize) {
+ final int lower = mPassSize / mCapacity;
+ // Remove any occurrences with frequencies less than lower boundary
+ for (int i = mFrequencies.size() - 1; i >= 0; i--) {
+ final int val = mFrequencies.valueAt(i);
+ if (val < lower) {
+ mFrequencies.removeAt(i);
+ mObjects.removeAt(i);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param k The number of heavy hitters it should return, k &lt; capacity, a value of 0
+ * will be equivalent to capacity - 1
+ * @param holder The list array into which the elements of the tops are to be stored; a new
+ * list would be created and returned if this parameter is null.
+ * @param freqs Optional, the frequencies of each items in the returned result
+ * @return The top K heavy hitters(frequent input)
+ */
+ @Override
+ @Nullable
+ public List<T> getTopHeavyHitters(final int k, final @Nullable List<T> holder,
+ final @Nullable List<Float> freqs) {
+ if (!mConfigured) {
+ throw new IllegalStateException();
+ }
+
+ if (k >= mCapacity) {
+ throw new IllegalArgumentException();
+ }
+
+ if (mNumInputs < mTotalSize) {
+ // It hasn't had all the inputs yet.
+ throw new IllegalStateException();
+ }
+
+ ArrayList<Integer> indexes = null;
+ for (int i = mFrequencies.size() - 1; i >= 0; i--) {
+ final int val = mFrequencies.valueAt(i);
+ if (val > 0) {
+ if (indexes == null) {
+ indexes = new ArrayList<>();
+ }
+ indexes.add(i);
+ }
+ }
+ if (indexes == null) {
+ return null;
+ }
+
+ Collections.sort(indexes, (a, b) -> mFrequencies.valueAt(b) - mFrequencies.valueAt(a));
+
+ final List<T> result = holder != null ? holder : new ArrayList<T>();
+ final int max = Math.min(k == 0 ? (mCapacity - 1) : k, indexes.size());
+ for (int i = 0; i < max; i++) {
+ final int index = indexes.get(i);
+ final T obj = mObjects.valueAt(index);
+ if (obj != null) {
+ result.add(obj);
+ if (freqs != null) {
+ freqs.add((float) mFrequencies.valueAt(index) / mPassSize);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @param holder The list array into which the elements of the candidates are to be stored;
+ * a new list would be created and returned if this parameter is null.
+ * @return The candidate heavy hitters so far, it could include false postives.
+ */
+ @Nullable
+ public List<T> getCandidates(final @Nullable List<T> holder) {
+ if (!mConfigured) {
+ throw new IllegalStateException();
+ }
+ if (mNumInputs < mPassSize) {
+ // It hasn't done with the first pass yet, return nothing
+ return null;
+ }
+
+ List<T> result = holder != null ? holder : new ArrayList<T>();
+ for (int i = mObjects.size() - 1; i >= 0; i--) {
+ final T obj = mObjects.valueAt(i);
+ if (obj != null) {
+ result.add(obj);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Reset this heavy hitter counter
+ */
+ @Override
+ public void reset() {
+ mNumInputs = 0;
+ mObjects.clear();
+ mFrequencies.clear();
+ }
+
+ /**
+ * @return The ratio of the input to be used as the validation data, Float.NaN means no
+ * validation is needed.
+ */
+ public float getRequiredValidationInputRatio() {
+ return 0.5f;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 0c2406559dcc..7a79cc9ef868 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -2088,10 +2088,11 @@ public class StateMachine {
pw.println(getName() + ":");
pw.println(" total records=" + getLogRecCount());
for (int i = 0; i < getLogRecSize(); i++) {
- pw.println(" rec[" + i + "]: " + getLogRec(i).toString());
+ pw.println(" rec[" + i + "]: " + getLogRec(i));
pw.flush();
}
- pw.println("curState=" + getCurrentState().getName());
+ final IState curState = getCurrentState();
+ pw.println("curState=" + (curState == null ? "<QUIT>" : curState.getName()));
}
@Override
diff --git a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
index cf1220c08467..6c97962ac057 100644
--- a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
+++ b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
@@ -22,40 +22,56 @@ import android.view.inputmethod.InlineSuggestionsRequest;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
/**
- * Binder interface for the IME service to send an inline suggestion request to the system.
+ * Binder interface for the IME service to send {@link InlineSuggestionsRequest} or notify other IME
+ * service events to the system.
* {@hide}
*/
oneway interface IInlineSuggestionsRequestCallback {
- // Indicates that the current IME does not support inline suggestion.
+ /** Indicates that the current IME does not support inline suggestion. */
void onInlineSuggestionsUnsupported();
- // Sends the inline suggestions request from IME to Autofill. Calling this method indicates
- // that the IME input is started on the view corresponding to the request.
+ /**
+ * Sends the inline suggestions request from IME to Autofill. Calling this method indicates
+ * that the IME input is started on the view corresponding to the request.
+ */
void onInlineSuggestionsRequest(in InlineSuggestionsRequest request,
in IInlineSuggestionsResponseCallback callback);
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onStartInput(EditorInfo, boolean)} is called on the given focused field.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onStartInput(EditorInfo, boolean)} is called on the given focused field.
+ */
void onInputMethodStartInput(in AutofillId imeFieldId);
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #dispatchOnShowInputRequested(int, boolean)} is called and shares the call result.
- // The true value of {@code requestResult} means the IME is about to be shown, while
- // false value means the IME will not be shown.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #dispatchOnShowInputRequested(int, boolean)} is called and shares the call result.
+ * The true value of {@code requestResult} means the IME is about to be shown, while
+ * false value means the IME will not be shown.
+ */
void onInputMethodShowInputRequested(boolean requestResult);
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onStartInputView(EditorInfo, boolean)} is called on the field specified by the earlier
- // {@link #onInputMethodStartInput(AutofillId)}.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onStartInputView(EditorInfo, boolean)} is called on the field specified by the earlier
+ * {@link #onInputMethodStartInput(AutofillId)}.
+ */
void onInputMethodStartInputView();
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onFinishInputView(boolean)} is called on the field specified by the earlier
- // {@link #onInputMethodStartInput(AutofillId)}.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onFinishInputView(boolean)} is called on the field specified by the earlier
+ * {@link #onInputMethodStartInput(AutofillId)}.
+ */
void onInputMethodFinishInputView();
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onFinishInput()} is called on the field specified by the earlier
- // {@link #onInputMethodStartInput(AutofillId)}.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onFinishInput()} is called on the field specified by the earlier
+ * {@link #onInputMethodStartInput(AutofillId)}.
+ */
void onInputMethodFinishInput();
+
+ // Indicates that the current IME changes inline suggestion session.
+ void onInlineSuggestionsSessionInvalidated();
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 8ec51b89d240..a1cbd3fcae79 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -73,5 +73,8 @@ interface IInputMethodManager {
in float[] matrixValues);
oneway void reportPerceptible(in IBinder windowToken, boolean perceptible);
+ /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */
void removeImeSurface();
+ /** Remove the IME surface. Requires passing the currently focused window. */
+ void removeImeSurfaceFromWindow(in IBinder windowToken);
}
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index dc8d57ab709e..a2de0aff5dfa 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -371,7 +371,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
}
if (haveLast) {
canvas.drawLine(lastX, lastY, x, y, mPathPaint);
- final Paint paint = ps.mTraceCurrent[i] ? mCurrentPointPaint : mPaint;
+ final Paint paint = ps.mTraceCurrent[i - 1] ? mCurrentPointPaint : mPaint;
canvas.drawPoint(lastX, lastY, paint);
drawn = true;
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 3f708f84750c..90eeabb47e9a 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -464,11 +464,7 @@ public class ResolverDrawerLayout extends ViewGroup {
smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel);
mDismissOnScrollerFinished = true;
} else {
- if (isNestedListChildScrolled()) {
- mNestedListChild.smoothScrollToPosition(0);
- } else if (isNestedRecyclerChildScrolled()) {
- mNestedRecyclerChild.smoothScrollToPosition(0);
- }
+ scrollNestedScrollableChildBackToTop();
smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel);
}
}
@@ -493,6 +489,17 @@ public class ResolverDrawerLayout extends ViewGroup {
return handled;
}
+ /**
+ * Scroll nested scrollable child back to top if it has been scrolled.
+ */
+ public void scrollNestedScrollableChildBackToTop() {
+ if (isNestedListChildScrolled()) {
+ mNestedListChild.smoothScrollToPosition(0);
+ } else if (isNestedRecyclerChildScrolled()) {
+ mNestedRecyclerChild.smoothScrollToPosition(0);
+ }
+ }
+
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = ev.getActionIndex();
final int pointerId = ev.getPointerId(pointerIndex);
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 0eb3981ed598..c6a1153c747f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -49,10 +49,12 @@ import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -104,11 +106,17 @@ public class SystemConfig {
public final String name;
public final String filename;
public final String[] dependencies;
+ public final boolean isNative;
SharedLibraryEntry(String name, String filename, String[] dependencies) {
+ this(name, filename, dependencies, false /* isNative */);
+ }
+
+ SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) {
this.name = name;
this.filename = filename;
this.dependencies = dependencies;
+ this.isNative = isNative;
}
}
@@ -170,12 +178,6 @@ public class SystemConfig {
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
- // These are the packages that are whitelisted to be able to run as system user
- final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>();
-
- // These are the packages that should not run under system user
- final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
-
// These are the components that are enabled by default as VR mode listener services.
final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
@@ -309,14 +311,6 @@ public class SystemConfig {
return mLinkedApps;
}
- public ArraySet<String> getSystemUserWhitelistedApps() {
- return mSystemUserWhitelistedApps;
- }
-
- public ArraySet<String> getSystemUserBlacklistedApps() {
- return mSystemUserBlacklistedApps;
- }
-
public ArraySet<String> getHiddenApiWhitelistedApps() {
return mHiddenApiPackageWhitelist;
}
@@ -457,6 +451,7 @@ public class SystemConfig {
log.traceBegin("readAllPermissions");
try {
readAllPermissions();
+ readPublicNativeLibrariesList();
} finally {
log.traceEnd();
}
@@ -895,34 +890,6 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
- case "system-user-whitelisted-app": {
- if (allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<" + name + "> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mSystemUserWhitelistedApps.add(pkgname);
- }
- } else {
- logNotAllowedInPartition(name, permFile, parser);
- }
- XmlUtils.skipCurrentTag(parser);
- } break;
- case "system-user-blacklisted-app": {
- if (allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<" + name + "> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mSystemUserBlacklistedApps.add(pkgname);
- }
- } else {
- logNotAllowedInPartition(name, permFile, parser);
- }
- XmlUtils.skipCurrentTag(parser);
- } break;
case "default-enabled-vr-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
@@ -1513,6 +1480,37 @@ public class SystemConfig {
}
}
+ private void readPublicNativeLibrariesList() {
+ readPublicLibrariesListFile(new File("/vendor/etc/public.libraries.txt"));
+ String[] dirs = {"/system/etc", "/system_ext/etc", "/product/etc"};
+ for (String dir : dirs) {
+ for (File f : (new File(dir)).listFiles()) {
+ String name = f.getName();
+ if (name.startsWith("public.libraries-") && name.endsWith(".txt")) {
+ readPublicLibrariesListFile(f);
+ }
+ }
+ }
+ }
+
+ private void readPublicLibrariesListFile(File listFile) {
+ try (BufferedReader br = new BufferedReader(new FileReader(listFile))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (line.isEmpty() || line.startsWith("#")) {
+ continue;
+ }
+ // Line format is <soname> [abi]. We take the soname part.
+ String soname = line.trim().split(" ")[0];
+ SharedLibraryEntry entry = new SharedLibraryEntry(
+ soname, soname, new String[0], true);
+ mSharedLibraries.put(entry.name, entry);
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to read public libraries file " + listFile, e);
+ }
+ }
+
private static boolean isSystemProcess() {
return Process.myUid() == Process.SYSTEM_UID;
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 9e2e85a7d041..b4b58ff903db 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -155,9 +155,8 @@ cc_library_shared {
"android_media_ToneGenerator.cpp",
"android_hardware_Camera.cpp",
"android_hardware_camera2_CameraMetadata.cpp",
- "android_hardware_camera2_legacy_LegacyCameraDevice.cpp",
- "android_hardware_camera2_legacy_PerfMeasurement.cpp",
"android_hardware_camera2_DngCreator.cpp",
+ "android_hardware_camera2_utils_SurfaceUtils.cpp",
"android_hardware_display_DisplayManagerGlobal.cpp",
"android_hardware_display_DisplayViewport.cpp",
"android_hardware_HardwareBuffer.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7b708efdb278..5b1196dd0f62 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -73,9 +73,8 @@ extern int register_android_opengl_jni_GLES32(JNIEnv* env);
extern int register_android_hardware_Camera(JNIEnv *env);
extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env);
-extern int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv *env);
-extern int register_android_hardware_camera2_legacy_PerfMeasurement(JNIEnv *env);
extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
+extern int register_android_hardware_camera2_utils_SurfaceUtils(JNIEnv* env);
extern int register_android_hardware_display_DisplayManagerGlobal(JNIEnv* env);
extern int register_android_hardware_HardwareBuffer(JNIEnv *env);
extern int register_android_hardware_SensorManager(JNIEnv *env);
@@ -1526,9 +1525,8 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
- REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
- REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
REG_JNI(register_android_hardware_camera2_DngCreator),
+ REG_JNI(register_android_hardware_camera2_utils_SurfaceUtils),
REG_JNI(register_android_hardware_display_DisplayManagerGlobal),
REG_JNI(register_android_hardware_HardwareBuffer),
REG_JNI(register_android_hardware_SensorManager),
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index bc69735d7453..e47f18a943d9 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -556,7 +556,7 @@ static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz,
// connect to camera service
static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
- jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
+ jobject weak_this, jint cameraId, jstring clientPackageName)
{
// Convert jstring to String16
const char16_t *rawClientName = reinterpret_cast<const char16_t*>(
@@ -566,19 +566,8 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
env->ReleaseStringChars(clientPackageName,
reinterpret_cast<const jchar*>(rawClientName));
- sp<Camera> camera;
- if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
- // Default path: hal version is don't care, do normal camera connect.
- camera = Camera::connect(cameraId, clientName,
- Camera::USE_CALLING_UID, Camera::USE_CALLING_PID);
- } else {
- jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
- Camera::USE_CALLING_UID, camera);
- if (status != NO_ERROR) {
- return status;
- }
- }
-
+ sp<Camera> camera =
+ Camera::connect(cameraId, clientName, Camera::USE_CALLING_UID, Camera::USE_CALLING_PID);
if (camera == NULL) {
return -EACCES;
}
@@ -1068,7 +1057,7 @@ static const JNINativeMethod camMethods[] = {
"(ILandroid/hardware/Camera$CameraInfo;)V",
(void*)android_hardware_Camera_getCameraInfo },
{ "native_setup",
- "(Ljava/lang/Object;IILjava/lang/String;)I",
+ "(Ljava/lang/Object;ILjava/lang/String;)I",
(void*)android_hardware_Camera_native_setup },
{ "native_release",
"()V",
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
deleted file mode 100644
index 8cf1d2cbfaae..000000000000
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ /dev/null
@@ -1,841 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Legacy-CameraDevice-JNI"
-// #define LOG_NDEBUG 0
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/Trace.h>
-#include <camera/CameraUtils.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-#include "android_runtime/android_view_Surface.h"
-#include "android_runtime/android_graphics_SurfaceTexture.h"
-
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/IProducerListener.h>
-#include <gui/Surface.h>
-#include <hardware/camera3.h>
-#include <surfacetexture/SurfaceTexture.h>
-#include <system/camera_metadata.h>
-#include <system/window.h>
-#include <ui/GraphicBuffer.h>
-
-#include <stdint.h>
-#include <inttypes.h>
-
-using namespace android;
-
-// fully-qualified class name
-#define CAMERA_DEVICE_CLASS_NAME "android/hardware/camera2/legacy/LegacyCameraDevice"
-#define CAMERA_DEVICE_BUFFER_SLACK 3
-#define DONT_CARE 0
-
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
-
-#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
-
-// Use BAD_VALUE for surface abandoned error
-#define OVERRIDE_SURFACE_ERROR(err) \
-do { \
- if (err == -ENODEV) { \
- err = BAD_VALUE; \
- } \
-} while (0)
-
-#define UPDATE(md, tag, data, size) \
-do { \
- if ((md).update((tag), (data), (size))) { \
- ALOGE("Update " #tag " failed!"); \
- return BAD_VALUE; \
- } \
-} while (0)
-
-/**
- * Convert from RGB 888 to Y'CbCr using the conversion specified in JFIF v1.02
- */
-static void rgbToYuv420(uint8_t* rgbBuf, size_t width, size_t height, uint8_t* yPlane,
- uint8_t* crPlane, uint8_t* cbPlane, size_t chromaStep, size_t yStride, size_t chromaStride) {
- uint8_t R, G, B;
- size_t index = 0;
- for (size_t j = 0; j < height; j++) {
- uint8_t* cr = crPlane;
- uint8_t* cb = cbPlane;
- uint8_t* y = yPlane;
- bool jEven = (j & 1) == 0;
- for (size_t i = 0; i < width; i++) {
- R = rgbBuf[index++];
- G = rgbBuf[index++];
- B = rgbBuf[index++];
- *y++ = (77 * R + 150 * G + 29 * B) >> 8;
- if (jEven && (i & 1) == 0) {
- *cb = (( -43 * R - 85 * G + 128 * B) >> 8) + 128;
- *cr = (( 128 * R - 107 * G - 21 * B) >> 8) + 128;
- cr += chromaStep;
- cb += chromaStep;
- }
- // Skip alpha
- index++;
- }
- yPlane += yStride;
- if (jEven) {
- crPlane += chromaStride;
- cbPlane += chromaStride;
- }
- }
-}
-
-static void rgbToYuv420(uint8_t* rgbBuf, size_t width, size_t height, android_ycbcr* ycbcr) {
- size_t cStep = ycbcr->chroma_step;
- size_t cStride = ycbcr->cstride;
- size_t yStride = ycbcr->ystride;
- ALOGV("%s: yStride is: %zu, cStride is: %zu, cStep is: %zu", __FUNCTION__, yStride, cStride,
- cStep);
- rgbToYuv420(rgbBuf, width, height, reinterpret_cast<uint8_t*>(ycbcr->y),
- reinterpret_cast<uint8_t*>(ycbcr->cr), reinterpret_cast<uint8_t*>(ycbcr->cb),
- cStep, yStride, cStride);
-}
-
-static status_t connectSurface(const sp<Surface>& surface, int32_t maxBufferSlack) {
- status_t err = NO_ERROR;
-
- err = surface->connect(NATIVE_WINDOW_API_CAMERA, /*listener*/NULL);
- if (err != OK) {
- ALOGE("%s: Unable to connect to surface, error %s (%d).", __FUNCTION__,
- strerror(-err), err);
- return err;
- }
-
- err = native_window_set_usage(surface.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to set native window usage flag, error %s (%d).", __FUNCTION__,
- strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
-
- int minUndequeuedBuffers;
- err = static_cast<ANativeWindow*>(surface.get())->query(surface.get(),
- NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to get native window min undequeued buffers, error %s (%d).",
- __FUNCTION__, strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
-
- ALOGV("%s: Setting buffer count to %d", __FUNCTION__,
- maxBufferSlack + 1 + minUndequeuedBuffers);
- err = native_window_set_buffer_count(surface.get(), maxBufferSlack + 1 + minUndequeuedBuffers);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to set native window buffer count, error %s (%d).", __FUNCTION__,
- strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- return NO_ERROR;
-}
-
-/**
- * Produce a frame in the given surface.
- *
- * Args:
- * anw - a surface to produce a frame in.
- * pixelBuffer - image buffer to generate a frame from.
- * width - width of the pixelBuffer in pixels.
- * height - height of the pixelBuffer in pixels.
- * pixelFmt - format of the pixelBuffer, one of:
- * HAL_PIXEL_FORMAT_YCrCb_420_SP,
- * HAL_PIXEL_FORMAT_YCbCr_420_888,
- * HAL_PIXEL_FORMAT_BLOB
- * bufSize - the size of the pixelBuffer in bytes.
- */
-static status_t produceFrame(const sp<ANativeWindow>& anw,
- uint8_t* pixelBuffer,
- int32_t bufWidth, // Width of the pixelBuffer
- int32_t bufHeight, // Height of the pixelBuffer
- int32_t pixelFmt, // Format of the pixelBuffer
- int32_t bufSize) {
- ATRACE_CALL();
- status_t err = NO_ERROR;
- ANativeWindowBuffer* anb;
- ALOGV("%s: Dequeue buffer from %p %dx%d (fmt=%x, size=%x)",
- __FUNCTION__, anw.get(), bufWidth, bufHeight, pixelFmt, bufSize);
-
- if (anw == 0) {
- ALOGE("%s: anw must not be NULL", __FUNCTION__);
- return BAD_VALUE;
- } else if (pixelBuffer == NULL) {
- ALOGE("%s: pixelBuffer must not be NULL", __FUNCTION__);
- return BAD_VALUE;
- } else if (bufWidth < 0) {
- ALOGE("%s: width must be non-negative", __FUNCTION__);
- return BAD_VALUE;
- } else if (bufHeight < 0) {
- ALOGE("%s: height must be non-negative", __FUNCTION__);
- return BAD_VALUE;
- } else if (bufSize < 0) {
- ALOGE("%s: bufSize must be non-negative", __FUNCTION__);
- return BAD_VALUE;
- }
-
- size_t width = static_cast<size_t>(bufWidth);
- size_t height = static_cast<size_t>(bufHeight);
- size_t bufferLength = static_cast<size_t>(bufSize);
-
- // TODO: Switch to using Surface::lock and Surface::unlockAndPost
- err = native_window_dequeue_buffer_and_wait(anw.get(), &anb);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to dequeue buffer, error %s (%d).", __FUNCTION__,
- strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
-
- sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
- uint32_t grallocBufWidth = buf->getWidth();
- uint32_t grallocBufHeight = buf->getHeight();
- uint32_t grallocBufStride = buf->getStride();
- if (grallocBufWidth != width || grallocBufHeight != height) {
- ALOGE("%s: Received gralloc buffer with bad dimensions %" PRIu32 "x%" PRIu32
- ", expecting dimensions %zu x %zu", __FUNCTION__, grallocBufWidth,
- grallocBufHeight, width, height);
- return BAD_VALUE;
- }
-
- int32_t bufFmt = 0;
- err = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &bufFmt);
- if (err != NO_ERROR) {
- ALOGE("%s: Error while querying surface pixel format %s (%d).", __FUNCTION__,
- strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
-
- uint64_t tmpSize = (pixelFmt == HAL_PIXEL_FORMAT_BLOB) ? grallocBufWidth :
- 4 * grallocBufHeight * grallocBufWidth;
- if (bufFmt != pixelFmt) {
- if (bufFmt == HAL_PIXEL_FORMAT_RGBA_8888 && pixelFmt == HAL_PIXEL_FORMAT_BLOB) {
- ALOGV("%s: Using BLOB to RGBA format override.", __FUNCTION__);
- tmpSize = 4 * (grallocBufWidth + grallocBufStride * (grallocBufHeight - 1));
- } else {
- ALOGW("%s: Format mismatch in produceFrame: expecting format %#" PRIx32
- ", but received buffer with format %#" PRIx32, __FUNCTION__, pixelFmt, bufFmt);
- }
- }
-
- if (tmpSize > SIZE_MAX) {
- ALOGE("%s: Overflow calculating size, buffer with dimens %zu x %zu is absurdly large...",
- __FUNCTION__, width, height);
- return BAD_VALUE;
- }
-
- size_t totalSizeBytes = tmpSize;
-
- ALOGV("%s: Pixel format chosen: %x", __FUNCTION__, pixelFmt);
- switch(pixelFmt) {
- case HAL_PIXEL_FORMAT_YCrCb_420_SP: {
- if (bufferLength < totalSizeBytes) {
- ALOGE("%s: PixelBuffer size %zu too small for given dimensions",
- __FUNCTION__, bufferLength);
- return BAD_VALUE;
- }
- uint8_t* img = NULL;
- ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
- err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
- if (err != NO_ERROR) return err;
-
- uint8_t* yPlane = img;
- uint8_t* uPlane = img + height * width;
- uint8_t* vPlane = uPlane + 1;
- size_t chromaStep = 2;
- size_t yStride = width;
- size_t chromaStride = width;
-
- rgbToYuv420(pixelBuffer, width, height, yPlane,
- uPlane, vPlane, chromaStep, yStride, chromaStride);
- break;
- }
- case HAL_PIXEL_FORMAT_YV12: {
- if (bufferLength < totalSizeBytes) {
- ALOGE("%s: PixelBuffer size %zu too small for given dimensions",
- __FUNCTION__, bufferLength);
- return BAD_VALUE;
- }
-
- if ((width & 1) || (height & 1)) {
- ALOGE("%s: Dimens %zu x %zu are not divisible by 2.", __FUNCTION__, width, height);
- return BAD_VALUE;
- }
-
- uint8_t* img = NULL;
- ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
- err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
- if (err != NO_ERROR) {
- ALOGE("%s: Error %s (%d) while locking gralloc buffer for write.", __FUNCTION__,
- strerror(-err), err);
- return err;
- }
-
- uint32_t stride = buf->getStride();
- ALOGV("%s: stride is: %" PRIu32, __FUNCTION__, stride);
- LOG_ALWAYS_FATAL_IF(stride % 16, "Stride is not 16 pixel aligned %d", stride);
-
- uint32_t cStride = ALIGN(stride / 2, 16);
- size_t chromaStep = 1;
-
- uint8_t* yPlane = img;
- uint8_t* crPlane = img + static_cast<uint32_t>(height) * stride;
- uint8_t* cbPlane = crPlane + cStride * static_cast<uint32_t>(height) / 2;
-
- rgbToYuv420(pixelBuffer, width, height, yPlane,
- crPlane, cbPlane, chromaStep, stride, cStride);
- break;
- }
- case HAL_PIXEL_FORMAT_YCbCr_420_888: {
- // Software writes with YCbCr_420_888 format are unsupported
- // by the gralloc module for now
- if (bufferLength < totalSizeBytes) {
- ALOGE("%s: PixelBuffer size %zu too small for given dimensions",
- __FUNCTION__, bufferLength);
- return BAD_VALUE;
- }
- android_ycbcr ycbcr = android_ycbcr();
- ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
-
- err = buf->lockYCbCr(GRALLOC_USAGE_SW_WRITE_OFTEN, &ycbcr);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to lock ycbcr buffer, error %s (%d).", __FUNCTION__,
- strerror(-err), err);
- return err;
- }
- rgbToYuv420(pixelBuffer, width, height, &ycbcr);
- break;
- }
- case HAL_PIXEL_FORMAT_BLOB: {
- int8_t* img = NULL;
- struct camera3_jpeg_blob footer = {
- .jpeg_blob_id = CAMERA3_JPEG_BLOB_ID,
- .jpeg_size = (uint32_t)bufferLength
- };
-
- size_t totalJpegSize = bufferLength + sizeof(footer);
- totalJpegSize = (totalJpegSize + 3) & ~0x3; // round up to nearest octonibble
-
- if (totalJpegSize > totalSizeBytes) {
- ALOGE("%s: Pixel buffer needs size %zu, cannot fit in gralloc buffer of size %zu",
- __FUNCTION__, totalJpegSize, totalSizeBytes);
- return BAD_VALUE;
- }
-
- err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to lock buffer, error %s (%d).", __FUNCTION__, strerror(-err),
- err);
- return err;
- }
-
- memcpy(img, pixelBuffer, bufferLength);
- memcpy(img + totalSizeBytes - sizeof(footer), &footer, sizeof(footer));
- break;
- }
- default: {
- ALOGE("%s: Invalid pixel format in produceFrame: %x", __FUNCTION__, pixelFmt);
- return BAD_VALUE;
- }
- }
-
- ALOGV("%s: Unlock buffer from %p", __FUNCTION__, anw.get());
- err = buf->unlock();
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to unlock buffer, error %s (%d).", __FUNCTION__, strerror(-err), err);
- return err;
- }
-
- ALOGV("%s: Queue buffer to %p", __FUNCTION__, anw.get());
- err = anw->queueBuffer(anw.get(), buf->getNativeBuffer(), /*fenceFd*/-1);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to queue buffer, error %s (%d).", __FUNCTION__, strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- return NO_ERROR;
-}
-
-static sp<ANativeWindow> getNativeWindow(JNIEnv* env, jobject surface) {
- sp<ANativeWindow> anw;
- if (surface) {
- anw = android_view_Surface_getNativeWindow(env, surface);
- if (env->ExceptionCheck()) {
- return NULL;
- }
- } else {
- jniThrowNullPointerException(env, "surface");
- return NULL;
- }
- if (anw == NULL) {
- ALOGE("%s: Surface had no valid native window.", __FUNCTION__);
- return NULL;
- }
- return anw;
-}
-
-static sp<ANativeWindow> getSurfaceTextureNativeWindow(JNIEnv* env, jobject thiz) {
- sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz));
- sp<Surface> surfaceTextureClient(producer != NULL ? new Surface(producer) : NULL);
- return surfaceTextureClient;
-}
-
-static sp<ANativeWindow> getNativeWindowFromTexture(JNIEnv* env, jobject surfaceTexture) {
- sp<ANativeWindow> anw;
- if (surfaceTexture) {
- anw = getSurfaceTextureNativeWindow(env, surfaceTexture);
- if (env->ExceptionCheck()) {
- return NULL;
- }
- } else {
- jniThrowNullPointerException(env, "surfaceTexture");
- return NULL;
- }
- if (anw == NULL) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "SurfaceTexture had no valid native window.");
- return NULL;
- }
- return anw;
-}
-
-static sp<Surface> getSurface(JNIEnv* env, jobject surface) {
- sp<Surface> s;
- if (surface) {
- s = android_view_Surface_getSurface(env, surface);
- if (env->ExceptionCheck()) {
- return NULL;
- }
- } else {
- jniThrowNullPointerException(env, "surface");
- return NULL;
- }
- if (s == NULL) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "Surface had no valid native Surface.");
- return NULL;
- }
- return s;
-}
-
-extern "C" {
-
-static jint LegacyCameraDevice_nativeDetectSurfaceType(JNIEnv* env, jobject thiz, jobject surface) {
- ALOGV("nativeDetectSurfaceType");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
- return BAD_VALUE;
- }
- int32_t fmt = 0;
- status_t err = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &fmt);
- if(err != NO_ERROR) {
- ALOGE("%s: Error while querying surface pixel format %s (%d).", __FUNCTION__, strerror(-err),
- err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- return fmt;
-}
-
-static jint LegacyCameraDevice_nativeDetectSurfaceDataspace(JNIEnv* env, jobject thiz, jobject surface) {
- ALOGV("nativeDetectSurfaceDataspace");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
- return BAD_VALUE;
- }
- int32_t fmt = 0;
- status_t err = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, &fmt);
- if(err != NO_ERROR) {
- ALOGE("%s: Error while querying surface dataspace %s (%d).", __FUNCTION__, strerror(-err),
- err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- return fmt;
-}
-
-static jint LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz,
- jobject surface, jintArray dimens) {
- ALOGV("nativeGetSurfaceDimens");
-
- if (dimens == NULL) {
- ALOGE("%s: Null dimens argument passed to nativeDetectSurfaceDimens", __FUNCTION__);
- return BAD_VALUE;
- }
-
- if (env->GetArrayLength(dimens) < 2) {
- ALOGE("%s: Invalid length of dimens argument in nativeDetectSurfaceDimens", __FUNCTION__);
- return BAD_VALUE;
- }
-
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
- return BAD_VALUE;
- }
- int32_t dimenBuf[2];
- status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf);
- if(err != NO_ERROR) {
- ALOGE("%s: Error while querying surface width %s (%d).", __FUNCTION__, strerror(-err),
- err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1);
- if(err != NO_ERROR) {
- ALOGE("%s: Error while querying surface height %s (%d).", __FUNCTION__, strerror(-err),
- err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- env->SetIntArrayRegion(dimens, /*start*/0, /*length*/ARRAY_SIZE(dimenBuf), dimenBuf);
- return NO_ERROR;
-}
-
-static jint LegacyCameraDevice_nativeDetectSurfaceUsageFlags(JNIEnv* env, jobject thiz,
- jobject surface) {
- ALOGV("nativeDetectSurfaceUsageFlags");
-
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- jniThrowException(env, "java/lang/UnsupportedOperationException",
- "Could not retrieve native window from surface.");
- return BAD_VALUE;
- }
- int32_t usage = 0;
- status_t err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage);
- if(err != NO_ERROR) {
- jniThrowException(env, "java/lang/UnsupportedOperationException",
- "Error while querying surface usage bits");
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- return usage;
-}
-
-static jint LegacyCameraDevice_nativeDisconnectSurface(JNIEnv* env, jobject thiz,
- jobject surface) {
- ALOGV("nativeDisconnectSurface");
- if (surface == nullptr) return NO_ERROR;
-
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGV("Buffer queue has already been abandoned.");
- return NO_ERROR;
- }
-
- status_t err = native_window_api_disconnect(anw.get(), NATIVE_WINDOW_API_CAMERA);
- if(err != NO_ERROR) {
- jniThrowException(env, "java/lang/UnsupportedOperationException",
- "Error while disconnecting surface");
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- return NO_ERROR;
-}
-
-static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz,
- jobject surfaceTexture, jintArray dimens) {
- ALOGV("nativeDetectTextureDimens");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindowFromTexture(env, surfaceTexture)) == NULL) {
- ALOGE("%s: Could not retrieve native window from SurfaceTexture.", __FUNCTION__);
- return BAD_VALUE;
- }
-
- int32_t dimenBuf[2];
- status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf);
- if(err != NO_ERROR) {
- ALOGE("%s: Error while querying SurfaceTexture width %s (%d)", __FUNCTION__,
- strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
-
- err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1);
- if(err != NO_ERROR) {
- ALOGE("%s: Error while querying SurfaceTexture height %s (%d)", __FUNCTION__,
- strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
-
- env->SetIntArrayRegion(dimens, /*start*/0, /*length*/ARRAY_SIZE(dimenBuf), dimenBuf);
- if (env->ExceptionCheck()) {
- return BAD_VALUE;
- }
- return NO_ERROR;
-}
-
-static jint LegacyCameraDevice_nativeConnectSurface(JNIEnv* env, jobject thiz, jobject surface) {
- ALOGV("nativeConnectSurface");
- sp<Surface> s;
- if ((s = getSurface(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve surface.", __FUNCTION__);
- return BAD_VALUE;
- }
- status_t err = connectSurface(s, CAMERA_DEVICE_BUFFER_SLACK);
- if (err != NO_ERROR) {
- ALOGE("%s: Error while configuring surface %s (%d).", __FUNCTION__, strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- return NO_ERROR;
-}
-
-static jint LegacyCameraDevice_nativeProduceFrame(JNIEnv* env, jobject thiz, jobject surface,
- jbyteArray pixelBuffer, jint width, jint height, jint pixelFormat) {
- ALOGV("nativeProduceFrame");
- sp<ANativeWindow> anw;
-
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
- return BAD_VALUE;
- }
-
- if (pixelBuffer == NULL) {
- jniThrowNullPointerException(env, "pixelBuffer");
- return DONT_CARE;
- }
-
- int32_t bufSize = static_cast<int32_t>(env->GetArrayLength(pixelBuffer));
- jbyte* pixels = env->GetByteArrayElements(pixelBuffer, /*is_copy*/NULL);
-
- if (pixels == NULL) {
- jniThrowNullPointerException(env, "pixels");
- return DONT_CARE;
- }
-
- status_t err = produceFrame(anw, reinterpret_cast<uint8_t*>(pixels), width, height,
- pixelFormat, bufSize);
- env->ReleaseByteArrayElements(pixelBuffer, pixels, JNI_ABORT);
-
- if (err != NO_ERROR) {
- ALOGE("%s: Error while producing frame %s (%d).", __FUNCTION__, strerror(-err), err);
- return err;
- }
- return NO_ERROR;
-}
-
-static jint LegacyCameraDevice_nativeSetSurfaceFormat(JNIEnv* env, jobject thiz, jobject surface,
- jint pixelFormat) {
- ALOGV("nativeSetSurfaceType");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
- return BAD_VALUE;
- }
- status_t err = native_window_set_buffers_format(anw.get(), pixelFormat);
- if (err != NO_ERROR) {
- ALOGE("%s: Error while setting surface format %s (%d).", __FUNCTION__, strerror(-err), err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- return NO_ERROR;
-}
-
-static jint LegacyCameraDevice_nativeSetSurfaceDimens(JNIEnv* env, jobject thiz, jobject surface,
- jint width, jint height) {
- ALOGV("nativeSetSurfaceDimens");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
- return BAD_VALUE;
- }
-
- // Set user dimensions only
- // The producer dimensions are owned by GL
- status_t err = native_window_set_buffers_user_dimensions(anw.get(), width, height);
- if (err != NO_ERROR) {
- ALOGE("%s: Error while setting surface user dimens %s (%d).", __FUNCTION__, strerror(-err),
- err);
- OVERRIDE_SURFACE_ERROR(err);
- return err;
- }
- return NO_ERROR;
-}
-
-static jlong LegacyCameraDevice_nativeGetSurfaceId(JNIEnv* env, jobject thiz, jobject surface) {
- ALOGV("nativeGetSurfaceId");
- sp<Surface> s;
- if ((s = getSurface(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native Surface from surface.", __FUNCTION__);
- return 0;
- }
- sp<IGraphicBufferProducer> gbp = s->getIGraphicBufferProducer();
- if (gbp == NULL) {
- ALOGE("%s: Could not retrieve IGraphicBufferProducer from surface.", __FUNCTION__);
- return 0;
- }
- sp<IBinder> b = IInterface::asBinder(gbp);
- if (b == NULL) {
- ALOGE("%s: Could not retrieve IBinder from surface.", __FUNCTION__);
- return 0;
- }
- /*
- * FIXME: Use better unique ID for surfaces than native IBinder pointer. Fix also in the camera
- * service (CameraDeviceClient.h).
- */
- return reinterpret_cast<jlong>(b.get());
-}
-
-static jint LegacyCameraDevice_nativeSetSurfaceOrientation(JNIEnv* env, jobject thiz,
- jobject surface, jint facing, jint orientation) {
- ALOGV("nativeSetSurfaceOrientation");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
- return BAD_VALUE;
- }
-
- status_t err = NO_ERROR;
- CameraMetadata staticMetadata;
-
- int32_t orientVal = static_cast<int32_t>(orientation);
- uint8_t facingVal = static_cast<uint8_t>(facing);
- staticMetadata.update(ANDROID_SENSOR_ORIENTATION, &orientVal, 1);
- staticMetadata.update(ANDROID_LENS_FACING, &facingVal, 1);
-
- int32_t transform = 0;
-
- if ((err = CameraUtils::getRotationTransform(staticMetadata, /*out*/&transform)) != NO_ERROR) {
- ALOGE("%s: Invalid rotation transform %s (%d)", __FUNCTION__, strerror(-err),
- err);
- return err;
- }
-
- ALOGV("%s: Setting buffer sticky transform to %d", __FUNCTION__, transform);
-
- if ((err = native_window_set_buffers_sticky_transform(anw.get(), transform)) != NO_ERROR) {
- ALOGE("%s: Unable to configure surface transform, error %s (%d)", __FUNCTION__,
- strerror(-err), err);
- return err;
- }
-
- return NO_ERROR;
-}
-
-static jint LegacyCameraDevice_nativeSetNextTimestamp(JNIEnv* env, jobject thiz, jobject surface,
- jlong timestamp) {
- ALOGV("nativeSetNextTimestamp");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
- return BAD_VALUE;
- }
-
- status_t err = NO_ERROR;
-
- if ((err = native_window_set_buffers_timestamp(anw.get(), static_cast<int64_t>(timestamp))) !=
- NO_ERROR) {
- ALOGE("%s: Unable to set surface timestamp, error %s (%d)", __FUNCTION__, strerror(-err),
- err);
- return err;
- }
- return NO_ERROR;
-}
-
-static jint LegacyCameraDevice_nativeSetScalingMode(JNIEnv* env, jobject thiz, jobject surface,
- jint mode) {
- ALOGV("nativeSetScalingMode");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
- return BAD_VALUE;
- }
- status_t err = NO_ERROR;
- if ((err = native_window_set_scaling_mode(anw.get(), static_cast<int>(mode))) != NO_ERROR) {
- ALOGE("%s: Unable to set surface scaling mode, error %s (%d)", __FUNCTION__,
- strerror(-err), err);
- return err;
- }
- return NO_ERROR;
-}
-
-static jint LegacyCameraDevice_nativeGetJpegFooterSize(JNIEnv* env, jobject thiz) {
- ALOGV("nativeGetJpegFooterSize");
- return static_cast<jint>(sizeof(struct camera3_jpeg_blob));
-}
-
-} // extern "C"
-
-static const JNINativeMethod gCameraDeviceMethods[] = {
- { "nativeDetectSurfaceType",
- "(Landroid/view/Surface;)I",
- (void *)LegacyCameraDevice_nativeDetectSurfaceType },
- { "nativeDetectSurfaceDataspace",
- "(Landroid/view/Surface;)I",
- (void *)LegacyCameraDevice_nativeDetectSurfaceDataspace },
- { "nativeDetectSurfaceDimens",
- "(Landroid/view/Surface;[I)I",
- (void *)LegacyCameraDevice_nativeDetectSurfaceDimens },
- { "nativeConnectSurface",
- "(Landroid/view/Surface;)I",
- (void *)LegacyCameraDevice_nativeConnectSurface },
- { "nativeProduceFrame",
- "(Landroid/view/Surface;[BIII)I",
- (void *)LegacyCameraDevice_nativeProduceFrame },
- { "nativeSetSurfaceFormat",
- "(Landroid/view/Surface;I)I",
- (void *)LegacyCameraDevice_nativeSetSurfaceFormat },
- { "nativeSetSurfaceDimens",
- "(Landroid/view/Surface;II)I",
- (void *)LegacyCameraDevice_nativeSetSurfaceDimens },
- { "nativeGetSurfaceId",
- "(Landroid/view/Surface;)J",
- (void *)LegacyCameraDevice_nativeGetSurfaceId },
- { "nativeDetectTextureDimens",
- "(Landroid/graphics/SurfaceTexture;[I)I",
- (void *)LegacyCameraDevice_nativeDetectTextureDimens },
- { "nativeSetSurfaceOrientation",
- "(Landroid/view/Surface;II)I",
- (void *)LegacyCameraDevice_nativeSetSurfaceOrientation },
- { "nativeSetNextTimestamp",
- "(Landroid/view/Surface;J)I",
- (void *)LegacyCameraDevice_nativeSetNextTimestamp },
- { "nativeGetJpegFooterSize",
- "()I",
- (void *)LegacyCameraDevice_nativeGetJpegFooterSize },
- { "nativeDetectSurfaceUsageFlags",
- "(Landroid/view/Surface;)I",
- (void *)LegacyCameraDevice_nativeDetectSurfaceUsageFlags },
- { "nativeSetScalingMode",
- "(Landroid/view/Surface;I)I",
- (void *)LegacyCameraDevice_nativeSetScalingMode },
- { "nativeDisconnectSurface",
- "(Landroid/view/Surface;)I",
- (void *)LegacyCameraDevice_nativeDisconnectSurface },
-};
-
-// Get all the required offsets in java class and register native functions
-int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv* env)
-{
- // Register native functions
- return RegisterMethodsOrDie(env,
- CAMERA_DEVICE_CLASS_NAME,
- gCameraDeviceMethods,
- NELEM(gCameraDeviceMethods));
-}
diff --git a/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp b/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
deleted file mode 100644
index fac243a70415..000000000000
--- a/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Camera2-Legacy-PerfMeasurement-JNI"
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/Trace.h>
-#include <utils/Vector.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-
-#include <ui/GraphicBuffer.h>
-#include <system/window.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-using namespace android;
-
-// fully-qualified class name
-#define PERF_MEASUREMENT_CLASS_NAME "android/hardware/camera2/legacy/PerfMeasurement"
-
-/** GL utility methods copied from com_google_android_gles_jni_GLImpl.cpp */
-
-// Check if the extension at the head of pExtensions is pExtension. Note that pExtensions is
-// terminated by either 0 or space, while pExtension is terminated by 0.
-
-static bool
-extensionEqual(const GLubyte* pExtensions, const GLubyte* pExtension) {
- while (true) {
- char a = *pExtensions++;
- char b = *pExtension++;
- bool aEnd = a == '\0' || a == ' ';
- bool bEnd = b == '\0';
- if (aEnd || bEnd) {
- return aEnd == bEnd;
- }
- if (a != b) {
- return false;
- }
- }
-}
-
-static const GLubyte*
-nextExtension(const GLubyte* pExtensions) {
- while (true) {
- char a = *pExtensions++;
- if (a == '\0') {
- return pExtensions-1;
- } else if ( a == ' ') {
- return pExtensions;
- }
- }
-}
-
-static bool
-checkForExtension(const GLubyte* pExtensions, const GLubyte* pExtension) {
- for (; *pExtensions != '\0'; pExtensions = nextExtension(pExtensions)) {
- if (extensionEqual(pExtensions, pExtension)) {
- return true;
- }
- }
- return false;
-}
-
-/** End copied GL utility methods */
-
-bool checkGlError(JNIEnv* env) {
- int error;
- if ((error = glGetError()) != GL_NO_ERROR) {
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "GLES20 error: 0x%d", error);
- return true;
- }
- return false;
-}
-
-/**
- * Asynchronous low-overhead GL performance measurement using
- * http://www.khronos.org/registry/gles/extensions/EXT/EXT_disjoint_timer_query.txt
- *
- * Measures the duration of GPU processing for a set of GL commands, delivering
- * the measurement asynchronously once processing completes.
- *
- * All calls must come from a single thread with a valid GL context active.
- **/
-class PerfMeasurementContext {
- private:
- Vector<GLuint> mTimingQueries;
- size_t mTimingStartIndex;
- size_t mTimingEndIndex;
- size_t mTimingQueryIndex;
- size_t mFreeQueries;
-
- bool mInitDone;
- public:
-
- /**
- * maxQueryCount should be a conservative estimate of how many query objects
- * will be active at once, which is a function of the GPU's level of
- * pipelining and the frequency of queries.
- */
- explicit PerfMeasurementContext(size_t maxQueryCount):
- mTimingStartIndex(0),
- mTimingEndIndex(0),
- mTimingQueryIndex(0) {
- mTimingQueries.resize(maxQueryCount);
- mFreeQueries = maxQueryCount;
- mInitDone = false;
- }
-
- int getMaxQueryCount() {
- return mTimingQueries.size();
- }
-
- /**
- * Start a measurement period using the next available query object.
- * Returns INVALID_OPERATION if called multiple times in a row,
- * and BAD_VALUE if no more query objects are available.
- */
- int startGlTimer() {
- // Lazy init of queries to avoid needing GL context during construction
- if (!mInitDone) {
- glGenQueriesEXT(mTimingQueries.size(), mTimingQueries.editArray());
- mInitDone = true;
- }
-
- if (mTimingEndIndex != mTimingStartIndex) {
- return INVALID_OPERATION;
- }
-
- if (mFreeQueries == 0) {
- return BAD_VALUE;
- }
-
- glBeginQueryEXT(GL_TIME_ELAPSED_EXT, mTimingQueries[mTimingStartIndex]);
-
- mTimingStartIndex = (mTimingStartIndex + 1) % mTimingQueries.size();
- mFreeQueries--;
-
- return OK;
- }
-
- /**
- * Finish the current measurement period
- * Returns INVALID_OPERATION if called before any startGLTimer calls
- * or if called multiple times in a row.
- */
- int stopGlTimer() {
- size_t nextEndIndex = (mTimingEndIndex + 1) % mTimingQueries.size();
- if (nextEndIndex != mTimingStartIndex) {
- return INVALID_OPERATION;
- }
- glEndQueryEXT(GL_TIME_ELAPSED_EXT);
-
- mTimingEndIndex = nextEndIndex;
-
- return OK;
- }
-
- static const nsecs_t NO_DURATION_YET = -1L;
- static const nsecs_t FAILED_MEASUREMENT = -2L;
-
- /**
- * Get the next available duration measurement.
- *
- * Returns NO_DURATION_YET if no new measurement is available,
- * and FAILED_MEASUREMENT if an error occurred during the next
- * measurement period.
- *
- * Otherwise returns a positive number of nanoseconds measuring the
- * duration of the oldest completed query.
- */
- nsecs_t getNextGlDuration() {
- if (!mInitDone) {
- // No start/stop called yet
- return NO_DURATION_YET;
- }
-
- GLint available;
- glGetQueryObjectivEXT(mTimingQueries[mTimingQueryIndex],
- GL_QUERY_RESULT_AVAILABLE_EXT, &available);
- if (!available) {
- return NO_DURATION_YET;
- }
-
- GLint64 duration = FAILED_MEASUREMENT;
- GLint disjointOccurred;
- glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjointOccurred);
-
- if (!disjointOccurred) {
- glGetQueryObjecti64vEXT(mTimingQueries[mTimingQueryIndex],
- GL_QUERY_RESULT_EXT,
- &duration);
- }
-
- mTimingQueryIndex = (mTimingQueryIndex + 1) % mTimingQueries.size();
- mFreeQueries++;
-
- return static_cast<nsecs_t>(duration);
- }
-
- static bool isMeasurementSupported() {
- const GLubyte* extensions = glGetString(GL_EXTENSIONS);
- return checkForExtension(extensions,
- reinterpret_cast<const GLubyte*>("GL_EXT_disjoint_timer_query"));
- }
-
-};
-
-PerfMeasurementContext* getContext(jlong context) {
- return reinterpret_cast<PerfMeasurementContext*>(context);
-}
-
-extern "C" {
-
-static jlong PerfMeasurement_nativeCreateContext(JNIEnv* env, jobject thiz,
- jint maxQueryCount) {
- PerfMeasurementContext *context = new PerfMeasurementContext(maxQueryCount);
- return reinterpret_cast<jlong>(context);
-}
-
-static void PerfMeasurement_nativeDeleteContext(JNIEnv* env, jobject thiz,
- jlong contextHandle) {
- PerfMeasurementContext *context = getContext(contextHandle);
- delete(context);
-}
-
-static jboolean PerfMeasurement_nativeQuerySupport(JNIEnv* env, jobject thiz) {
- bool supported = PerfMeasurementContext::isMeasurementSupported();
- checkGlError(env);
- return static_cast<jboolean>(supported);
-}
-
-static void PerfMeasurement_nativeStartGlTimer(JNIEnv* env, jobject thiz,
- jlong contextHandle) {
-
- PerfMeasurementContext *context = getContext(contextHandle);
- status_t err = context->startGlTimer();
- if (err != OK) {
- switch (err) {
- case INVALID_OPERATION:
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "Mismatched start/end GL timing calls");
- return;
- case BAD_VALUE:
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "Too many timing queries in progress, max %d",
- context->getMaxQueryCount());
- return;
- default:
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "Unknown error starting GL timing");
- return;
- }
- }
- checkGlError(env);
-}
-
-static void PerfMeasurement_nativeStopGlTimer(JNIEnv* env, jobject thiz,
- jlong contextHandle) {
-
- PerfMeasurementContext *context = getContext(contextHandle);
- status_t err = context->stopGlTimer();
- if (err != OK) {
- switch (err) {
- case INVALID_OPERATION:
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "Mismatched start/end GL timing calls");
- return;
- default:
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "Unknown error ending GL timing");
- return;
- }
- }
- checkGlError(env);
-}
-
-static jlong PerfMeasurement_nativeGetNextGlDuration(JNIEnv* env,
- jobject thiz, jlong contextHandle) {
- PerfMeasurementContext *context = getContext(contextHandle);
- nsecs_t duration = context->getNextGlDuration();
-
- checkGlError(env);
- return static_cast<jlong>(duration);
-}
-
-} // extern "C"
-
-static const JNINativeMethod gPerfMeasurementMethods[] = {
- { "nativeCreateContext",
- "(I)J",
- (jlong *)PerfMeasurement_nativeCreateContext },
- { "nativeDeleteContext",
- "(J)V",
- (void *)PerfMeasurement_nativeDeleteContext },
- { "nativeQuerySupport",
- "()Z",
- (jboolean *)PerfMeasurement_nativeQuerySupport },
- { "nativeStartGlTimer",
- "(J)V",
- (void *)PerfMeasurement_nativeStartGlTimer },
- { "nativeStopGlTimer",
- "(J)V",
- (void *)PerfMeasurement_nativeStopGlTimer },
- { "nativeGetNextGlDuration",
- "(J)J",
- (jlong *)PerfMeasurement_nativeGetNextGlDuration }
-};
-
-
-// Get all the required offsets in java class and register native functions
-int register_android_hardware_camera2_legacy_PerfMeasurement(JNIEnv* env)
-{
- // Register native functions
- return RegisterMethodsOrDie(env,
- PERF_MEASUREMENT_CLASS_NAME,
- gPerfMeasurementMethods,
- NELEM(gPerfMeasurementMethods));
-}
diff --git a/core/jni/android_hardware_camera2_utils_SurfaceUtils.cpp b/core/jni/android_hardware_camera2_utils_SurfaceUtils.cpp
new file mode 100644
index 000000000000..2437a511238c
--- /dev/null
+++ b/core/jni/android_hardware_camera2_utils_SurfaceUtils.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera-SurfaceUtils-JNI"
+// #define LOG_NDEBUG 0
+#include <camera/CameraUtils.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include <nativehelper/JNIHelp.h>
+#include "android_runtime/android_graphics_SurfaceTexture.h"
+#include "android_runtime/android_view_Surface.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+#include <system/window.h>
+#include <ui/GraphicBuffer.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+
+using namespace android;
+
+// fully-qualified class name
+#define CAMERA_UTILS_CLASS_NAME "android/hardware/camera2/utils/SurfaceUtils"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+#define OVERRIDE_SURFACE_ERROR(err) \
+ do { \
+ if (err == -ENODEV) { \
+ err = BAD_VALUE; \
+ } \
+ } while (0)
+
+static sp<ANativeWindow> getNativeWindow(JNIEnv* env, jobject surface) {
+ sp<ANativeWindow> anw;
+ if (surface) {
+ anw = android_view_Surface_getNativeWindow(env, surface);
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ } else {
+ jniThrowNullPointerException(env, "surface");
+ return NULL;
+ }
+ if (anw == NULL) {
+ ALOGE("%s: Surface had no valid native window.", __FUNCTION__);
+ return NULL;
+ }
+ return anw;
+}
+
+static sp<Surface> getSurface(JNIEnv* env, jobject surface) {
+ sp<Surface> s;
+ if (surface) {
+ s = android_view_Surface_getSurface(env, surface);
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ } else {
+ jniThrowNullPointerException(env, "surface");
+ return NULL;
+ }
+ if (s == NULL) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Surface had no valid native Surface.");
+ return NULL;
+ }
+ return s;
+}
+
+extern "C" {
+
+static jint SurfaceUtils_nativeDetectSurfaceType(JNIEnv* env, jobject thiz, jobject surface) {
+ ALOGV("nativeDetectSurfaceType");
+ sp<ANativeWindow> anw;
+ if ((anw = getNativeWindow(env, surface)) == NULL) {
+ ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ int32_t fmt = 0;
+ status_t err = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &fmt);
+ if (err != NO_ERROR) {
+ ALOGE("%s: Error while querying surface pixel format %s (%d).", __FUNCTION__,
+ strerror(-err), err);
+ OVERRIDE_SURFACE_ERROR(err);
+ return err;
+ }
+ return fmt;
+}
+
+static jint SurfaceUtils_nativeDetectSurfaceDataspace(JNIEnv* env, jobject thiz, jobject surface) {
+ ALOGV("nativeDetectSurfaceDataspace");
+ sp<ANativeWindow> anw;
+ if ((anw = getNativeWindow(env, surface)) == NULL) {
+ ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ int32_t fmt = 0;
+ status_t err = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, &fmt);
+ if (err != NO_ERROR) {
+ ALOGE("%s: Error while querying surface dataspace %s (%d).", __FUNCTION__, strerror(-err),
+ err);
+ OVERRIDE_SURFACE_ERROR(err);
+ return err;
+ }
+ return fmt;
+}
+
+static jint SurfaceUtils_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz, jobject surface,
+ jintArray dimens) {
+ ALOGV("nativeGetSurfaceDimens");
+
+ if (dimens == NULL) {
+ ALOGE("%s: Null dimens argument passed to nativeDetectSurfaceDimens", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (env->GetArrayLength(dimens) < 2) {
+ ALOGE("%s: Invalid length of dimens argument in nativeDetectSurfaceDimens", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ sp<ANativeWindow> anw;
+ if ((anw = getNativeWindow(env, surface)) == NULL) {
+ ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ int32_t dimenBuf[2];
+ status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf);
+ if (err != NO_ERROR) {
+ ALOGE("%s: Error while querying surface width %s (%d).", __FUNCTION__, strerror(-err), err);
+ OVERRIDE_SURFACE_ERROR(err);
+ return err;
+ }
+ err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1);
+ if (err != NO_ERROR) {
+ ALOGE("%s: Error while querying surface height %s (%d).", __FUNCTION__, strerror(-err),
+ err);
+ OVERRIDE_SURFACE_ERROR(err);
+ return err;
+ }
+ env->SetIntArrayRegion(dimens, /*start*/ 0, /*length*/ ARRAY_SIZE(dimenBuf), dimenBuf);
+ return NO_ERROR;
+}
+
+static jlong SurfaceUtils_nativeDetectSurfaceUsageFlags(JNIEnv* env, jobject thiz,
+ jobject surface) {
+ ALOGV("nativeDetectSurfaceUsageFlags");
+
+ sp<ANativeWindow> anw;
+ if ((anw = getNativeWindow(env, surface)) == NULL) {
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
+ "Could not retrieve native window from surface.");
+ return BAD_VALUE;
+ }
+ uint64_t usage = 0;
+ status_t err = native_window_get_consumer_usage(anw.get(), &usage);
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
+ "Error while querying surface usage bits");
+ OVERRIDE_SURFACE_ERROR(err);
+ return err;
+ }
+ return usage;
+}
+
+static jlong SurfaceUtils_nativeGetSurfaceId(JNIEnv* env, jobject thiz, jobject surface) {
+ ALOGV("nativeGetSurfaceId");
+ sp<Surface> s;
+ if ((s = getSurface(env, surface)) == NULL) {
+ ALOGE("%s: Could not retrieve native Surface from surface.", __FUNCTION__);
+ return 0;
+ }
+ sp<IGraphicBufferProducer> gbp = s->getIGraphicBufferProducer();
+ if (gbp == NULL) {
+ ALOGE("%s: Could not retrieve IGraphicBufferProducer from surface.", __FUNCTION__);
+ return 0;
+ }
+ sp<IBinder> b = IInterface::asBinder(gbp);
+ if (b == NULL) {
+ ALOGE("%s: Could not retrieve IBinder from surface.", __FUNCTION__);
+ return 0;
+ }
+ /*
+ * FIXME: Use better unique ID for surfaces than native IBinder pointer. Fix also in the camera
+ * service (CameraDeviceClient.h).
+ */
+ return reinterpret_cast<jlong>(b.get());
+}
+
+} // extern "C"
+
+static const JNINativeMethod gCameraSurfaceUtilsMethods[] = {
+ {"nativeDetectSurfaceType", "(Landroid/view/Surface;)I",
+ (void*)SurfaceUtils_nativeDetectSurfaceType},
+ {"nativeDetectSurfaceDataspace", "(Landroid/view/Surface;)I",
+ (void*)SurfaceUtils_nativeDetectSurfaceDataspace},
+ {"nativeDetectSurfaceDimens", "(Landroid/view/Surface;[I)I",
+ (void*)SurfaceUtils_nativeDetectSurfaceDimens},
+ {"nativeDetectSurfaceUsageFlags", "(Landroid/view/Surface;)J",
+ (void*)SurfaceUtils_nativeDetectSurfaceUsageFlags},
+ {"nativeGetSurfaceId", "(Landroid/view/Surface;)J", (void*)SurfaceUtils_nativeGetSurfaceId},
+};
+
+// Get all the required offsets in java class and register native functions
+int register_android_hardware_camera2_utils_SurfaceUtils(JNIEnv* env) {
+ // Register native functions
+ return RegisterMethodsOrDie(env, CAMERA_UTILS_CLASS_NAME, gCameraSurfaceUtilsMethods,
+ NELEM(gCameraSurfaceUtilsMethods));
+}
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 71edfd553e7e..350f35828418 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -61,8 +61,8 @@ bool NativeInputApplicationHandle::updateInfo() {
mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>");
- mInfo.dispatchingTimeout = env->GetLongField(obj,
- gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
+ mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)(
+ env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutNanos));
jobject tokenObj = env->GetObjectField(obj,
gInputApplicationHandleClassInfo.token);
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 81569e0f7b7a..bdb4544f4aae 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -16,20 +16,21 @@
#define LOG_TAG "InputWindowHandle"
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-#include "jni.h"
-#include <android_runtime/AndroidRuntime.h>
-#include <utils/threads.h>
+#include "android_hardware_input_InputWindowHandle.h"
#include <android/graphics/region.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <binder/IPCThreadState.h>
#include <gui/SurfaceControl.h>
+#include <nativehelper/JNIHelp.h>
#include <ui/Region.h>
+#include <utils/threads.h>
-#include "android_hardware_input_InputWindowHandle.h"
#include "android_hardware_input_InputApplicationHandle.h"
#include "android_util_Binder.h"
-#include <binder/IPCThreadState.h>
+#include "core_jni_helpers.h"
+#include "input/InputWindow.h"
+#include "jni.h"
namespace android {
@@ -113,12 +114,12 @@ bool NativeInputWindowHandle::updateInfo() {
mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
- mInfo.layoutParamsFlags = env->GetIntField(obj,
- gInputWindowHandleClassInfo.layoutParamsFlags);
- mInfo.layoutParamsType = env->GetIntField(obj,
- gInputWindowHandleClassInfo.layoutParamsType);
- mInfo.dispatchingTimeout = env->GetLongField(obj,
- gInputWindowHandleClassInfo.dispatchingTimeoutNanos);
+ mInfo.flags = Flags<InputWindowInfo::Flag>(
+ env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags));
+ mInfo.type = static_cast<InputWindowInfo::Type>(
+ env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType));
+ mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)(
+ env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutNanos));
mInfo.frameLeft = env->GetIntField(obj,
gInputWindowHandleClassInfo.frameLeft);
mInfo.frameTop = env->GetIntField(obj,
@@ -157,8 +158,8 @@ bool NativeInputWindowHandle::updateInfo() {
gInputWindowHandleClassInfo.ownerPid);
mInfo.ownerUid = env->GetIntField(obj,
gInputWindowHandleClassInfo.ownerUid);
- mInfo.inputFeatures = env->GetIntField(obj,
- gInputWindowHandleClassInfo.inputFeatures);
+ mInfo.inputFeatures = static_cast<InputWindowInfo::Feature>(
+ env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures));
mInfo.displayId = env->GetIntField(obj,
gInputWindowHandleClassInfo.displayId);
mInfo.portalToDisplayId = env->GetIntField(obj,
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 3da2fa26ffe6..d6e8531fa6ca 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -853,9 +853,8 @@ static jboolean android_os_Debug_isVmapStack(JNIEnv *env, jobject clazz)
} cfg_state = CONFIG_UNKNOWN;
if (cfg_state == CONFIG_UNKNOWN) {
- auto runtime_info = vintf::VintfObject::GetInstance()
- ->getRuntimeInfo(false /* skip cache */,
- vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
+ auto runtime_info = vintf::VintfObject::GetInstance()->getRuntimeInfo(
+ vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
CHECK(runtime_info != nullptr) << "Kernel configs cannot be fetched. b/151092221";
const std::map<std::string, std::string>& configs = runtime_info->kernelConfigs();
std::map<std::string, std::string>::const_iterator it = configs.find("CONFIG_VMAP_STACK");
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index b6427c9aa01c..48f33a6a3d77 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -339,6 +339,10 @@ static jobject JHwBinder_native_getService(
return JHwRemoteBinder::NewObject(env, service);
}
+void JHwBinder_native_setTrebleTestingOverride(JNIEnv*, jclass, jboolean testingOverride) {
+ hardware::details::setTrebleTestingOverride(testingOverride);
+}
+
void JHwBinder_native_configureRpcThreadpool(JNIEnv *, jclass,
jlong maxThreads, jboolean callerWillJoin) {
CHECK(maxThreads > 0);
@@ -368,6 +372,9 @@ static JNINativeMethod gMethods[] = {
{ "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;",
(void *)JHwBinder_native_getService },
+ { "setTrebleTestingOverride", "(Z)V",
+ (void *)JHwBinder_native_setTrebleTestingOverride },
+
{ "configureRpcThreadpool", "(JZ)V",
(void *)JHwBinder_native_configureRpcThreadpool },
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index a88f8919ed08..ff336ee64b54 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -122,10 +122,18 @@ void signalExceptionForError(JNIEnv *env, status_t err, bool canThrowRemoteExcep
std::stringstream ss;
ss << "HwBinder Error: (" << err << ")";
- jniThrowException(
- env,
- canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException",
- ss.str().c_str());
+ const char* exception = nullptr;
+ if (canThrowRemoteException) {
+ if (err == DEAD_OBJECT) {
+ exception = "android/os/DeadObjectException";
+ } else {
+ exception = "android/os/RemoteException";
+ }
+ } else {
+ exception = "java/lang/RuntimeException";
+ }
+
+ jniThrowException(env, exception, ss.str().c_str());
break;
}
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 6ec5bce224fc..2000ecbbcf89 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -230,47 +230,28 @@ static void android_os_Parcel_writeBlob(JNIEnv* env, jclass clazz, jlong nativeP
blob.release();
}
-static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
+static int android_os_Parcel_writeInt(jlong nativePtr, jint val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
- if (parcel != NULL) {
- const status_t err = parcel->writeInt32(val);
- if (err != NO_ERROR) {
- signalExceptionForError(env, clazz, err);
- }
- }
+ return (parcel != NULL) ? parcel->writeInt32(val) : OK;
}
-static void android_os_Parcel_writeLong(JNIEnv* env, jclass clazz, jlong nativePtr, jlong val)
-{
+static int android_os_Parcel_writeLong(jlong nativePtr, jlong val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
- if (parcel != NULL) {
- const status_t err = parcel->writeInt64(val);
- if (err != NO_ERROR) {
- signalExceptionForError(env, clazz, err);
- }
- }
+ return (parcel != NULL) ? parcel->writeInt64(val) : OK;
}
-static void android_os_Parcel_writeFloat(JNIEnv* env, jclass clazz, jlong nativePtr, jfloat val)
-{
+static int android_os_Parcel_writeFloat(jlong nativePtr, jfloat val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
- if (parcel != NULL) {
- const status_t err = parcel->writeFloat(val);
- if (err != NO_ERROR) {
- signalExceptionForError(env, clazz, err);
- }
- }
+ return (parcel != NULL) ? parcel->writeFloat(val) : OK;
}
-static void android_os_Parcel_writeDouble(JNIEnv* env, jclass clazz, jlong nativePtr, jdouble val)
-{
+static int android_os_Parcel_writeDouble(jlong nativePtr, jdouble val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
- if (parcel != NULL) {
- const status_t err = parcel->writeDouble(val);
- if (err != NO_ERROR) {
- signalExceptionForError(env, clazz, err);
- }
- }
+ return (parcel != NULL) ? parcel->writeDouble(val) : OK;
+}
+
+static void android_os_Parcel_nativeSignalExceptionForError(JNIEnv* env, jclass clazz, jint err) {
+ signalExceptionForError(env, clazz, err);
}
static void android_os_Parcel_writeString8(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val)
@@ -752,14 +733,15 @@ static const JNINativeMethod gParcelMethods[] = {
{"nativeWriteByteArray", "(J[BII)V", (void*)android_os_Parcel_writeByteArray},
{"nativeWriteBlob", "(J[BII)V", (void*)android_os_Parcel_writeBlob},
- // @FastNative
- {"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},
- // @FastNative
- {"nativeWriteLong", "(JJ)V", (void*)android_os_Parcel_writeLong},
- // @FastNative
- {"nativeWriteFloat", "(JF)V", (void*)android_os_Parcel_writeFloat},
- // @FastNative
- {"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble},
+ // @CriticalNative
+ {"nativeWriteInt", "(JI)I", (void*)android_os_Parcel_writeInt},
+ // @CriticalNative
+ {"nativeWriteLong", "(JJ)I", (void*)android_os_Parcel_writeLong},
+ // @CriticalNative
+ {"nativeWriteFloat", "(JF)I", (void*)android_os_Parcel_writeFloat},
+ // @CriticalNative
+ {"nativeWriteDouble", "(JD)I", (void*)android_os_Parcel_writeDouble},
+ {"nativeSignalExceptionForError", "(I)V", (void*)android_os_Parcel_nativeSignalExceptionForError},
// @FastNative
{"nativeWriteString8", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString8},
// @FastNative
diff --git a/core/jni/android_os_VintfRuntimeInfo.cpp b/core/jni/android_os_VintfRuntimeInfo.cpp
index 9379ea6dcd10..b0271b9e92af 100644
--- a/core/jni/android_os_VintfRuntimeInfo.cpp
+++ b/core/jni/android_os_VintfRuntimeInfo.cpp
@@ -29,14 +29,12 @@ namespace android {
using vintf::RuntimeInfo;
using vintf::VintfObject;
-#define MAP_STRING_METHOD(javaMethod, cppString, flags) \
- static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) \
- { \
- std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo( \
- false /* skipCache */, flags); \
- if (info == nullptr) return nullptr; \
- return env->NewStringUTF((cppString).c_str()); \
- } \
+#define MAP_STRING_METHOD(javaMethod, cppString, flags) \
+ static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) { \
+ std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(flags); \
+ if (info == nullptr) return nullptr; \
+ return env->NewStringUTF((cppString).c_str()); \
+ }
MAP_STRING_METHOD(getCpuInfo, info->cpuInfo(), RuntimeInfo::FetchFlag::CPU_INFO);
MAP_STRING_METHOD(getOsName, info->osName(), RuntimeInfo::FetchFlag::CPU_VERSION);
@@ -54,8 +52,8 @@ MAP_STRING_METHOD(getBootVbmetaAvbVersion, vintf::to_string(info->bootVbmetaAvbV
static jlong android_os_VintfRuntimeInfo_getKernelSepolicyVersion(JNIEnv *env, jclass clazz)
{
- std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(
- false /* skipCache */, RuntimeInfo::FetchFlag::POLICYVERS);
+ std::shared_ptr<const RuntimeInfo> info =
+ VintfObject::GetRuntimeInfo(RuntimeInfo::FetchFlag::POLICYVERS);
if (info == nullptr) return 0;
return static_cast<jlong>(info->kernelSepolicyVersion());
}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index e6881b3995a8..efca33aaf520 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -39,7 +39,6 @@
#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeResolution.h"
#include "androidfw/MutexGuard.h"
-#include "androidfw/PosixUtils.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
@@ -58,7 +57,6 @@ extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
using ::android::base::StringPrintf;
-using ::android::util::ExecuteBinary;
namespace android {
@@ -114,88 +112,6 @@ constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie)
return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
}
-static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
- jclass /*clazz*/) {
- // --input-directory can be given multiple times, but idmap2 expects the directory to exist
- std::vector<std::string> input_dirs;
- struct stat st;
- if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::SYSTEM_EXT_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::SYSTEM_EXT_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::ODM_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::OEM_OVERLAY_DIR);
- }
-
- if (input_dirs.empty()) {
- LOG(WARNING) << "no directories for idmap2 to scan";
- return env->NewObjectArray(0, g_stringClass, nullptr);
- }
-
- if (access("/system/bin/idmap2", X_OK) == -1) {
- PLOG(WARNING) << "unable to execute idmap2";
- return nullptr;
- }
-
- std::vector<std::string> argv{"/system/bin/idmap2",
- "scan",
- "--recursive",
- "--target-package-name", "android",
- "--target-apk-path", "/system/framework/framework-res.apk",
- "--output-directory", "/data/resource-cache"};
-
- for (const auto& dir : input_dirs) {
- argv.push_back("--input-directory");
- argv.push_back(dir);
- }
-
- const auto result = ExecuteBinary(argv);
-
- if (!result) {
- LOG(ERROR) << "failed to execute idmap2";
- return nullptr;
- }
-
- if (result->status != 0) {
- LOG(ERROR) << "idmap2: " << result->stderr;
- return nullptr;
- }
-
- std::vector<std::string> idmap_paths;
- std::istringstream input(result->stdout);
- std::string path;
- while (std::getline(input, path)) {
- idmap_paths.push_back(path);
- }
-
- jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
- if (array == nullptr) {
- return nullptr;
- }
- for (size_t i = 0; i < idmap_paths.size(); i++) {
- const std::string path = idmap_paths[i];
- jstring java_string = env->NewStringUTF(path.c_str());
- if (env->ExceptionCheck()) {
- return nullptr;
- }
- env->SetObjectArrayElement(array, i, java_string);
- env->DeleteLocalRef(java_string);
- }
- return array;
-}
-
static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
@@ -1563,8 +1479,6 @@ static const JNINativeMethod gAssetManagerMethods[] = {
{"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
// System/idmap related methods.
- {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
- (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
{"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
(void*)NativeGetOverlayableMap},
{"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 4c9d7abeb5a1..885b0a33e43c 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -30,6 +30,7 @@
#include <unistd.h>
#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
@@ -1047,6 +1048,10 @@ static void android_os_Binder_setExtension(JNIEnv* env, jobject obj, jobject ext
jbh->setExtension(extension);
}
+static jint android_os_Binder_getNativeTid() {
+ return (jint)android::base::GetThreadId();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gBinderMethods[] = {
@@ -1078,6 +1083,8 @@ static const JNINativeMethod gBinderMethods[] = {
{ "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable },
{ "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension },
{ "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
+ // @CriticalNative
+ { "getNativeTid", "()I", (void*)android_os_Binder_getNativeTid },
};
const char* const kBinderPathName = "android/os/Binder";
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 6fd0c1371825..2436b23a45d0 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -43,25 +43,24 @@ static struct {
class NativeInputChannel {
public:
- explicit NativeInputChannel(const sp<InputChannel>& inputChannel);
+ explicit NativeInputChannel(const std::shared_ptr<InputChannel>& inputChannel);
~NativeInputChannel();
- inline sp<InputChannel> getInputChannel() { return mInputChannel; }
+ inline std::shared_ptr<InputChannel> getInputChannel() { return mInputChannel; }
void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
void dispose(JNIEnv* env, jobject obj);
private:
- sp<InputChannel> mInputChannel;
+ std::shared_ptr<InputChannel> mInputChannel;
InputChannelObjDisposeCallback mDisposeCallback;
void* mDisposeData;
};
// ----------------------------------------------------------------------------
-NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
- mInputChannel(inputChannel), mDisposeCallback(nullptr) {
-}
+NativeInputChannel::NativeInputChannel(const std::shared_ptr<InputChannel>& inputChannel)
+ : mInputChannel(inputChannel), mDisposeCallback(nullptr) {}
NativeInputChannel::~NativeInputChannel() {
}
@@ -81,7 +80,7 @@ void NativeInputChannel::dispose(JNIEnv* env, jobject obj) {
mDisposeCallback = nullptr;
mDisposeData = nullptr;
}
- mInputChannel.clear();
+ mInputChannel.reset();
}
// ----------------------------------------------------------------------------
@@ -92,7 +91,8 @@ static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEn
return reinterpret_cast<NativeInputChannel*>(longPtr);
}
-sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
+std::shared_ptr<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env,
+ jobject inputChannelObj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr;
@@ -109,8 +109,8 @@ void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChan
}
}
-static jlong android_view_InputChannel_createInputChannel(JNIEnv* env,
- sp<InputChannel> inputChannel) {
+static jlong android_view_InputChannel_createInputChannel(
+ JNIEnv* env, std::shared_ptr<InputChannel> inputChannel) {
std::unique_ptr<NativeInputChannel> nativeInputChannel =
std::make_unique<NativeInputChannel>(inputChannel);
@@ -122,8 +122,8 @@ static jlongArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* e
ScopedUtfChars nameChars(env, nameObj);
std::string name = nameChars.c_str();
- sp<InputChannel> serverChannel;
- sp<InputChannel> clientChannel;
+ std::unique_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
@@ -139,12 +139,12 @@ static jlongArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* e
}
jlong* outArray = env->GetLongArrayElements(channelPair, 0);
- outArray[0] = android_view_InputChannel_createInputChannel(env, serverChannel);
+ outArray[0] = android_view_InputChannel_createInputChannel(env, std::move(serverChannel));
if (env->ExceptionCheck()) {
return nullptr;
}
- outArray[1] = android_view_InputChannel_createInputChannel(env, clientChannel);
+ outArray[1] = android_view_InputChannel_createInputChannel(env, std::move(clientChannel));
if (env->ExceptionCheck()) {
return nullptr;
}
@@ -180,7 +180,8 @@ static jlong android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject
if (parcel) {
bool isInitialized = parcel->readInt32();
if (isInitialized) {
- sp<InputChannel> inputChannel = InputChannel::read(*parcel);
+ std::shared_ptr<InputChannel> inputChannel = std::make_shared<InputChannel>();
+ inputChannel->readFromParcel(parcel);
NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
return reinterpret_cast<jlong>(nativeInputChannel);
}
@@ -203,7 +204,7 @@ static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject o
return;
}
parcel->writeInt32(1); // initialized
- nativeInputChannel->getInputChannel()->write(*parcel);
+ nativeInputChannel->getInputChannel()->writeToParcel(parcel);
}
static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj, jlong channel) {
@@ -226,13 +227,13 @@ static jlong android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jlong
return 0;
}
- sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
+ std::shared_ptr<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "NativeInputChannel has no corresponding InputChannel");
return 0;
}
- sp<InputChannel> dupInputChannel = inputChannel->dup();
+ std::shared_ptr<InputChannel> dupInputChannel = inputChannel->dup();
if (dupInputChannel == nullptr) {
std::string message = android::base::StringPrintf(
"Could not duplicate input channel %s", inputChannel->getName().c_str());
diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h
index 2ba2dc0516d0..8030c96ab19f 100644
--- a/core/jni/android_view_InputChannel.h
+++ b/core/jni/android_view_InputChannel.h
@@ -24,10 +24,11 @@
namespace android {
typedef void (*InputChannelObjDisposeCallback)(JNIEnv* env, jobject inputChannelObj,
- const sp<InputChannel>& inputChannel, void* data);
+ const std::shared_ptr<InputChannel>& inputChannel,
+ void* data);
-extern sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env,
- jobject inputChannelObj);
+extern std::shared_ptr<InputChannel> android_view_InputChannel_getInputChannel(
+ JNIEnv* env, jobject inputChannelObj);
/* Sets a callback that is invoked when the InputChannel DVM object is disposed (or finalized).
* This is used to automatically dispose of other native objects in the input dispatcher
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index cc94d6ff5d67..979a69aa3ce2 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -55,9 +55,9 @@ static struct {
class NativeInputEventReceiver : public LooperCallback {
public:
- NativeInputEventReceiver(JNIEnv* env,
- jobject receiverWeak, const sp<InputChannel>& inputChannel,
- const sp<MessageQueue>& messageQueue);
+ NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak,
+ const std::shared_ptr<InputChannel>& inputChannel,
+ const sp<MessageQueue>& messageQueue);
status_t initialize();
void dispose();
@@ -91,13 +91,14 @@ private:
virtual int handleEvent(int receiveFd, int events, void* data) override;
};
-
-NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
- jobject receiverWeak, const sp<InputChannel>& inputChannel,
- const sp<MessageQueue>& messageQueue) :
- mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
- mInputConsumer(inputChannel), mMessageQueue(messageQueue),
- mBatchedInputEventPending(false), mFdEvents(0) {
+NativeInputEventReceiver::NativeInputEventReceiver(
+ JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,
+ const sp<MessageQueue>& messageQueue)
+ : mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
+ mInputConsumer(inputChannel),
+ mMessageQueue(messageQueue),
+ mBatchedInputEventPending(false),
+ mFdEvents(0) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
}
@@ -356,8 +357,8 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 0a2b1d4a661f..3ca43ce915cf 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -48,9 +48,9 @@ static struct {
class NativeInputEventSender : public LooperCallback {
public:
- NativeInputEventSender(JNIEnv* env,
- jobject senderWeak, const sp<InputChannel>& inputChannel,
- const sp<MessageQueue>& messageQueue);
+ NativeInputEventSender(JNIEnv* env, jobject senderWeak,
+ const std::shared_ptr<InputChannel>& inputChannel,
+ const sp<MessageQueue>& messageQueue);
status_t initialize();
void dispose();
@@ -76,12 +76,12 @@ private:
status_t receiveFinishedSignals(JNIEnv* env);
};
-
-NativeInputEventSender::NativeInputEventSender(JNIEnv* env,
- jobject senderWeak, const sp<InputChannel>& inputChannel,
- const sp<MessageQueue>& messageQueue) :
- mSenderWeakGlobal(env->NewGlobalRef(senderWeak)),
- mInputPublisher(inputChannel), mMessageQueue(messageQueue),
+NativeInputEventSender::NativeInputEventSender(JNIEnv* env, jobject senderWeak,
+ const std::shared_ptr<InputChannel>& inputChannel,
+ const sp<MessageQueue>& messageQueue)
+ : mSenderWeakGlobal(env->NewGlobalRef(senderWeak)),
+ mInputPublisher(inputChannel),
+ mMessageQueue(messageQueue),
mNextPublishedSeq(1) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName().c_str());
@@ -249,8 +249,8 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak,
jobject inputChannelObj, jobject messageQueueObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == NULL) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 9816d713c6dc..15a91107cbb1 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -342,11 +342,11 @@ static jlong android_view_MotionEvent_nativeInitialize(
return 0;
}
- MotionEvent* event;
+ std::unique_ptr<MotionEvent> event;
if (nativePtr) {
- event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event = std::unique_ptr<MotionEvent>(reinterpret_cast<MotionEvent*>(nativePtr));
} else {
- event = new MotionEvent();
+ event = std::make_unique<MotionEvent>();
}
PointerProperties pointerProperties[pointerCount];
@@ -355,7 +355,7 @@ static jlong android_view_MotionEvent_nativeInitialize(
for (jint i = 0; i < pointerCount; i++) {
jobject pointerPropertiesObj = env->GetObjectArrayElement(pointerPropertiesObjArray, i);
if (!pointerPropertiesObj) {
- goto Error;
+ return 0;
}
pointerPropertiesToNative(env, pointerPropertiesObj, &pointerProperties[i]);
env->DeleteLocalRef(pointerPropertiesObj);
@@ -363,7 +363,7 @@ static jlong android_view_MotionEvent_nativeInitialize(
jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
if (!pointerCoordsObj) {
jniThrowNullPointerException(env, "pointerCoords");
- goto Error;
+ return 0;
}
pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]);
env->DeleteLocalRef(pointerCoordsObj);
@@ -377,13 +377,7 @@ static jlong android_view_MotionEvent_nativeInitialize(
downTimeNanos, eventTimeNanos, pointerCount, pointerProperties,
rawPointerCoords);
- return reinterpret_cast<jlong>(event);
-
-Error:
- if (!nativePtr) {
- delete event;
- }
- return 0;
+ return reinterpret_cast<jlong>(event.release());
}
static void android_view_MotionEvent_nativeDispose(JNIEnv* env, jclass clazz,
diff --git a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
index f8d41e4bef54..59c413ff58a6 100644
--- a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
+++ b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
@@ -28,16 +28,19 @@ static jstring createClassloaderNamespace_native(JNIEnv* env,
jstring librarySearchPath,
jstring libraryPermittedPath,
jboolean isShared,
- jstring dexPath) {
+ jstring dexPath,
+ jstring sonameList) {
return android::CreateClassLoaderNamespace(env, targetSdkVersion,
classLoader, isShared == JNI_TRUE,
dexPath,
- librarySearchPath, libraryPermittedPath);
+ librarySearchPath,
+ libraryPermittedPath,
+ sonameList);
}
static const JNINativeMethod g_methods[] = {
{ "createClassloaderNamespace",
- "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Ljava/lang/String;",
+ "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
reinterpret_cast<void*>(createClassloaderNamespace_native) },
};
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 6e49c0dda1ad..c73441cbd4f9 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -101,6 +101,19 @@
#include "nativebridge/native_bridge.h"
+/* Functions in the callchain during the fork shall not be protected with
+ Armv8.3-A Pointer Authentication, otherwise child will not be able to return. */
+#ifdef __ARM_FEATURE_PAC_DEFAULT
+#ifdef __ARM_FEATURE_BTI_DEFAULT
+#define NO_PAC_FUNC __attribute__((target("branch-protection=bti")))
+#else
+#define NO_PAC_FUNC __attribute__((target("branch-protection=none")))
+#endif /* __ARM_FEATURE_BTI_DEFAULT */
+#else /* !__ARM_FEATURE_PAC_DEFAULT */
+#define NO_PAC_FUNC
+#endif /* __ARM_FEATURE_PAC_DEFAULT */
+
+
namespace {
// TODO (chriswailes): Add a function to initialize native Zygote data.
@@ -124,7 +137,6 @@ typedef const std::function<void(std::string)>& fail_fn_t;
static pid_t gSystemServerPid = 0;
static constexpr const char* kVoldAppDataIsolation = "persist.sys.vold_app_data_isolation_enabled";
-static constexpr const char* kPropFuse = "persist.sys.fuse";
static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
static jclass gZygoteClass;
static jmethodID gCallPostForkSystemServerHooks;
@@ -836,29 +848,20 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode,
PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
- bool isFuse = GetBoolProperty(kPropFuse, false);
bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false);
- if (isFuse) {
- if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
+ if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
PrepareDir(pass_through_source, 0710, AID_ROOT, AID_MEDIA_RW, fail_fn);
BindMount(pass_through_source, "/storage", fail_fn);
- } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
+ } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
const std::string installer_source = StringPrintf("/mnt/installer/%d", user_id);
BindMount(installer_source, "/storage", fail_fn);
- } else if (isAppDataIsolationEnabled && mount_mode == MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+ } else if (isAppDataIsolationEnabled && mount_mode == MOUNT_EXTERNAL_ANDROID_WRITABLE) {
const std::string writable_source = StringPrintf("/mnt/androidwritable/%d", user_id);
BindMount(writable_source, "/storage", fail_fn);
- } else {
- BindMount(user_source, "/storage", fail_fn);
- }
} else {
- const std::string& storage_source = ExternalStorageViews[mount_mode];
- BindMount(storage_source, "/storage", fail_fn);
-
- // Mount user-specific symlink helper into place
- BindMount(user_source, "/storage/self", fail_fn);
+ BindMount(user_source, "/storage", fail_fn);
}
}
@@ -1076,7 +1079,23 @@ static void ClearUsapTable() {
gUsapPoolCount = 0;
}
+NO_PAC_FUNC
+static void PAuthKeyChange(JNIEnv* env) {
+#ifdef __aarch64__
+ unsigned long int hwcaps = getauxval(AT_HWCAP);
+ if (hwcaps & HWCAP_PACA) {
+ const unsigned long key_mask = PR_PAC_APIAKEY | PR_PAC_APIBKEY |
+ PR_PAC_APDAKEY | PR_PAC_APDBKEY | PR_PAC_APGAKEY;
+ if (prctl(PR_PAC_RESET_KEYS, key_mask, 0, 0, 0) != 0) {
+ ALOGE("Failed to change the PAC keys: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "PAC key change failed.");
+ }
+ }
+#endif
+}
+
// Utility routine to fork a process from the zygote.
+NO_PAC_FUNC
static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
const std::vector<int>& fds_to_close,
const std::vector<int>& fds_to_ignore,
@@ -1127,6 +1146,7 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
}
// The child process.
+ PAuthKeyChange(env);
PreApplicationInit();
// Clean up any descriptors which must be closed immediately
@@ -2062,6 +2082,7 @@ static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jcl
PreApplicationInit();
}
+NO_PAC_FUNC
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
@@ -2114,6 +2135,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
return pid;
}
+NO_PAC_FUNC
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
@@ -2185,6 +2207,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
* @param is_priority_fork Controls the nice level assigned to the newly created process
* @return
*/
+NO_PAC_FUNC
static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env,
jclass,
jint read_pipe_fd,
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 50b5eadcf0aa..097af763038c 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -1722,7 +1722,7 @@ enum PageId {
// OPEN: Settings > System > Language & Region
SETTINGS_LANGUAGE_CATEGORY = 750;
- // OPEN: Settings > System > Input & Gesture > Swipe to notification gesture
+ // OPEN: Settings > System > Input & Gesture > Swipe fingerprint for notifications
SETTINGS_GESTURE_SWIPE_TO_NOTIFICATION = 751;
// OPEN: Settings > System > Input & Gesture > Double tap power button gesture
@@ -2688,4 +2688,9 @@ enum PageId {
// CATEGORY: SETTINGS
// OS: R
MEDIA_CONTROLS_SETTINGS = 1845;
+
+ // OPEN: Settings > System > Gestures > Swipe for notification
+ // CATEGORY: SETTINGS
+ // OS: R QPR
+ SETTINGS_SWIPE_BOTTOM_TO_NOTIFICATION = 1846;
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 9bbe0caa9e98..ac143ac1a147 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -508,7 +508,7 @@ message GlobalSettingsProto {
optional SettingProto job_scheduler_constants = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto job_scheduler_quota_controller_constants = 149 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto job_scheduler_time_controller_constants = 150 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 150; // job_scheduler_time_controller_constants
optional SettingProto keep_profile_in_background = 67 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 7627961760f2..ca4dc18689bc 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -208,6 +208,13 @@ message SecureSettingsProto {
optional Doze doze = 21;
optional SettingProto emergency_assistance_application = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ message EmergencyResponse {
+ optional SettingProto panic_gesture_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+
+ optional EmergencyResponse emergency_response = 83;
+
optional SettingProto enhanced_voice_privacy_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
message Gesture {
@@ -381,6 +388,15 @@ message SecureSettingsProto {
}
optional Notification notification = 41;
+ message OneHanded {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional OneHanded onehanded = 80;
+
message PackageVerifier {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -502,6 +518,7 @@ message SecureSettingsProto {
// parent profile.
optional SettingProto sync_parent_sounds = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto system_navigation_keys_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto swipe_bottom_to_notification_enabled = 82 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto theme_customization_overlay_packages = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto trust_agents_initialized = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -594,16 +611,7 @@ message SecureSettingsProto {
}
optional Zen zen = 71;
- message OneHanded {
- option (android.msg_privacy).dest = DEST_EXPLICIT;
-
- optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
- }
- optional OneHanded onehanded = 80;
-
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 82;
+ // Next tag = 84;
}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index ec99684bf636..76b7fc028a98 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -236,6 +236,8 @@ message ConstantsProto {
optional int64 api_quota_schedule_window_ms = 33;
// Whether or not to throw an exception when an app hits its schedule quota limit.
optional bool api_quota_schedule_throw_exception = 34;
+ // Whether or not to return a failure result when an app hits its schedule quota limit.
+ optional bool api_quota_schedule_return_failure_result = 35;
message QuotaController {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -322,7 +324,7 @@ message ConstantsProto {
// ready now.
reserved 1; // skip_not_ready_jobs
// Whether or not TimeController will use a non-wakeup alarm for delay constraints.
- optional bool use_non_wakeup_alarm_for_delay = 2;
+ reserved 2; // use_non_wakeup_alarm_for_delay
}
optional TimeController time_controller = 25;
@@ -335,7 +337,7 @@ message ConstantsProto {
// In this time after screen turns on, we increase job concurrency.
optional int32 screen_off_job_concurrency_increase_delay_ms = 28;
- // Next tag: 35
+ // Next tag: 36
}
// Next tag: 4
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 884d2c7e6d83..c21663811756 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -28,6 +28,7 @@ import "frameworks/base/core/proto/android/view/enums.proto";
import "frameworks/base/core/proto/android/view/surface.proto";
import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
import "frameworks/base/core/proto/android/privacy.proto";
+import "frameworks/base/core/proto/android/typedef.proto";
package com.android.server.wm;
@@ -43,8 +44,8 @@ message WindowManagerServiceDumpProto {
optional string focused_app = 4;
optional IdentifierProto input_method_window = 5;
optional bool display_frozen = 6;
- optional int32 rotation = 7;
- optional int32 last_orientation = 8;
+ optional int32 rotation = 7 [(.android.typedef) = "android.view.Surface.Rotation"];
+ optional int32 last_orientation = 8 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
optional int32 focused_display_id = 9;
}
@@ -177,7 +178,7 @@ message DisplayContentProto {
repeated WindowTokenProto ime_windows = 8 [deprecated=true];
optional int32 dpi = 9;
optional .android.view.DisplayInfoProto display_info = 10;
- optional int32 rotation = 11;
+ optional int32 rotation = 11 [(.android.typedef) = "android.view.Surface.Rotation"];
optional ScreenRotationAnimationProto screen_rotation_animation = 12;
optional DisplayFramesProto display_frames = 13;
optional int32 surface_size = 14 [deprecated=true];
@@ -264,8 +265,8 @@ message TaskProto {
optional int32 display_id = 15;
optional int32 root_task_id = 16;
- optional int32 activity_type = 17;
- optional int32 resize_mode = 18;
+ optional int32 activity_type = 17 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType"];
+ optional int32 resize_mode = 18 [(.android.typedef) = "android.appwidget.AppWidgetProviderInfo.ResizeModeFlags"];
optional int32 min_width = 19;
optional int32 min_height = 20;
@@ -309,7 +310,7 @@ message ActivityRecordProto {
repeated .android.graphics.RectProto frozen_bounds = 23;
optional bool visible = 24;
reserved 25; // configuration_container
- optional IdentifierProto identifier = 26;
+ optional IdentifierProto identifier = 26 [deprecated=true];
optional string state = 27 [(.android.privacy).dest = DEST_EXPLICIT];
optional bool front_of_task = 28;
optional int32 proc_id = 29;
@@ -332,7 +333,7 @@ message WindowStateProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional WindowContainerProto window_container = 1;
- optional IdentifierProto identifier = 2;
+ optional IdentifierProto identifier = 2 [deprecated=true];
// Unique identifier of a DisplayContent stack.
optional int32 display_id = 3;
// Unique identifier for the task stack.
@@ -351,7 +352,7 @@ message WindowStateProto {
optional .android.graphics.RectProto surface_position = 16;
optional int32 requested_width = 18;
optional int32 requested_height = 19;
- optional int32 view_visibility = 20;
+ optional int32 view_visibility = 20 [(.android.typedef) = "android.view.View.Visibility"];
optional int32 system_ui_visibility = 21;
optional bool has_surface = 22;
optional bool is_ready_for_display = 23;
@@ -424,10 +425,11 @@ message WindowContainerProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional ConfigurationContainerProto configuration_container = 1;
- optional int32 orientation = 2;
+ optional int32 orientation = 2 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
optional bool visible = 3;
optional SurfaceAnimatorProto surface_animator = 4;
repeated WindowContainerChildProto children = 5;
+ optional IdentifierProto identifier = 6;
}
/* represents a generic child of a WindowContainer */
diff --git a/core/proto/android/typedef.proto b/core/proto/android/typedef.proto
new file mode 100644
index 000000000000..be8742de5673
--- /dev/null
+++ b/core/proto/android/typedef.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android;
+
+import "google/protobuf/descriptor.proto";
+
+extend google.protobuf.FieldOptions {
+ // Used to specify the IntDef annotation type so that ints
+ // can be associated with their string representation
+
+ // 60001 is a random field numbers assigned to the custom options
+ // numbers between 50000 and 99999 are reserved for internal use within individual organizations
+ optional string typedef = 60001;
+}
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
index 272a24515367..64e6da852529 100644
--- a/core/proto/android/view/windowlayoutparams.proto
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -19,6 +19,7 @@ syntax = "proto2";
import "frameworks/base/core/proto/android/graphics/pixelformat.proto";
import "frameworks/base/core/proto/android/view/display.proto";
import "frameworks/base/core/proto/android/privacy.proto";
+import "frameworks/base/core/proto/android/typedef.proto";
package android.view;
option java_multiple_files = true;
@@ -27,15 +28,15 @@ option java_multiple_files = true;
message WindowLayoutParamsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional int32 type = 1;
+ optional int32 type = 1 [(.android.typedef) = "android.view.WindowManager.LayoutParams.WindowType"];
optional int32 x = 2;
optional int32 y = 3;
optional int32 width = 4;
optional int32 height = 5;
optional float horizontal_margin = 6;
optional float vertical_margin = 7;
- optional int32 gravity = 8;
- optional int32 soft_input_mode = 9;
+ optional int32 gravity = 8; // TODO (b/160129453): Add IntDef
+ optional int32 soft_input_mode = 9 [(.android.typedef) = "android.view.WindowManager.LayoutParams.SoftInputModeFlags"];
optional .android.graphics.PixelFormatProto.Format format = 10;
optional int32 window_animations = 11;
optional float alpha = 12;
@@ -53,17 +54,17 @@ message WindowLayoutParamsProto {
optional float preferred_refresh_rate = 16;
optional int32 preferred_display_mode_id = 17;
optional bool has_system_ui_listeners = 18;
- optional uint32 input_feature_flags = 19;
+ optional uint32 input_feature_flags = 19; // TODO (b/160129453): Add IntDef
optional int64 user_activity_timeout = 20;
optional DisplayProto.ColorMode color_mode = 23;
- optional uint32 flags = 24;
- optional uint32 private_flags = 26;
- optional uint32 system_ui_visibility_flags = 27;
- optional uint32 subtree_system_ui_visibility_flags = 28;
- optional uint32 appearance = 29;
- optional uint32 behavior = 30;
- optional uint32 fit_insets_types = 31;
- optional uint32 fit_insets_sides = 32;
+ optional uint32 flags = 24 [(.android.typedef) = "android.view.WindowManager.LayoutParams.Flags"];
+ optional uint32 private_flags = 26 [(.android.typedef) = "android.view.WindowManager.LayoutParams.PrivateFlags"];
+ optional uint32 system_ui_visibility_flags = 27; // TODO (b/160129453): Add IntDef
+ optional uint32 subtree_system_ui_visibility_flags = 28; // TODO (b/160129453): Add IntDef
+ optional uint32 appearance = 29 [(.android.typedef) = "android.view.WindowInsetsController.Appearance"];
+ optional uint32 behavior = 30 [(.android.typedef) = "android.view.WindowInsetsController.Behavior"];
+ optional uint32 fit_insets_types = 31 [(.android.typedef) = "android.view.WindowInsets.Type.InsetsType"];
+ optional uint32 fit_insets_sides = 32 [(.android.typedef) = "android.view.WindowInsets.Side.InsetsSide"];
optional bool fit_ignore_visibility = 33;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0fd4bf944607..bae3bf905082 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -227,6 +227,8 @@
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED" />
<protected-broadcast
android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" />
<protected-broadcast
@@ -263,6 +265,7 @@
<protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE" />
<protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
@@ -352,6 +355,7 @@
<protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER" />
<protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER" />
<protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED" />
+ <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_CLICKED" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
@@ -4845,7 +4849,7 @@
<!-- @SystemApi Allows an application to turn on / off quiet mode.
@hide -->
<permission android:name="android.permission.MODIFY_QUIET_MODE"
- android:protectionLevel="signature|privileged|wellbeing" />
+ android:protectionLevel="signature|privileged|wellbeing|development" />
<!-- Allows internal management of the camera framework
@hide -->
@@ -5031,6 +5035,11 @@
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
+ <!-- @hide @TestApi Allows apps to reset the state of {@link com.android.server.am.AppErrors}.
+ <p>CTS tests will use UiAutomation.adoptShellPermissionIdentity() to gain access. -->
+ <permission android:name="android.permission.RESET_APP_ERRORS"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
@@ -5068,7 +5077,7 @@
android:process=":ui"
android:exported="true"
android:visibleToInstantApps="true">
- <intent-filter>
+ <intent-filter android:priority="100">
<action android:name="android.intent.action.CHOOSER" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.VOICE" />
diff --git a/core/res/res/anim/screen_rotate_180_enter.xml b/core/res/res/anim/screen_rotate_180_enter.xml
index 889a615e07f4..3b6b4072dbcd 100644
--- a/core/res/res/anim/screen_rotate_180_enter.xml
+++ b/core/res/res/anim/screen_rotate_180_enter.xml
@@ -25,4 +25,10 @@
android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/fast_out_slow_in"
android:duration="@android:integer/config_screen_rotation_total_180" />
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/screen_rotation_alpha_in"
+ android:startOffset="@android:integer/config_screen_rotation_fade_in_delay"
+ android:duration="@android:integer/config_screen_rotation_fade_in" />
</set>
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index 766fcfae1f91..26fb6d8df506 100644
--- a/core/res/res/anim/screen_rotate_180_exit.xml
+++ b/core/res/res/anim/screen_rotate_180_exit.xml
@@ -25,4 +25,9 @@
android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/fast_out_slow_in"
android:duration="@android:integer/config_screen_rotation_total_180" />
-</set> \ No newline at end of file
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/screen_rotation_alpha_out"
+ android:duration="@android:integer/config_screen_rotation_fade_out" />
+</set>
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 3615b9e2f9cb..df271f0f2942 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -25,6 +25,7 @@
android:id="@+id/actions_container_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="end"
android:orientation="horizontal"
android:paddingEnd="@dimen/bubble_gone_padding_end"
>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index 83301d0f6afb..48cfa073d08b 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -128,6 +128,9 @@
android:layout_weight="1">
<!-- Header -->
+
+ <!-- Use layout_marginStart instead of paddingStart to work around strange
+ measurement behavior on lower display densities. -->
<LinearLayout
android:id="@+id/conversation_header"
android:layout_width="wrap_content"
@@ -135,11 +138,11 @@
android:orientation="horizontal"
android:paddingTop="16dp"
android:layout_marginBottom="2dp"
- android:paddingStart="@dimen/conversation_content_start"
+ android:layout_marginStart="@dimen/conversation_content_start"
>
<TextView
android:id="@+id/conversation_text"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 82927bbbb52f..03c682fd74af 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Druk kieslys om oop te sluit of maak noodoproep."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Druk kieslys om oop te maak."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Teken patroon om te ontsluit"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Noodgeval"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Keer terug na oproep"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Reg!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probeer weer"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Opgedateer deur jou administrateur"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Uitgevee deur jou administrateur"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Batterybespaarder doen die volgende om die batterylewe te verleng:\n\n•Skakel Donkertema aan\n•Skakel agtergrondaktiwiteit, sommige visuele effekte en ander kenmerke, soos \"Ok Google\", af of beperk hulle\n\n"<annotation id="url">"Kom meer te wete"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Batterybespaarder doen die volgende om batterylewe te verleng:\n\n•Skakel Donkertema aan\n•Skakel agtergrondaktiwiteit, sommige visuele effekte en ander kenmerke, soos \"Ok Google\", af of beperk hulle"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Batterybespaarder doen die volgende om die batterylewe te verleng:\n\n• Skakel Donkertema aan\n• Skakel agtergrondaktiwiteit, sommige visuele effekte en ander kenmerke, soos \"Ok Google\", af of beperk dit\n\n"<annotation id="url">"Kom meer te wete"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Batterybespaarder doen die volgende om batterylewe te verleng:\n\n• Skakel Donkertema aan\n• Skakel agtergrondaktiwiteit, sommige visuele effekte en ander kenmerke, soos \"Ok Google\", af of beperk dit"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Databespaarder verhoed sommige programme om data in die agtergrond te stuur of te aanvaar om datagebruik te help verminder. \'n Program wat jy tans gebruik kan by data ingaan, maar sal dit dalk minder gereeld doen. Dit kan byvoorbeeld beteken dat prente nie wys totdat jy op hulle tik nie."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Skakel Databespaarder aan?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Skakel aan"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofoon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"wys tans bo-oor ander programme op jou skerm"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Gee terugvoer"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Roetinemodus-inligtingkennisgewing"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery kan afloop voordat dit normaalweg gelaai word"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterybespaarder is geaktiveer om batterylewe te verleng"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 21094a971386..f80da896d8fa 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ለመክፈት ምናሌ ተጫንወይም የአደጋ ጊዜ ጥሪ አድርግ።"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ለመክፈት ምናሌ ተጫን"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ለመክፈት ስርዓተ ጥለት ሳል"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ድንገተኛ አደጋ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ወደ ጥሪ ተመለስ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ትክክል!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"እንደገና ሞክር"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"በእርስዎ አስተዳዳሪ ተዘምኗል"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"በእርስዎ አስተዳዳሪ ተሰርዟል"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"እሺ"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"የባትሪ ዕድሜን ለማራዘም፣ የባትሪ ቆጣቢ፦\n\n•ጨለማ ገጽታን ያበራል\n•የበስተጀርባ እንቅስቃሴን፣ አንዳንድ የሚታዩ ማሳመሪያዎችን፣ እና ሌሎች እንደ «Hey Google» ያሉ ባህሪያትን ያጠፋል ወይም ይገድባል\n\n"<annotation id="url">"የበለጠ ለመረዳት"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"የባትሪ ዕድሜን ለማራዘም፣ የባትሪ ቆጣቢ፦\n\n•ጨለማ ገጽታን ያበራል\n•የበስተጀርባ እንቅስቃሴን፣ አንዳንድ የሚታዩ ማሳመሪያዎችን፣ እና ሌሎች እንደ «Hey Google» ያሉ ባህሪያትን ያጠፋል ወይም ይገድባል"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"የባትሪ ዕድሜን ለማራዘም፣ የባትሪ ቆጣቢ፦\n\n•ጨለማ ገጽታን ያበራል\n•የበስተጀርባ እንቅስቃሴን፣ አንዳንድ የሚታዩ ማሳመሪያዎችን፣ እና ሌሎች እንደ «Hey Google» ያሉ ባህሪያትን ያጠፋል ወይም ይገድባል\n\n"<annotation id="url">"የበለጠ ለመረዳት"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"የባትሪ ዕድሜን ለማራዘም የባትሪ ኃይል ቆጣቢ፦\n\n• ጨለማ ገጽታን ያበራል\n• የበስተጀርባ እንቅስቃሴን፣ አንዳንድ ምስላዊ ተጽዕኖዎችን እና ሌሎች እንደ «Hey Google» ያሉ ባህሪያትን ያጠፋል ወይም ይገድባል"</string>
<string name="data_saver_description" msgid="4995164271550590517">"የውሂብ አጠቃቀም እንዲቀንስ ለማገዝ ውሂብ ቆጣቢ አንዳንድ መተግበሪያዎች ከበስተጀርባ ሆነው ውሂብ እንዳይልኩ ወይም እንዳይቀበሉ ይከለክላቸዋል። በአሁኑ ጊዜ እየተጠቀሙበት ያለ መተግበሪያ ውሂብ ሊደርስ ይችላል፣ ነገር ግን ባነሰ ተደጋጋሚነት ሊሆን ይችላል። ይሄ ማለት ለምሳሌ ምስሎችን መታ እስኪያደርጓቸው ድረስ ላይታዩ ይችላሉ ማለት ነው።"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ውሂብ ቆጣቢ ይጥፋ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"አብራ"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"ካሜራ"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"ማይክሮፎን"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"በማያዎ ላይ በሌሎች መተግበሪያዎች ላይ በማሳየት ላይ"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ግብረመልስ ይስጡ"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"የዕለት ተዕለት ሁነታ መረጃ ማሳወቂያዎች"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ባትሪ ከተለመደው ኃይል መሙላት በፊት ሊያልቅ ይችላል"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"የባትሪ ቆጣቢ የባትሪ ዕድሜን ለማራዘም ገብሯል።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0f1b1b5a8de1..41e3e26afc2c 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -844,7 +844,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"اضغط على \"القائمة\" لإلغاء التأمين أو إجراء اتصال بالطوارئ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"رسم نقش لإلغاء التأمين"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"الطوارئ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"العودة إلى الاتصال"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحيح!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"أعد المحاولة"</string>
@@ -1172,7 +1173,7 @@
<string name="Midnight" msgid="8176019203622191377">"منتصف الليل"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="1532369154488982046">"اختيار الكل"</string>
+ <string name="selectAll" msgid="1532369154488982046">"تحديد الكل"</string>
<string name="cut" msgid="2561199725874745819">"قص"</string>
<string name="copy" msgid="5472512047143665218">"نسخ"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"تعذّر النسخ في الحافظة"</string>
@@ -1884,8 +1885,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"تم التحديث بواسطة المشرف"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"تم الحذف بواسطة المشرف"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"حسنًا"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"‏لإطالة عمر البطارية، تعمل ميزة \"توفير شحن البطارية\" على:\n\n• تفعيل \"المظهر الداكن\"\n• إيقاف أو حظر النشاط في الخلفية وبعض التأثيرات المرئية والميزات الأخرى، مثل \"Ok Google\".\n\n"<annotation id="url">"مزيد من المعلومات"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"‏لإطالة عمر البطارية، تعمل ميزة \"توفير شحن البطارية\" على:\n\n• تفعيل \"المظهر الداكن\"\n• إيقاف أو حظر النشاط في الخلفية وبعض التأثيرات المرئية والميزات الأخرى، مثل \"Ok Google\"."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"‏لإطالة عمر البطارية، تعمل ميزة \"توفير شحن البطارية\" على:\n·تفعيل \"المظهر الداكن\"\n إيقاف أو حظر النشاط في الخلفية وبعض التأثيرات المرئية والميزات الأخرى، مثل \"Ok Google\"\n\n\n"<annotation id="url">"مزيد من المعلومات"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"‏لإطالة عمر البطارية، تعمل ميزة \"توفير شحن البطارية\" على:\n\n• تفعيل \"المظهر الداكن\"\n• إيقاف أو حظر النشاط في الخلفية وبعض التأثيرات المرئية والميزات الأخرى، مثل \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"للمساعدة في خفض استخدام البيانات، تمنع ميزة \"توفير البيانات\" بعض التطبيقات من إرسال البيانات وتلقّيها في الخلفية. يمكن للتطبيقات المتاحة لديك الآن استخدام البيانات، ولكن لا يمكنها الإكثار من ذلك. وهذا يعني أن الصور مثلاً لا تظهر حتى تنقر عليها."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"هل تريد تفعيل توفير البيانات؟"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"تفعيل"</string>
@@ -1981,9 +1982,9 @@
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"توسيع"</string>
<string name="expand_button_content_description_expanded" msgid="7484217944948667489">"تصغير"</string>
<string name="expand_action_accessibility" msgid="1947657036871746627">"تبديل التوسيع"</string>
- <string name="usb_midi_peripheral_name" msgid="490523464968655741">"‏منفذ الأجهزة الطرفية المزودة بكابل USB ونظام التشغيل Android"</string>
+ <string name="usb_midi_peripheral_name" msgid="490523464968655741">"‏منفذ الأجهزة الملحقة المزودة بكابل USB ونظام التشغيل Android"</string>
<string name="usb_midi_peripheral_manufacturer_name" msgid="7557148557088787741">"Android"</string>
- <string name="usb_midi_peripheral_product_name" msgid="2836276258480904434">"‏منفذ الأجهزة الطرفية المزودة بكابل USB"</string>
+ <string name="usb_midi_peripheral_product_name" msgid="2836276258480904434">"‏منفذ الأجهزة الملحقة المزودة بكابل USB"</string>
<string name="floating_toolbar_open_overflow_description" msgid="2260297653578167367">"خيارات أخرى"</string>
<string name="floating_toolbar_close_overflow_description" msgid="3949818077708138098">"إغلاق التجاوز"</string>
<string name="maximize_button_text" msgid="4258922519914732645">"تكبير"</string>
@@ -2129,8 +2130,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"كاميرا"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"ميكروفون"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"العرض فوق التطبيقات الأخرى على شاشتك"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"تقديم تعليقات"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"إشعار معلومات \"وضع سلسلة الإجراءات\""</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"قد تنفد طاقة البطارية قبل الشحن المعتاد"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"تم تفعيل \"توفير شحن البطارية\" لإطالة عمرها."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index c92b077337c0..c6c8bb502ba8 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"আনলক কৰিবলৈ বা জৰুৰীকালীন কল কৰিবলৈ মেনু টিপক।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"আনলক কৰিবলৈ মেনু টিপক।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"আনলক কৰিবলৈ আর্হি আঁকক"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"জৰুৰীকালীন"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"কললৈ উভতি যাওক"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"শুদ্ধ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আকৌ চেষ্টা কৰক"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"আপোনাৰ প্ৰশাসকে আপেডট কৰিছে"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"আপোনাৰ প্ৰশাসকে মচিছে"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ঠিক আছে"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"বেটাৰীৰ জীৱনকাল বৃদ্ধি কৰিবলৈ, বেটাৰী সঞ্চয়কাৰীয়ে:\n\n•গাঢ় ৰঙৰ থীম অন কৰে\n•পটভূমিৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল প্ৰভাৱ আৰু “Hey Google”ৰ দৰে অন্য সুবিধাসমূহ অফ কৰে অথবা সেইবোৰ সীমাবদ্ধ কৰে\n\n"<annotation id="url">"অধিক জানক"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"বেটাৰীৰ জীৱনকাল বৃদ্ধি কৰিবলৈ, বেটাৰী সঞ্চয়কাৰীয়ে:\n\n•গাঢ় ৰঙৰ থীম অন কৰে\n•পটভূমিৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল প্ৰভাৱ আৰু “Hey Google”ৰ দৰে অন্য সুবিধাসমূহ অফ কৰে অথবা সেইবোৰ সীমাবদ্ধ কৰে"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"বেটাৰীৰ জীৱনকাল বৃদ্ধি কৰিবলৈ, বেটাৰী সঞ্চয়কাৰীয়ে:\n\n•গাঢ় ৰঙৰ থীম অন কৰে\n•নেপথ্যৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল ইফেক্ট আৰু “Hey Google”ৰ দৰে অন্য সুবিধাসমূহ অফ কৰে অথবা সেইবোৰ সীমাবদ্ধ কৰে\n\n"<annotation id="url">"অধিক জানক"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"বেটাৰীৰ জীৱনকাল বৃদ্ধি কৰিবলৈ বেটাৰী সঞ্চয়কাৰীয়ে:\n\n• গাঢ় ৰঙৰ থীম অন কৰে\n• নেপথ্যৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল ইফেক্ট আৰু “Hey Google”ৰ দৰে অন্য সুবিধাসমূহ অফ কৰে অথবা সীমাবদ্ধ কৰে"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ডেটা ব্য়ৱহাৰ মাত্ৰা কম কৰিবৰ বাবে ডেটা সঞ্চয়কাৰীয়ে কিছুমান এপক নেপথ্য়ত ডেটা প্ৰেৰণ বা সংগ্ৰহ কৰাত বাধা প্ৰদান কৰে। আপুনি বৰ্তমান ব্য়ৱহাৰ কৰি থকা এটা এপে ডেটা ব্য়ৱহাৰ কৰিব পাৰে, কিন্তু সঘনাই এই কার্য কৰিব নোৱাৰিব পাৰে। ইয়াৰ অৰ্থ এইয়ে হ\'ব পাৰে যে, উদাহৰণস্বৰূপে, আপুনি নিটিপা পর্যন্ত প্ৰতিচ্ছবিসমূহ দেখুওৱা নহ’ব।"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ডেটা সঞ্চয়কাৰী অন কৰিবনে?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"অন কৰক"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"কেমেৰা"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"মাইক্ৰ\'ফ\'ন"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"স্ক্ৰীণত অইন এপৰ ওপৰত দেখুৱাওক"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"মতামত দিয়ক"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ৰুটিন ম’ডৰ তথ্য জাননী"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"চ্চাৰ্জ কৰাৰ সচৰাচৰ সময়ৰ আগতেই বেটাৰি শেষ হ’ব পাৰে"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"বেটাৰিৰ খৰচ কমাবলৈ বেটাৰি সঞ্চয়কাৰী অন কৰা হৈছে"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 380869faa957..f0ff88365173 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Təcili zəng kilidini açmaq və ya yerləşdirmək üçün Menyu düyməsinə basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Kilidi açmaq üçün model çəkin"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Təcili"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zəngə qayıt"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Düzdür!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Bir də cəhd edin"</string>
@@ -1651,7 +1652,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rəng İnversiyası"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Rəng korreksiyası"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edildi."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> istifadə etmək üçün hər iki səs düyməsini üç saniyə basıb saxlayın"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Əlçatımlılıq düyməsinə toxunduqda istifadə edəcəyiniz funksiyanı seçin:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Əlçatımlılıq jesti (iki barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Admin tərəfindən yeniləndi"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Admin tərəfindən silindi"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Batareyanın ömrünü artırmaq üçün Enerjiyə Qənaət funksiyası:\n\n•Qaranlıq temanı aktiv edir\n•Arxa fondakı fəaliyyəti, bəzi vizual effektləri və “Hey Google” kimi digər funksiyaları deaktiv edir və ya məhdudlaşdırır\n\n"<annotation id="url">"Ətraflı məlumat"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Batareyanın ömrünü artırmaq üçün Enerjiyə Qənaət funksiyası:\n\n•Qaranlıq temanı aktiv edir\n•Arxa fondakı fəaliyyəti, bəzi vizual effektləri və “Hey Google” kimi digər funksiyaları deaktiv edir və ya məhdudlaşdırır"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Batareyanın ömrünü artırmaq üçün Enerjiyə Qənaət funksiyası:\n\n• Qaranlıq temanı aktiv edir\n• Arxa fondakı fəaliyyəti, bəzi vizual effektləri və “Hey Google” kimi digər funksiyaları deaktiv edir və ya məhdudlaşdırır\n\n"<annotation id="url">"Ətraflı məlumat"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Batareyanın ömrünü artırmaq üçün Enerjiyə Qənaət funksiyası:\n\n• Qaranlıq temanı aktiv edir\n• Arxa fondakı fəaliyyəti, bəzi vizual effektləri və “Hey Google” kimi digər funksiyaları deaktiv edir və ya məhdudlaşdırır"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Data istifadəsini azalatmaq üçün, Data Qanaəti bəzi tətbiqlərin arxafonda data göndərməsinin və qəbulunun qarşısını alır. Hazırda işlətdiyiniz tətbiq dataya daxil ola bilər, ancaq bunu tez-tez edə bilməz. Bu o deməkdir ki, məsələn, Siz üzərinə tıklamadıqca o şəkillər göstərilməyəcək."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Data Qənaəti aktiv edilsin?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivləşdirin"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ekrandakı digər tətbiqlərdə göstərin"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Rəy bildirin"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Rejim üçün məlumat bildirişi"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batareya həmişəki vaxtdan əvvəl bitə bilər"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Enerjiyə Qənaət rejimi batareya istifadəsinin müddətini artırmaq üçün aktiv edilir"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index c7b57b01ecf1..8319da7ca7ee 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite „Meni“ da biste otključali telefon ili uputite hitan poziv."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite „Meni“ za otključavanje."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Unesite šablon za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitne službe"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazad na poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tačno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probajte ponovo"</string>
@@ -1145,7 +1146,7 @@
<string name="capital_off" msgid="7443704171014626777">"NE"</string>
<string name="checked" msgid="9179896827054513119">"označeno je"</string>
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Dovršavanje radnje pomoću"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Otvorite pomoću"</string>
@@ -1179,7 +1180,7 @@
<string name="noApplications" msgid="1186909265235544019">"Nijedna aplikacija ne može da obavlja ovu radnju."</string>
<string name="aerr_application" msgid="4090916809370389109">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> je zaustavljena"</string>
<string name="aerr_process" msgid="4268018696970966407">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> je zaustavljen"</string>
- <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> se stalno zaustavlja(ju)"</string>
+ <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> se stalno zaustavlja"</string>
<string name="aerr_process_repeated" msgid="1153152413537954974">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> se stalno zaustavlja"</string>
<string name="aerr_restart" msgid="2789618625210505419">"Ponovo otvori aplikaciju"</string>
<string name="aerr_report" msgid="3095644466849299308">"Pošaljite povratne informacije"</string>
@@ -1815,8 +1816,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Potvrdi"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n•uključuje tamnu temu\n•isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“\n\n"<annotation id="url">"Saznajte više"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n•uključuje tamnu temu\n•isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjila potrošnja podataka, Ušteda podataka sprečava neke aplikacije da šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može da pristupa podacima, ali će to činiti ređe. Na primer, slike se neće prikazivati dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Želite da uključite Uštedu podataka?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string>
@@ -2030,8 +2031,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"prikazuje se na ekranu dok koristite druge aplikacije"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Pošaljite povratne informacije"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Obaveštenje o informacijama Rutinskog režima"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterija će se možda isprazniti pre uobičajenog punjenja"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Ušteda baterije je aktivirana da bi se produžilo trajanje baterije"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 82ffc9b13e58..3540516fd9f3 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Націсніце \"Меню\", каб разблакаваць, або зрабіце экстраны выклік."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Націсніце \"Меню\", каб разблакаваць."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Намалюйце камбінацыю разблакоўкі, каб разблакаваць"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Экстранны выклік"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Вярнуцца да выкліку"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правільна!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Паспрабуйце яшчэ раз"</string>
@@ -1838,8 +1839,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Абноўлены вашым адміністратарам"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Выдалены вашым адміністратарам"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Каб павялічыць тэрмін службы акумулятара, рэжым эканоміі зараду:\n\n•·уключае цёмную тэму;\n• выключае ці абмяжоўвае дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты і іншыя функцыі, напрыклад \"Ok Google\"\n\n"<annotation id="url">"Даведацца больш"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Каб павялічыць тэрмін службы акумулятара, рэжым эканоміі зараду:\n\n• уключае цёмную тэму;\n• выключае ці абмяжоўвае дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты і іншыя функцыі, напрыклад \"Ok Google\""</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Каб павялічыць тэрмін службы акумулятара, рэжым эканоміі зараду:\n\n•·уключае цёмную тэму;\n• выключае ці абмяжоўвае дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты і іншыя функцыі, напрыклад \"Ok Google\"\n\n"<annotation id="url">"Даведацца больш"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Каб павялічыць тэрмін службы акумулятара, рэжым эканоміі зараду:\n\n• уключае цёмную тэму;\n• выключае ці абмяжоўвае дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты і іншыя функцыі, напрыклад \"Ok Google\""</string>
<string name="data_saver_description" msgid="4995164271550590517">"У рэжыме \"Эканомія трафіка\" фонавая перадача для некаторых праграмам адключана. Праграма, якую вы зараз выкарыстоўваеце, можа атрымліваць доступ да даных, але радзей, чым звычайна. Напрыклад, відарысы могуць не загружацца, пакуль вы не націсніце на іх."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Уключыць Эканомію трафіка?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Уключыць"</string>
@@ -2063,8 +2064,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Мікрафон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"паказваецца паверх іншых праграм на экране"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Даць водгук"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Апавяшчэнне з інфармацыяй пра ўсталяваны рэжым"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Акумулятар можа разрадзіцца хутчэй, чым прыйдзе час звычайнай зарадкі"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Каб павялічыць тэрмін работы акумулятара, уключаны рэжым эканоміі зараду"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index ee522b390577..6643bcfa822c 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Натиснете „Меню“, за да отключите или да извършите спешно обаждане."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Натиснете „Меню“, за да отключите."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Нарисувайте фигура, за да отключите"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Спешни случаи"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад към обаждането"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правилно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Опитайте отново"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Актуализирано от администратора ви"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Изтрито от администратора ви"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"С цел удължаване на живота на батерията режимът за запазването ѝ:\n\n•·включва тъмната тема;\n•·изключва или ограничава активността на заден план, някои визуални ефекти и други функции, като например „Ok Google“.\n\n"<annotation id="url">"Научете повече"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"С цел удължаване на живота на батерията режимът за запазването ѝ:\n\n• включва тъмната тема;\n• изключва или ограничава активността на заден план, някои визуални ефекти и други функции, като например „Ok Google“."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"С цел удължаване на живота на батерията режимът за запазването ѝ:\n\n•·включва тъмната тема;\n•·изключва или ограничава активността на заден план, някои визуални ефекти и други функции, като например „Ok Google“.\n\n"<annotation id="url">"Научете повече"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"С цел удължаване на живота на батерията режимът за запазването ѝ:\n\n• включва тъмната тема;\n• изключва или ограничава активността на заден план, някои визуални ефекти и други функции, като например „Ok Google“."</string>
<string name="data_saver_description" msgid="4995164271550590517">"С цел намаляване на преноса на данни функцията за икономия на данни не позволява на някои приложения да изпращат или получават данни на заден план. Понастоящем използвано от вас приложение може да използва данни, но по-рядко. Това например може да означава, че изображенията не се показват, докато не ги докоснете."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ще вкл. ли „Икономия на данни“?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Включване"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"се показва върху други приложения на екрана"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Предоставяне на отзиви"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Известие с информация за режима на поредица"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батерията може да се изтощи преди обичайното зареждане"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Режимът за запазване на батерията е активиран с цел удължаване на живота на батерията"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 614d36404a49..cbec4ac51d09 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"আনলক করতে বা জরুরি কল করতে মেনু টিপুন৷"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"আনলক করতে মেনু টিপুন৷"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"আনলক করতে প্যাটার্ন আঁকুন"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"জরুরী"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"কলে ফিরুন"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"সঠিক!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আবার চেষ্টা করুন"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"আপনার প্রশাসক আপডেট করেছেন"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"আপনার প্রশাসক মুছে দিয়েছেন"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ঠিক আছে"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ব্যাটারি আরও বেশিক্ষণ চালাতে, ব্যাটারি সেভার:\n\n•গাঢ় থিম চালু করে\n•ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট এবং “হ্যালো Google”-এর মতো অন্যান্য ফিচার বন্ধ বা সীমিত করে\n\n"<annotation id="url">"আরও জানুন"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ব্যাটারি আরও বেশিক্ষণ চালাতে, ব্যাটারি সেভার:\n\n•গাঢ় থিম চালু করে\n•ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট এবং “হ্যালো Google”-এর মতো অন্যান্য ফিচার বন্ধ বা সীমিত করে"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ব্যাটারি আরও বেশিক্ষণ চালাতে, ব্যাটারি সেভার:\n\n• ডার্ক থিম চালু করে\n• ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট এবং “হ্যালো Google”-এর মতো অন্যান্য ফিচার বন্ধ বা সীমিত করে\n\n"<annotation id="url">"আরও জানুন"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ব্যাটারি আরও বেশিক্ষণ চালাতে, ব্যাটারি সেভার:\n\n• ডার্ক থিম চালু করে\n• ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট এবং “হ্যালো Google”-এর মতো অন্যান্য ফিচার বন্ধ অথবা সীমিত করে"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ডেটার ব্যবহার কমাতে সহায়তা করার জন্য, ডেটা সেভার ব্যাকগ্রাউন্ডে কিছু অ্যাপ্লিকেশনকে ডেটা পাঠাতে বা গ্রহণ করতে বাধা দেয়৷ আপনি বর্তমানে এমন একটি অ্যাপ্লিকেশন ব্যবহার করছেন যেটি ডেটা অ্যাক্সেস করতে পারে, তবে সেটি কমই করে৷ এর ফলে যা হতে পারে, উদাহরণস্বরূপ, আপনি ছবির উপর ট্যাপ না করা পর্যন্ত সেগুলি দেখানো হবে না৷"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ডেটা সেভার চালু করবেন?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"চালু করুন"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"ক্যামেরা"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"মাইক্রোফোন"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"স্ক্রিনে অন্যান্য অ্যাপের উপরে দেখানো হচ্ছে"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"মতামত জানান"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"রুটিন মোডের তথ্য সংক্রান্ত বিজ্ঞপ্তি"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"সাধারণত যখন চার্জ দেন, তার আগে চার্জ শেষ হয়ে যেতে পারে"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ডিভাইস বেশিক্ষণ চালু রাখতে ব্যাটারি সেভার চালু করা হয়েছে"</string>
@@ -2041,7 +2041,7 @@
<string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"দ্রুত সেটিংস"</string>
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"পাওয়ার ডায়লগ"</string>
<string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"লক স্ক্রিন"</string>
- <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"স্ক্রিনশট"</string>
+ <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"স্ক্রিনশট নিন"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"অন-স্ক্রিন অ্যাক্সেসিবিলিটি শর্টকাট"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"অন-স্ক্রিন অ্যাক্সেসিবিলিটি শর্টকাট বেছে নেওয়ার বিকল্প"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"অ্যাক্সেসিবিলিটি শর্টকাট"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 95d1b95a0d8c..87b02f553037 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite dugme Meni kako biste otključali uređaj ili obavili hitni poziv."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite dugme Meni za otključavanje uređaja."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Nacrtajte uzorak za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitno"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Povratak na poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ispravno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Pokušajte ponovo"</string>
@@ -1145,8 +1146,8 @@
<string name="capital_off" msgid="7443704171014626777">"Isključeno"</string>
<string name="checked" msgid="9179896827054513119">"označeno"</string>
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Izvrši akciju koristeći"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršite akciju koristeći %1$s"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvršiti akciju"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Otvori koristeći"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"Otvori koristeći %1$s"</string>
@@ -1645,7 +1646,7 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li koristiti Prečicu za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kada je prečica uključena, pritiskom i držanjem oba dugmeta za jačinu zvuka u trajanju od 3 sekunde pokrenut će se funkcija pristupačnosti."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Uključiti funkcije pristupačnosti?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkcije pristupačnosti. Ovo može uticati na način rada uređaja.\n\nTrenutne funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nOdabrane funkcije možete izmijeniti u odjeljku Postavke &gt; Pristupačnost."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkcije pristupačnosti. Ovo može uticati na način rada uređaja.\n\nTrenutne funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nOdabrane funkcije možete promijeniti u odjeljku Postavke &gt; Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Uključiti <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkciju pristupačnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Ovo može promijeniti način rada uređaja.\n\nOvu prečicu možete zamijeniti drugom funkcijom u odjeljku Postavke &gt; Pristupačnost."</string>
@@ -1815,8 +1816,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je vaš administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je vaš administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Uredu"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Radi produženja vijeka trajanja baterije, Ušteda baterije:\n\n•Uključuje Tamnu temu\n•Isključuje ili ograničava aktivnosti u pozadini, određene vizuelne efekte i druge funkcije kao što je \"Ok Google\"\n\n"<annotation id="url">"Saznajte više"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Radi produženja vijeka trajanja baterije, Ušteda baterije:\n\n•Uključuje Tamnu temu\n•Isključuje ili ograničava aktivnosti u pozadini, određene vizuelne efekte i druge funkcije kao što je \"Ok Google\""</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Radi produženja vijeka trajanja baterije, Ušteda baterije:\n\n• Uključuje Tamnu temu\n• Isključuje ili ograničava aktivnosti u pozadini, određene vizuelne efekte i druge funkcije kao što je \"Ok Google\"\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Radi produženja vijeka trajanja baterije, Ušteda baterije:\n\n• Uključuje Tamnu temu\n• Isključuje ili ograničava aktivnost u pozadini, određene vizuelne efekte i druge funkcije kao što je \"Ok Google\""</string>
<string name="data_saver_description" msgid="4995164271550590517">"Radi smanjenja prijenosa podataka, Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali će to činiti rjeđe. Naprimjer, to može značiti da se slike ne prikazuju dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Uštedu podataka?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string>
@@ -2030,8 +2031,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"prikazivanje preko drugih aplikacija na ekranu"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Pružanje povratnih informacija"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Obavještenje za informacije Rutinskog načina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Moguće je da će se baterija isprazniti prije uobičajenog punjenja"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Ušteda baterije je aktivirana da bi se produžio vijek trajanja baterije"</string>
@@ -2065,7 +2065,7 @@
<item quantity="few"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> fajla</item>
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> fajlova</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Nijedna osoba nije preporučena za dijeljenje"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Nema preporučenih osoba za dijeljenje"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista aplikacija"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Ovoj aplikaciji nije dato odobrenje za snimanje, ali može snimati zvuk putem ovog USB uređaja."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Početna stranica"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index c94d8d1f7e05..5fadc92d2970 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1792,8 +1792,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualitzat per l\'administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Suprimit per l\'administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"D\'acord"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Per allargar la durada de la bateria, el mode Estalvi de bateria fa el següent:\n\n• Activa el tema fosc.\n• Desactiva o restringeix l\'activitat en segon pla, alguns efectes visuals i altres funcions com \"Ok Google\".\n\n"<annotation id="url">"Més informació"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Per allargar la durada de la bateria, el mode Estalvi de bateria fa el següent:\n\n• Activa el tema fosc.\n• Desactiva o restringeix l\'activitat en segon pla, alguns efectes visuals i altres funcions com \"Ok Google\"."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Per allargar la durada de la bateria, la funció Estalvi de bateria fa el següent:\n\n• Activa el tema fosc.\n• Desactiva o restringeix l\'activitat en segon pla, alguns efectes visuals i altres funcions com \"Ok Google\".\n\n"<annotation id="url">"Més informació"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Per allargar la durada de la bateria, la funció Estalvi de bateria fa el següent:\n\n• Activa el tema fosc.\n• Desactiva o restringeix l\'activitat en segon pla, alguns efectes visuals i altres funcions com \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Economitzador de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activar l\'Economitzador de dades?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activa"</string>
@@ -1997,8 +1997,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Càmera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Micròfon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"es mostra sobre altres aplicacions a la pantalla"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Envia suggeriments"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificació d\'informació del mode de rutina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"És possible que la bateria s\'esgoti abans de la càrrega habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"S\'ha activat l\'estalvi de bateria per prolongar-ne la durada"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 2685f0d62b67..c69ca37d0f1c 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Chcete-li odemknout telefon nebo provést tísňové volání, stiskněte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Telefon odemknete stisknutím tlačítka Menu."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Odblokujte pomocí gesta"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Stav nouze"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zavolat zpět"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Správně!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Zkusit znovu"</string>
@@ -1695,7 +1696,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"Převrácení barev"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Oprava barev"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> byla vypnuta."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Chcete-li používat službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tři sekundy podržte stisknutá obě tlačítka hlasitosti"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Určete, jakou funkci aktivujete klepnutím na tlačítko přístupnosti:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Určete, jakou funkci aktivujete pomocí gesta přístupnosti (přejetí dvěma prsty ze spodní části obrazovky nahoru):"</string>
@@ -1838,8 +1839,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Aktualizováno administrátorem"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Smazáno administrátorem"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Spořič baterie za účelem úspory energie:\n\n• zapne tmavý motiv,\n• vypne nebo omezí aktivitu na pozadí, některé vizuální efekty a další funkce jako „Ok Google“\n\n"<annotation id="url">"Další informace"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Spořič baterie za účelem úspory energie:\n\n• zapne tmavý motiv,\n• vypne nebo omezí aktivitu na pozadí, některé vizuální efekty a další funkce jako „Ok Google“"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Spořič baterie za účelem úspory energie:\n\n• Zapne tmavý motiv.\n• Vypne nebo omezí aktivitu na pozadí, některé vizuální efekty a další funkce jako „Ok Google“.\n\n"<annotation id="url">"Další informace"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Spořič baterie za účelem úspory energie:\n\n• Zapne tmavý motiv.\n• Vypne nebo omezí aktivitu na pozadí, některé vizuální efekty a další funkce jako „Ok Google“."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Spořič dat z důvodu snížení využití dat některým aplikacím brání v odesílání nebo příjmu dat na pozadí. Aplikace, kterou právě používáte, data přenášet může, ale může tak činit méně často. V důsledku toho se například obrázky nemusejí zobrazit, dokud na ně neklepnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Chcete zapnout Spořič dat?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Zapnout"</string>
@@ -2063,8 +2064,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Fotoaparát"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"zobrazení přes ostatní aplikace na obrazovce"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Odeslat zpětnou vazbu"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Informační oznámení režimu sledu činností"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterie se možná vybije před obvyklým časem nabití"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Byl aktivován spořič baterie za účelem prodloužení výdrže"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 360692883a10..fb100773dfa6 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tryk på Menu for at låse op eller foretage et nødopkald."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tryk på Menu for at låse op."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Tegn oplåsningsmønster"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nødsituation"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tilbage til opkald"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Rigtigt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Prøv igen"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Opdateret af din administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Slettet af din administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Batterisparefunktionen gør følgende for at spare på batteriet:\n\n•Aktiverer Mørkt tema\n•Deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og andre funktioner som f.eks. \"Hey Google\"\n\n"<annotation id="url">"Få flere oplysninger"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Batterisparefunktionen gør følgende for at spare på batteriet:\n\n•Aktiverer Mørkt tema\n•Deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og andre funktioner som f.eks. \"Hey Google\""</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Batterisparefunktionen gør følgende for at spare på batteriet:\n\n• Aktiverer Mørkt tema\n• Deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og andre funktioner som f.eks. \"Hey Google\"\n\n"<annotation id="url">"Få flere oplysninger"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Batterisparefunktionen gør følgende for at spare på batteriet:\n\n• Aktiverer Mørkt tema\n• Deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og andre funktioner som f.eks. \"Hey Google\""</string>
<string name="data_saver_description" msgid="4995164271550590517">"Datasparefunktionen forhindrer nogle apps i at sende eller modtage data i baggrunden for at reducere dataforbruget. En app, der er i brug, kan få adgang til data, men gør det måske ikke så ofte. Dette kan f.eks. betyde, at billeder ikke vises, før du trykker på dem."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vil du aktivere Datasparefunktion?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivér"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"vises over andre apps på din skærm"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Giv feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notifikation med oplysninger om rutinetilstand"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Enheden løber muligvis tør for batteri, inden du normalt oplader den"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterisparefunktion er aktiveret for at forlænge batteritiden"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 62b219eeeae0..4adcf0ae48ec 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Drücke die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Zum Entsperren die Menütaste drücken"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Muster zum Entsperren zeichnen"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Notfall"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zurück zum Anruf"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Erneut versuchen"</string>
@@ -935,7 +936,7 @@
<string name="save_password_label" msgid="9161712335355510035">"Bestätigen"</string>
<string name="double_tap_toast" msgid="7065519579174882778">"Tipp: Zum Vergrößern und Verkleinern doppeltippen"</string>
<string name="autofill_this_form" msgid="3187132440451621492">"AutoFill"</string>
- <string name="setup_autofill" msgid="5431369130866618567">"AutoFill konfig."</string>
+ <string name="setup_autofill" msgid="5431369130866618567">"Autom.Ausfüll.konf."</string>
<string name="autofill_window_title" msgid="4379134104008111961">"Mit <xliff:g id="SERVICENAME">%1$s</xliff:g> automatisch ausfüllen"</string>
<string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string>
<string name="autofill_address_summary_name_format" msgid="3402882515222673691">"$1$2$3"</string>
@@ -1104,7 +1105,7 @@
<string name="selectTextMode" msgid="3225108910999318778">"Text auswählen"</string>
<string name="undo" msgid="3175318090002654673">"Rückgängig machen"</string>
<string name="redo" msgid="7231448494008532233">"Wiederholen"</string>
- <string name="autofill" msgid="511224882647795296">"AutoFill"</string>
+ <string name="autofill" msgid="511224882647795296">"Automatisches Ausfüllen"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"Textauswahl"</string>
<string name="addToDictionary" msgid="8041821113480950096">"Zum Wörterbuch hinzufügen"</string>
<string name="deleteText" msgid="4200807474529938112">"Löschen"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string>
@@ -1928,13 +1929,13 @@
<string name="time_picker_prompt_label" msgid="303588544656363889">"Uhrzeit eingeben"</string>
<string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"In den Texteingabemodus wechseln, um die Uhrzeit einzugeben."</string>
<string name="time_picker_radial_mode_description" msgid="1222342577115016953">"In den Uhrzeitmodus wechseln, um die Uhrzeit einzugeben."</string>
- <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"AutoFill-Optionen"</string>
- <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Für AutoFill speichern"</string>
+ <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Optionen für automatisches Ausfüllen"</string>
+ <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Für \"Automatisches Ausfüllen\" speichern"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Inhalte können nicht automatisch ausgefüllt werden"</string>
- <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Keine AutoFill-Vorschläge"</string>
+ <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Keine Vorschläge für automatisches Ausfüllen"</string>
<plurals name="autofill_picker_some_suggestions" formatted="false" msgid="6651883186966959978">
- <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> AutoFill-Vorschläge</item>
- <item quantity="one">1 AutoFill-Vorschlag</item>
+ <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> Vorschläge für automatisches Ausfüllen</item>
+ <item quantity="one">1 Vorschlag für automatisches Ausfüllen</item>
</plurals>
<string name="autofill_save_title" msgid="7719802414283739775">"In "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" speichern?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"<xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" speichern?"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"wird über anderen Apps auf dem Bildschirm angezeigt"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Feedback geben"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Infomitteilung zum Ablaufmodus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index cfc76b0f85f3..23895f4fbf57 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Πατήστε \"Menu\" για ξεκλείδωμα ή για κλήση έκτακτης ανάγκης."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Σχεδιασμός μοτίβου για ξεκλείδωμα"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Κλήση έκτακτης ανάγκης"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Επιστροφή στην κλήση"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Σωστό!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Προσπαθήστε ξανά"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ενημερώθηκε από τον διαχειριστή σας"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Διαγράφηκε από τον διαχειριστή σας"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Για την επέκταση της διάρκειας ζωής της μπαταρίας σας, η Εξοικονόμηση μπαταρίας:\n\n•Ενεργοποιεί το Σκούρο θέμα\n•Απενεργοποιεί ή περιορίζει τη δραστηριότητα παρασκηνίου, ορισμένα οπτικά εφέ και άλλες λειτουργίες όπως την εντολή Hey Google\n\n"<annotation id="url">"Μάθετε περισσότερα"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Για την επέκταση της διάρκειας ζωής της μπαταρίας, η Εξοικονόμηση μπαταρίας:\n\n•Ενεργοποιεί το Σκούρο θέμα\n•Απενεργοποιεί ή περιορίζει τη δραστηριότητα παρασκηνίου, ορισμένα οπτικά εφέ και άλλες λειτουργίες όπως την εντολή Hey Google."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Για την επέκταση της διάρκειας ζωής της μπαταρίας σας, η Εξοικονόμηση μπαταρίας:\n\n• Ενεργοποιεί το Σκούρο θέμα\n• Απενεργοποιεί ή περιορίζει τη δραστηριότητα παρασκηνίου, ορισμένα οπτικά εφέ και άλλες λειτουργίες όπως την εντολή Hey Google\n\n"<annotation id="url">"Μάθετε περισσότερα"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Για την επέκταση της διάρκειας ζωής της μπαταρίας σας, η Εξοικονόμηση μπαταρίας:\n\n• Ενεργοποιεί το Σκούρο θέμα\n• Απενεργοποιεί ή περιορίζει τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ και άλλες λειτουργίες, όπως την εντολή Hey Google"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Προκειμένου να μειωθεί η χρήση δεδομένων, η Εξοικονόμηση δεδομένων αποτρέπει την αποστολή ή λήψη δεδομένων από ορισμένες εφαρμογές στο παρασκήνιο. Μια εφαρμογή που χρησιμοποιείτε αυτήν τη στιγμή μπορεί να χρησιμοποιήσει δεδομένα αλλά με μικρότερη συχνότητα. Για παράδειγμα, οι εικόνες μπορεί να μην εμφανίζονται μέχρι να τις πατήσετε."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ενεργ.Εξοικονόμησης δεδομένων;"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ενεργοποίηση"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Κάμερα"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Μικρόφωνο"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"εμφανίζεται πάνω σε άλλες εφαρμογές στην οθόνη σας"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Υποβολή σχολίων"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ειδοποίηση πληροφοριών λειτουργίας Ρουτίνας"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Η μπαταρία μπορεί να εξαντληθεί πριν από τη συνηθισμένη φόρτιση"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Η Εξοικονόμηση μπαταρίας ενεργοποιήθηκε για την επέκταση της διάρκειας ζωής της μπαταρίας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 7f1f888fcd10..0484ccd8abaa 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -318,7 +318,7 @@
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Body sensors"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"access sensor data about your vital signs"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Retrieve window content"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspect the content of a window that you\'re interacting with."</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspect the content of a window you\'re interacting with."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Turn on Explore by Touch"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Tapped items will be spoken aloud and the screen can be explored using gestures."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Observe text that you type"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
@@ -1792,9 +1793,9 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'"</string>
- <string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like “Hey Google”\n\n"<annotation id="url">"Learn more"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like “Hey Google”"</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you\'re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273">
@@ -1912,7 +1913,7 @@
<string name="conference_call" msgid="5731633152336490471">"Conference Call"</string>
<string name="tooltip_popup_title" msgid="7863719020269945722">"Tooltip"</string>
<string name="app_category_game" msgid="4534216074910244790">"Games"</string>
- <string name="app_category_audio" msgid="8296029904794676222">"Music &amp; Audio"</string>
+ <string name="app_category_audio" msgid="8296029904794676222">"Music and audio"</string>
<string name="app_category_video" msgid="2590183854839565814">"Movies &amp; Video"</string>
<string name="app_category_image" msgid="7307840291864213007">"Photos &amp; Images"</string>
<string name="app_category_social" msgid="2278269325488344054">"Social &amp; Communication"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Camera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microphone"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"displaying over other apps on your screen"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Provide feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Routine Mode info notification"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery may run out before usual charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Battery Saver activated to extend battery life"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index f2cce1477fd2..9981c1212123 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like “Hey Google”\n\n"<annotation id="url">"Learn more"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like “Hey Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you\'re currently using can access data, but may do so less frequently. This may mean, for example, that images don\'t display until you tap them."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string>
@@ -1912,7 +1913,7 @@
<string name="conference_call" msgid="5731633152336490471">"Conference Call"</string>
<string name="tooltip_popup_title" msgid="7863719020269945722">"Tooltip"</string>
<string name="app_category_game" msgid="4534216074910244790">"Games"</string>
- <string name="app_category_audio" msgid="8296029904794676222">"Music &amp; Audio"</string>
+ <string name="app_category_audio" msgid="8296029904794676222">"Music and audio"</string>
<string name="app_category_video" msgid="2590183854839565814">"Movies &amp; Video"</string>
<string name="app_category_image" msgid="7307840291864213007">"Photos &amp; Images"</string>
<string name="app_category_social" msgid="2278269325488344054">"Social &amp; Communication"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Camera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microphone"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"displaying over other apps on your screen"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Provide feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Routine Mode info notification"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery may run out before usual charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Battery Saver activated to extend battery life"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7f1f888fcd10..02cdf69a2cd8 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like “Hey Google”\n\n"<annotation id="url">"Learn more"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like “Hey Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Camera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microphone"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"displaying over other apps on your screen"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Provide feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Routine Mode info notification"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery may run out before usual charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Battery Saver activated to extend battery life"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7f1f888fcd10..3b18aca951f7 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -318,7 +318,7 @@
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Body sensors"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"access sensor data about your vital signs"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Retrieve window content"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspect the content of a window that you\'re interacting with."</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspect the content of a window you\'re interacting with."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Turn on Explore by Touch"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Tapped items will be spoken aloud and the screen can be explored using gestures."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Observe text that you type"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
@@ -1009,7 +1010,7 @@
<string name="hour" msgid="7796325297097314653">"hour"</string>
<string name="hours" msgid="8517014849629200683">"hours"</string>
<string name="minute" msgid="8369209540986467610">"min"</string>
- <string name="minutes" msgid="3456532942641808971">"mins"</string>
+ <string name="minutes" msgid="3456532942641808971">"Min."</string>
<string name="second" msgid="9210875257112211713">"sec"</string>
<string name="seconds" msgid="2175052687727971048">"secs"</string>
<string name="week" msgid="907127093960923779">"week"</string>
@@ -1649,7 +1650,7 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Turn off Shortcut"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
- <string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+ <string name="color_correction_feature_name" msgid="3655077237805422597">"Color correction"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like “Hey Google”\n\n"<annotation id="url">"Learn more"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like “Hey Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string>
@@ -1912,7 +1913,7 @@
<string name="conference_call" msgid="5731633152336490471">"Conference Call"</string>
<string name="tooltip_popup_title" msgid="7863719020269945722">"Tooltip"</string>
<string name="app_category_game" msgid="4534216074910244790">"Games"</string>
- <string name="app_category_audio" msgid="8296029904794676222">"Music &amp; Audio"</string>
+ <string name="app_category_audio" msgid="8296029904794676222">"Music and audio"</string>
<string name="app_category_video" msgid="2590183854839565814">"Movies &amp; Video"</string>
<string name="app_category_image" msgid="7307840291864213007">"Photos &amp; Images"</string>
<string name="app_category_social" msgid="2278269325488344054">"Social &amp; Communication"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Camera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microphone"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"displaying over other apps on your screen"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Provide feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Routine Mode info notification"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery may run out before usual charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Battery Saver activated to extend battery life"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index d9342027ffa1..6503f7ce65a7 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -832,7 +832,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‏‎‎‎Press Menu to unlock or place emergency call.‎‏‎‎‏‎"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎Press Menu to unlock.‎‏‎‎‏‎"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎Draw pattern to unlock‎‏‎‎‏‎"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‏‎Emergency‎‏‎‎‏‎"</string>
+ <string name="lockscreen_emergency_call" msgid="7549683825868928636">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎Emergency call‎‏‎‎‏‎"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎Return to call‎‏‎‎‏‎"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‎Correct!‎‏‎‎‏‎"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎Try again‎‏‎‎‏‎"</string>
@@ -1792,8 +1792,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎Updated by your admin‎‏‎‎‏‎"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎Deleted by your admin‎‏‎‎‏‎"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎OK‎‏‎‎‏‎"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‎To extend battery life, Battery Saver:‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎•Turns on Dark theme‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎•Turns off or restricts background activity, some visual effects, and other features like “Hey Google”‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<annotation id="url">"‎‏‎‎‏‏‏‎Learn more‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="battery_saver_description" msgid="8587408568232177204">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎To extend battery life, Battery Saver:‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎•Turns on Dark theme‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎•Turns off or restricts background activity, some visual effects, and other features like “Hey Google”‎‏‎‎‏‎"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎To extend battery life, Battery Saver:‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎• Turns on Dark theme‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎• Turns off or restricts background activity, some visual effects, and other features like “Hey Google”‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<annotation id="url">"‎‏‎‎‏‏‏‎Learn more‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‎‎To extend battery life, Battery Saver:‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎• Turns on Dark theme‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎• Turns off or restricts background activity, some visual effects, and other features like “Hey Google”‎‏‎‎‏‎"</string>
<string name="data_saver_description" msgid="4995164271550590517">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.‎‏‎‎‏‎"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‎Turn on Data Saver?‎‏‎‎‏‎"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‎Turn on‎‏‎‎‏‎"</string>
@@ -1997,8 +1997,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎Camera‎‏‎‎‏‎"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‏‎‎‎Microphone‎‏‎‎‏‎"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎displaying over other apps on your screen‎‏‎‎‏‎"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎Provide Feedback‎‏‎‎‏‎"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‎‎‏‎‏‎‏‏‎‎‎‎‎‎Routine Mode info notification‎‏‎‎‏‎"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‎Battery may run out before usual charge‎‏‎‎‏‎"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‎Battery Saver activated to extend battery life‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 37cc9d6f663a..7e8a89aa5a94 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Presiona el Menú para desbloquear o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Presionar Menú para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dibujar el patrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergencia"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Regresar a llamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Vuelve a intentarlo."</string>
@@ -1623,7 +1624,7 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Usar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Cuando la combinación de teclas está activada, puedes presionar los botones de volumen durante 3 segundos para iniciar una función de accesibilidad."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"¿Quieres activar las funciones de accesibilidad?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si mantienes presionadas ambas teclas de volumen durante unos segundos, se activarán las funciones de accesibilidad. Esto podría cambiar la manera en la que funciona tu dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Configuración &gt; Accesibilidad."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si mantienes presionadas las dos teclas de volumen durante unos segundos, se activarán las funciones de accesibilidad. Esto puede cambiar el funcionamiento de tu dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Configuración &gt; Accesibilidad."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"¿Quieres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si mantienes presionadas ambas teclas de volumen durante unos segundos, se activará la función de accesibilidad <xliff:g id="SERVICE">%1$s</xliff:g>. Esto podría cambiar la forma en que funciona tu dispositivo.\n\nPuedes cambiar este acceso directo a otra función en Configuración &gt; Accesibilidad."</string>
@@ -1651,7 +1652,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Como mantuviste presionadas las teclas de volumen, se activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Al mantener presionadas las teclas de volumen, se desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se presionaron las teclas de volumen. Se desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén presionadas ambas teclas de volumen durante tres segundos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Elige una función para usar cuando pulses el botón accesibilidad:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Elige la función que se usará cuando realices el gesto de accesibilidad (deslizar dos dedos hacia arriba desde la parte inferior de la pantalla):"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Tu administrador actualizó este paquete"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Tu administrador borró este paquete"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Para extender la duración de la batería, el Ahorro de batería hace lo siguiente:\n\n•Activa el Tema oscuro.\n•Desactiva o restringe la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Hey Google\".\n\n"<annotation id="url">"Más información"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Para extender la duración de batería, el Ahorro de batería hace lo siguiente:\n\n•Activa el Tema oscuro.\n•Desactiva o restringe la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Hey Google\"."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Para extender la duración de batería, el Ahorro de batería hace lo siguiente:\n\n• Activa el Tema oscuro.\n• Desactiva o restringe la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Hey Google\".\n\n"<annotation id="url">"Más información"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Para extender la duración de batería, el Ahorro de batería hace lo siguiente:\n\n• Activa el Tema oscuro.\n• Desactiva o restringe la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Hey Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para reducir el uso de datos, el modo Ahorro de datos evita que algunas apps envíen y reciban datos en segundo plano. La app que estés usando podrá acceder a los datos, pero con menor frecuencia. De esta forma, por ejemplo, las imágenes no se mostrarán hasta que las presiones."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"¿Deseas activar Ahorro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Cámara"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Micrófono"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"se superpone a otras apps en tu pantalla"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Enviar comentarios"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificación de información del modo de Rutinas"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Es posible que la batería se agote antes de la carga habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Se activó el Ahorro de batería para extender la duración de la batería"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 784db5e86dc3..02aafa75945a 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pulsa la tecla de menú para desbloquear el teléfono o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pulsa la tecla de menú para desbloquear la pantalla."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dibujar patrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergencia"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Volver a llamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Vuelve a intentarlo"</string>
@@ -1152,7 +1153,7 @@
<string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Capturar imagen con %1$s"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Capturar imagen"</string>
<string name="alwaysUse" msgid="3153558199076112903">"Usar siempre para esta acción"</string>
- <string name="use_a_different_app" msgid="4987790276170972776">"Utiliza otra aplicación"</string>
+ <string name="use_a_different_app" msgid="4987790276170972776">"Usar otra aplicación"</string>
<string name="clearDefaultHintMsg" msgid="1325866337702524936">"Para borrar los valores predeterminados, accede a Ajustes del sistema &gt; Aplicaciones &gt; Descargadas."</string>
<string name="chooseActivity" msgid="8563390197659779956">"Selecciona una acción"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"Elegir una aplicación para el dispositivo USB"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado por el administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado por el administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Para que la batería dure más, el modo Ahorro de batería hace lo siguiente:\n\n• Activa el tema oscuro\n•Desactiva o restringe actividad en segundo plano, algunos efectos visuales y otras funciones como \"Ok Google\"\n\n"<annotation id="url">"Más información"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Para que la batería dure más, el modo Ahorro de batería hace lo siguiente:\n\n• Activa el tema oscuro\n• Desactiva o restringe actividad en segundo plano, algunos efectos visuales y otras funciones como \"Ok Google\""</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Para que la batería dure más, Ahorro de batería hace lo siguiente:\n\n• Activa el tema oscuro.\n• Desactiva o restringe la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Ok Google\".\n\n"<annotation id="url">"Más información"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Para que la batería dure más, Ahorro de batería hace lo siguiente:\n\n• Activa el tema oscuro.\n• Desactiva o restringe la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"El modo Ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano, lo que puede reducir el uso de datos. Una aplicación activa puede acceder a los datos, aunque con menos frecuencia. Esto significa que es posible que, por ejemplo, algunas imágenes no se muestren hasta que las toques."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"¿Activar Ahorro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Cámara"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Micrófono"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"se muestra sobre otras aplicaciones que haya en la pantalla"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Enviar comentarios"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificación sobre el modo rutina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Quizás se agote la batería antes de lo habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Se ha activado el modo Ahorro de batería para aumentar la duración de la batería"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 446cda26e223..813629417a8e 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Vajutage avamiseks või hädaabikõne tegemiseks menüünuppu"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Vajutage avamiseks menüüklahvi."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Avamiseks joonistage muster"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hädaabi"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kõne juurde tagasi"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Õige."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Proovige uuesti"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Administraator on seda värskendanud"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administraator on selle kustutanud"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Aku tööea pikendamiseks teeb akusäästja järgmist.\n\n• Lülitab sisse tumeda teema.\n• Lülitab välja taustategevused, mõned visuaalsed efektid ja muud funktsioonid (nt „Ok Google”) või piirab neid.\n\n"<annotation id="url">"Lisateave"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Aku tööea pikendamiseks teeb akusäästja järgmist.\n\n• Lülitab sisse tumeda teema.\n• Lülitab välja taustategevused, mõned visuaalsed efektid ja muud funktsioonid (nt „Ok Google”) või piirab neid."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Aku tööea pikendamiseks teeb akusäästja järgmist.\n\n• Lülitab sisse tumeda teema.\n• Lülitab välja taustategevused, mõned visuaalsed efektid ja muud funktsioonid (nt „Ok Google”) või piirab neid.\n\n"<annotation id="url">"Lisateave"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Aku tööea pikendamiseks teeb akusäästja järgmist.\n\n• Lülitab sisse tumeda teema.\n• Lülitab välja taustategevused, mõned visuaalsed efektid ja muud funktsioonid (nt „Ok Google”) või piirab neid."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Andmekasutuse vähendamiseks keelab andmemahu säästja mõne rakenduse puhul andmete taustal saatmise ja vastuvõtmise. Rakendus, mida praegu kasutate, pääseb andmesidele juurde, kuid võib seda teha väiksema sagedusega. Seetõttu võidakse näiteks pildid kuvada alles siis, kui neid puudutate."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Lülitada andmemahu säästja sisse?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Lülita sisse"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kaamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"teie ekraanil muude rakenduste peal kuvamine"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Andke tagasisidet"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Rutiinirežiimi teabe märguanne"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Aku võib enne tavapärast laadimist tühjaks saada"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Akusäästja aktiveeriti aku tööea pikendamiseks"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index fcca5bb3b0a4..1b88f2708782 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -424,9 +424,9 @@
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"atzitu kokapen-hornitzaileen komando gehigarriak"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Kokapen-hornitzailearen agindu gehigarriak atzitzeko baimena ematen die aplikazioei. Horrela, agian aplikazioek GPSaren edo bestelako kokapenaren iturburuen funtzionamenduan eragina izan dezakete."</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"lortu kokapen zehatza aurreko planoan bakarrik"</string>
- <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Erabiltzen ari zarenean, aplikazioak kokapen zehatza lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan. Bateria-erabilera areagotzen du horrek."</string>
+ <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Abian denean, aplikazioak kokapen zehatza lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan. Bateria-erabilera areagotzen du horrek."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"atzitu gutxi gorabeherako kokapena aurreko planoan bakarrik"</string>
- <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Erabiltzen ari zarenean, aplikazioak gutxi gorabeherako kokapena lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan."</string>
+ <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Abian denean, aplikazioak gutxi gorabeherako kokapena lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan."</string>
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"atzitu kokapena atzeko planoan"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Aplikazioak kokapena atzi dezake, baita aplikazioa erabiltzen ari ez zarenean ere."</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"aldatu audio-ezarpenak"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Desblokeatzeko edo larrialdi-deia egiteko, sakatu Menua."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Desblokeatzeko, sakatu Menua."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desblokeatzeko, marraztu eredua"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Larrialdi-deiak"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Itzuli deira"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Eredua zuzena da!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Saiatu berriro"</string>
@@ -846,7 +847,7 @@
<string name="lockscreen_missing_sim_instructions" msgid="8473601862688263903">"Sartu SIM txartela."</string>
<string name="lockscreen_missing_sim_instructions_long" msgid="3664999892038416334">"SIM txartela falta da edo ezin da irakurri. Sartu SIM txartel bat."</string>
<string name="lockscreen_permanent_disabled_sim_message_short" msgid="3812893366715730539">"SIM txartela hondatuta dago."</string>
- <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"SIM txartela behin betiko desgaitu zaizu.\n Beste SIM txartel bat lortzeko, jarri zerbitzu-hornitzailearekin harremanetan."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"SIM txartela betiko desgaitu zaizu.\n Beste SIM txartel bat lortzeko, jarri zerbitzu-hornitzailearekin harremanetan."</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Aurreko pista"</string>
<string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Hurrengo pista"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pausatu"</string>
@@ -1310,7 +1311,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Audio-osagarri analogiko bat hauteman da"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Erantsitako gailua ez da telefono honekin bateragarria. Sakatu informazio gehiago lortzeko."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"USB bidezko arazketa konektatuta"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Sakatu USB bidezko arazketa desaktibatzeko"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Sakatu hau USB bidezko arazketa desaktibatzeko"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Hautatu USB bidezko arazketa desgaitzeko."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Hari gabeko arazketa konektatuta dago"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Sakatu hau hari gabeko arazketa desaktibatzeko"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Administratzaileak eguneratu du"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratzaileak ezabatu du"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Ados"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Bateriaren iraupena luzatzeko, erabili bateria-aurrezlea:\n\n• Gai iluna aktibatzen du.\n• Desaktibatu edo murriztu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual eta beste eginbide batzuk, hala nola \"Ok Google\".\n\n"<annotation id="url">"Lortu informazio gehiago"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Bateriaren iraupena luzatzeko, erabili bateria-aurrezlea:\n\n• Gai iluna aktibatzen du.\n• Desaktibatu edo murriztu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual eta beste eginbide batzuk, hala nola \"Ok Google\"."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Bateriaren iraupena luzatzeko, erabili bateria-aurrezlea:\n\n• Gai iluna aktibatzen du.\n• Desaktibatu edo murriztu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual eta beste eginbide batzuk, hala nola \"Ok Google\".\n\n"<annotation id="url">"Lortu informazio gehiago"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Bateriaren iraupena luzatzeko, erabili bateria-aurrezlea:\n\n• Gai iluna aktibatzen du.\n• Atzeko planoko jarduerak, zenbait efektu bisual eta beste eginbide batzuk desaktibatzen edo murrizten ditu, hala nola \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Datuen erabilera murrizteko, atzeko planoan datuak bidaltzea eta jasotzea galarazten die datu-aurrezleak aplikazio batzuei. Une honetan erabiltzen ari zaren aplikazioak atzitu egin ahal izango ditu datuak, baina baliteke maiztasun txikiagoarekin atzitzea. Horrela, adibidez, baliteke irudiak ez erakustea haiek sakatu arte."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Datu-aurrezlea aktibatu nahi duzu?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktibatu"</string>
@@ -1926,7 +1927,7 @@
<string name="time_picker_header_text" msgid="9073802285051516688">"Ezarri ordua"</string>
<string name="time_picker_input_error" msgid="8386271930742451034">"Idatzi balio duen ordu bat"</string>
<string name="time_picker_prompt_label" msgid="303588544656363889">"Idatzi ordua"</string>
- <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Aldatu testu modura ordua zehazteko."</string>
+ <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Ordua idazteko, aldatu testua idazteko modura."</string>
<string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Aldatu erloju modura ordua zehazteko."</string>
<string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Betetze automatikoaren aukerak"</string>
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Gorde betetze automatikoarekin erabiltzeko"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofonoa"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"pantailako beste aplikazioen gainean bistaratzen"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Eman iritzia"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ohitura moduaren informazio-jakinarazpena"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baliteke bateria ohi baino lehenago agortzea"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Bateria-aurrezlea aktibatuta dago bateriaren iraupena luzatzeko"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index f28bf9e5d222..506a7ee79dcb 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -516,8 +516,8 @@
<string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"‏به برنامه اجازه می‎دهد تا پیکربندی بلوتوث در رایانهٔ لوحی را مشاهده کند و اتصال با دستگاه‌های مرتبط را برقرار کرده و بپذیرد."</string>
<string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"‏به برنامه اجازه می‌دهد پیکربندی بلوتوث را در دستگاه Android TV شما ببیند، و اتصالات با دستگاه‌های مرتبط‌شده را بپذیرد یا این اتصالات را برقرار کند."</string>
<string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"‏به برنامه اجازه می‎دهد تا پیکربندی بلوتوث در تلفن را مشاهده کند، و اتصالات دستگاه‌های مرتبط را برقرار کرده و بپذیرد."</string>
- <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏اطلاعات ترجیحی سرویس پولی NFC"</string>
- <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏به برنامه اجازه می‌دهد اطلاعات ترجیحی «سرویس پولی NFC»، مانند کمک‌های ثبت‌شده و مقصد مسیر را دریافت کند."</string>
+ <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏اطلاعات ترجیحی سرویس پولی «ارتباط میدان نزدیک» (NFC)"</string>
+ <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏به برنامه اجازه می‌دهد اطلاعات ترجیحی سرویس پولی «ارتباط میدان نزدیک» (NFC)، مانند کمک‌های ثبت‌شده و مقصد مسیر را دریافت کند."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"کنترل ارتباط راه نزدیک"</string>
<string name="permdesc_nfc" msgid="8352737680695296741">"‏به برنامه اجازه می‎دهد تا با تگ‌های «ارتباط میدان نزدیک» (NFC)، کارت‌ها و فایل‌خوان ارتباط برقرار کند."</string>
<string name="permlab_disableKeyguard" msgid="3605253559020928505">"غیرفعال کردن قفل صفحه شما"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"برای بازگشایی قفل یا انجام تماس اضطراری روی منو فشار دهید."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"برای بازگشایی قفل روی منو فشار دهید."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"الگو را بکشید تا قفل آن باز شود"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"اضطراری"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"بازگشت به تماس"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحیح است!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوباره امتحان کنید"</string>
@@ -1125,8 +1126,8 @@
<string name="capital_off" msgid="7443704171014626777">"خاموش"</string>
<string name="checked" msgid="9179896827054513119">"علامت‌زده‌شده"</string>
<string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
- <string name="whichApplication" msgid="5432266899591255759">"تکمیل عملکرد با استفاده از"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"‏تکمیل عملکرد با استفاده از %1$s"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"‏تکمیل کنش بااستفاده از %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"تکمیل عملکرد"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"باز کردن با"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"‏باز کردن با %1$s"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"توسط سرپرست سیستم به‌روزرسانی شد"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"توسط سرپرست سیستم حذف شد"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"تأیید"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"‏برای افزایش عمر باتری، «بهینه‌سازی باتری»:\n\n•«طرح زمینه تیره» را روشن می‌کند\n•فعالیت پس‌زمینه، برخی جلوه‌های بصری، و دیگر ویژگی‌ها مانند «Ok Google» را خاموش یا محدود می‌کند\n\n"<annotation id="url">"بیشتر بدانید"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"‏برای افزایش عمر باتری، «بهینه‌سازی باتری»:\n\n•«طرح زمینه تیره» را روشن می‌کند\n•فعالیت پس‌زمینه، برخی جلوه‌های بصری، و دیگر ویژگی‌ها مانند «Ok Google» را خاموش یا محدود می‌کند"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"‏برای افزایش عمر باتری، «بهینه‌سازی باتری»:\n\n• «طرح زمینه تیره» را روشن می‌کند\n• فعالیت پس‌زمینه، برخی جلوه‌های بصری، و دیگر ویژگی‌ها مانند «Ok Google» را خاموش یا محدود می‌کند\n\n"<annotation id="url">"بیشتر بدانید"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"‏برای افزایش عمر باتری، «بهینه‌سازی باتری»:\n\n• «طرح زمینه تیره» را روشن می‌کند\n• فعالیت پس‌زمینه، برخی جلوه‌های بصری، و دیگر ویژگی‌ها مثل «Ok Google» را خاموش یا محدود می‌کند"</string>
<string name="data_saver_description" msgid="4995164271550590517">"«صرفه‌جویی داده»، برای کمک به کاهش مصرف داده، از ارسال و دریافت داده در پس‌زمینه ازطرف بعضی برنامه‌ها جلوگیری می‌کند. برنامه‌ای که درحال‌حاضر استفاده می‌کنید می‌تواند به داده‌ها دسترسی داشته باشد اما دفعات دسترسی آن محدود است.این یعنی، برای مثال، تصاویر تازمانی‌که روی آن‌ها ضربه نزنید نشان داده نمی‌شوند."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"«صرفه‌جویی داده» روشن شود؟"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"روشن کردن"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"دوربین"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"میکروفون"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"نمایش روی برنامه‌های دیگر در صفحه‌نمایش"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ارائه بازخورد"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"اعلان اطلاعات حالت روال معمول"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ممکن است شارژ باتری قبل از شارژ معمول تمام شود"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"جهت افزایش عمر باتری، «بهینه‌سازی باتری» فعال شد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0dea7fa2b3a5..01db781a6ccd 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Poista lukitus tai soita hätäpuhelu painamalla Valikko-painiketta."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Poista lukitus painamalla Valikko-painiketta."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Poista lukitus piirtämällä kuvio"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hätäpuhelu"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Palaa puheluun"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Oikein!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Yritä uudelleen"</string>
@@ -1159,8 +1160,8 @@
<string name="noApplications" msgid="1186909265235544019">"Yksikään sovellus ei voi suorittaa tätä toimintoa."</string>
<string name="aerr_application" msgid="4090916809370389109">"<xliff:g id="APPLICATION">%1$s</xliff:g> pysähtyi."</string>
<string name="aerr_process" msgid="4268018696970966407">"<xliff:g id="PROCESS">%1$s</xliff:g> pysähtyi."</string>
- <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> pysähtyy toistuvasti."</string>
- <string name="aerr_process_repeated" msgid="1153152413537954974">"<xliff:g id="PROCESS">%1$s</xliff:g> pysähtyy toistuvasti."</string>
+ <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> pysähtyy toistuvasti"</string>
+ <string name="aerr_process_repeated" msgid="1153152413537954974">"<xliff:g id="PROCESS">%1$s</xliff:g> pysähtyy toistuvasti"</string>
<string name="aerr_restart" msgid="2789618625210505419">"Avaa sovellus uudelleen"</string>
<string name="aerr_report" msgid="3095644466849299308">"Lähetä palautetta"</string>
<string name="aerr_close" msgid="3398336821267021852">"Sulje"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Järjestelmänvalvoja päivitti tämän."</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Järjestelmänvalvoja poisti tämän."</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Parantaakseen akunkestoa Virransäästö\n\n•·laittaa tumman teeman päälle\n•·laittaa pois päältä tai rajoittaa taustatoimintoja, joitakin visuaalisia tehosteita ja muita ominaisuuksia (esim. Hei Google).\n\n"<annotation id="url">"Lue lisää"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Parantaakseen akunkestoa Virransäästö\n\n•·laittaa tumman teeman päälle\n•·laittaa pois päältä tai rajoittaa taustatoimintoja, joitakin visuaalisia tehosteita ja muita ominaisuuksia (esim. Hei Google)."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Parantaakseen akunkestoa Virransäästö\n\n•·laittaa tumman teeman päälle\n•·laittaa pois päältä tai rajoittaa taustatoimintoja, joitakin visuaalisia tehosteita ja muita ominaisuuksia (esim. Ok Google).\n\n"<annotation id="url">"Lue lisää"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Parantaakseen akunkestoa Virransäästö\n\n•·laittaa tumman teeman päälle\n•·laittaa pois päältä tai rajoittaa taustatoimintoja, joitakin visuaalisia tehosteita ja muita ominaisuuksia (esim. Ok Google)."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Data Saver estää joitakin sovelluksia lähettämästä tai vastaanottamasta tietoja taustalla, jotta datan käyttöä voidaan vähentää. Käytössäsi oleva sovellus voi yhä käyttää dataa, mutta se saattaa tehdä niin tavallista harvemmin. Tämä voi tarkoittaa esimerkiksi sitä, että kuva ladataan vasta, kun kosketat sitä."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Otetaanko Data Saver käyttöön?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ota käyttöön"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofoni"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"näkyy näytöllä muiden sovellusten päällä"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Anna palautetta"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ohjelmatilan tietoilmoitus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Akku saattaa loppua ennen normaalia latausaikaa"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Virransäästö otettu käyttöön akunkeston pidentämiseksi"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 341a2b84153a..f5adc779e082 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Appuyez sur \"Menu\" pour débloquer le téléphone ou appeler un numéro d\'urgence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dessinez un schéma pour déverrouiller le téléphone"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgence"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retour à l\'appel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"C\'est exact!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Réessayer"</string>
@@ -1651,7 +1652,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"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="8343213623338605305">"Choisissez une fonctionnalité à utiliser lorsque vous touchez le bouton d\'accessibilité :"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Choisissez une fonctionnalité à utiliser lorsque vous utilisez le geste d\'accessibilité (balayer l\'écran de bas en haut avec deux doigts) :"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Mise à jour par votre administrateur"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Pour prolonger l\'autonomie de la pile, l\'économiseur de pile effectue les actions suivantes :\n\n•·Active le thème sombre\n•·Désactive ou limite l\'activité en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme « Ok Google »\n\n"<annotation id="url">"En savoir plus"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Pour prolonger l\'autonomie de la pile, l\'économiseur de pile effectue les actions suivantes :\n\n•·Active le thème sombre\n•·Désactive ou limite l\'activité en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme « Ok Google »"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Pour prolonger l\'autonomie de la pile, l\'économiseur de pile effectue les actions suivantes :\n\n•·Active le thème sombre\n•·Désactive ou limite l\'activité en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme « Ok Google »\n\n"<annotation id="url">"En savoir plus"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Pour prolonger l\'autonomie de la pile, l\'économiseur de pile effectue les actions suivantes :\n\n•·Active le thème sombre\n•·Désactive ou limite l\'activité en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme « Ok Google »"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Pour aider à diminuer l\'utilisation des données, la fonctionnalité Économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Une application que vous utilisez actuellement peut accéder à des données, mais peut le faire moins souvent. Cela peut signifier, par exemple, que les images ne s\'affichent pas jusqu\'à ce que vous les touchiez."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Appareil photo"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microphone"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"superpose du contenu par-dessus d\'autres applications à l\'écran"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Envoyer des commentaires"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification d\'information du mode Routine"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"La pile pourrait s\'épuiser avant la charge habituelle"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Le mode Économiseur de pile est activé afin de prolonger l\'autonomie"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 8adb72032e1d..8880dc63428f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -202,9 +202,9 @@
<string name="factory_reset_warning" msgid="6858705527798047809">"Les données de votre appareil vont être effacées"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Impossible d\'utiliser l\'application d\'administration. Les données de votre appareil vont maintenant être effacées.\n\nSi vous avez des questions, contactez l\'administrateur de votre organisation."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Impression désactivée par <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
- <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Activez profil professionnel"</string>
- <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Vos applications personnelles sont bloquées jusqu\'à ce que vous activiez votre profil professionnel"</string>
- <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Les applications personnelles seront bloquées le <xliff:g id="DATE">%1$s</xliff:g> à <xliff:g id="TIME">%2$s</xliff:g>. Votre administrateur informatique ne vous autorise pas à désactiver votre profil professionnel pendant plus de <xliff:g id="NUMBER">%3$d</xliff:g> jours."</string>
+ <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Activez votre profil pro"</string>
+ <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Vos applications personnelles seront bloquées jusqu\'à ce que vous activiez votre profil professionnel"</string>
+ <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Vos applications personnelles seront bloquées le <xliff:g id="DATE">%1$s</xliff:g> à <xliff:g id="TIME">%2$s</xliff:g>. Votre administrateur informatique ne vous autorise pas à désactiver votre profil professionnel pendant plus de <xliff:g id="NUMBER">%3$d</xliff:g> jours."</string>
<string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Activer"</string>
<string name="me" msgid="6207584824693813140">"Moi"</string>
<string name="power_dialog" product="tablet" msgid="8333207765671417261">"Options de la tablette"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Appuyez sur \"Menu\" pour déverrouiller le téléphone ou appeler un numéro d\'urgence"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Appuyez sur \"Menu\" pour déverrouiller le téléphone."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dessinez un schéma pour déverrouiller le téléphone"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgences"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retour à l\'appel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Combinaison correcte !"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Veuillez réessayer."</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Mis à jour par votre administrateur"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Pour prolonger l\'autonomie de la batterie, l\'économiseur de batterie :\n\n·• active le thème sombre\n·• désactive ou restreint les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme \"Ok Google\"\n\n"<annotation id="url">"En savoir plus"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Pour prolonger l\'autonomie de la batterie, l\'économiseur de batterie :\n\n·• active le thème sombre\n • désactive ou restreint les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme \"Ok Google\""</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Pour prolonger l\'autonomie de la batterie, l\'économiseur de batterie :\n\n·• active le thème sombre ;\n·• désactive ou restreint les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme \"Hey Google\".\n\n"<annotation id="url">"En savoir plus"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Pour prolonger l\'autonomie de la batterie, l\'économiseur de batterie :\n\n• active le thème sombre ;\n • désactive ou restreint les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités, comme \"Hey Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation de données, l\'économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Ainsi, les applications que vous utilisez peuvent toujours accéder aux données, mais pas en permanence. Par exemple, il se peut que les images ne s\'affichent pas tant que vous n\'appuyez pas dessus."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Caméra"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Micro"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"se superpose aux autres applications sur l\'écran"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Envoyer des commentaires"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification d\'information du mode Routine"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Vous risquez d\'être à court de batterie plus tôt que prévu"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Économiseur de batterie activé pour prolonger l\'autonomie"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 5bfb3e1027e3..dffca1c2abcd 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Preme Menú para desbloquear ou realizar unha chamada de emerxencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Preme Menú para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Crea o padrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emerxencia"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Volver á chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Téntao de novo"</string>
@@ -857,7 +858,7 @@
<string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emerxencia"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Bloqueada pola rede"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"A tarxeta SIM está bloqueada con código PUK."</string>
- <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía do usuario ou ponte en contacto co servizo de asistencia ao cliente."</string>
+ <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía para usuarios ou ponte en contacto co servizo de asistencia ao cliente."</string>
<string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"A tarxeta SIM está bloqueada."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"Desbloqueando tarxeta SIM…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado polo teu administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado polo teu administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Para aumentar a duración da batería, a función Aforro de batería fai o seguinte:\n\n•Activa o tema escuro\n•Desactiva ou restrinxe a actividade en segundo plano, algúns efectos visuais e outras funcións, como \"Ok Google\"\n\n"<annotation id="url">"Máis información"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Para aumentar a duración da batería, a función Aforro de batería fai o seguinte:\n\n•Activa o tema escuro\n•Desactiva ou restrinxe a actividade en segundo plano, algúns efectos visuais e outras funcións, como \"Ok Google\""</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Para aumentar a duración da batería, a función Aforro de batería fai o seguinte:\n\n• Activa o tema escuro.\n• Desactiva ou restrinxe a actividade en segundo plano, algúns efectos visuais e outras funcións, como \"Ok Google\".\n\n"<annotation id="url">"Máis información"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Para aumentar a duración da batería, a función Aforro de batería fai o seguinte:\n\n• Activa o tema escuro\n• Desactiva ou restrinxe a actividade en segundo plano, algúns efectos visuais e outras funcións, como \"Ok Google\""</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para contribuír a reducir o uso de datos, o aforro de datos impide que algunhas aplicacións envíen ou reciban datos en segundo plano. Cando esteas utilizando unha aplicación, esta poderá acceder aos datos, pero é posible que o faga con menos frecuencia. Por exemplo, é posible que as imaxes non se mostren ata que as toques."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Queres activar o aforro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Cámara"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Micrófono"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"mostrando outras aplicacións na pantalla"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Enviar comentarios"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificación da información do modo de rutina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A batería pode esgotarse antes do habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Para ampliar a duración da batería activouse a función Aforro de batería"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index be3ddadc6e12..784e85f9a174 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"અનલૉક કરવા માટે અથવા કટોકટીનો કૉલ કરવા માટે મેનૂ દબાવો."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"અનલૉક કરવા માટે પૅટર્ન દોરો."</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ઇમર્જન્સી"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"કૉલ પર પાછા ફરો"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"સાચું!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ફરી પ્રયાસ કરો"</string>
@@ -854,7 +855,7 @@
<string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"રોકો"</string>
<string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"રીવાઇન્ડ કરો"</string>
<string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"ઝડપી ફોરવર્ડ કરો"</string>
- <string name="emergency_calls_only" msgid="3057351206678279851">"ફક્ત કટોકટીના કૉલ્સ"</string>
+ <string name="emergency_calls_only" msgid="3057351206678279851">"ફક્ત ઇમર્જન્સી કૉલ"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"નેટવર્ક લૉક થયું"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"સિમ કાર્ડ, PUK-લૉક કરેલ છે."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"વપરાશકર્તા માર્ગદર્શિકા જુઓ અથવા ગ્રાહક સંભાળનો સંપર્ક કરો."</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"તમારા વ્યવસ્થાપક દ્વારા અપડેટ કરવામાં આવેલ છે"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"તમારા વ્યવસ્થાપક દ્વારા કાઢી નાખવામાં આવેલ છે"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ઓકે"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"બૅટરીની આવરદા વધારવા માટે, બૅટરી સેવર:\n\n•ઘેરી થીમ ચાલુ કરે છે\n•બૅકગ્રાઉન્ડ પ્રવૃત્તિ, અમુક વિઝ્યુઅલ ઇફેક્ટ અને “ઓકે Google” જેવી અન્ય સુવિધાઓ બંધ અથવા પ્રતિબંધિત કરે છે\n\n"<annotation id="url">"વધુ જાણો"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"બૅટરીની આવરદા વધારવા માટે, બૅટરી સેવર:\n\n•ઘેરી થીમ ચાલુ કરે છે\n•બૅકગ્રાઉન્ડ પ્રવૃત્તિ, અમુક વિઝ્યુઅલ ઇફેક્ટ અને “ઓકે Google” જેવી અન્ય સુવિધાઓ બંધ અથવા પ્રતિબંધિત કરે છે"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"બૅટરીની આવરદા વધારવા માટે, બૅટરી સેવર:\n\n• ઘેરી થીમ ચાલુ કરે છે\n• બૅકગ્રાઉન્ડ પ્રવૃત્તિ, અમુક વિઝ્યુઅલ ઇફેક્ટ અને “ઓકે Google” જેવી અન્ય સુવિધાઓ બંધ અથવા પ્રતિબંધિત કરે છે\n\n"<annotation id="url">"વધુ જાણો"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"બૅટરીની આવરદા વધારવા માટે, બૅટરી સેવર:\n\n• ઘેરી થીમ ચાલુ કરે છે\n• બૅકગ્રાઉન્ડ પ્રવૃત્તિ, અમુક વિઝ્યુઅલ ઇફેક્ટ અને “ઓકે Google” જેવી અન્ય સુવિધાઓ બંધ અથવા પ્રતિબંધિત કરે છે"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ડેટા વપરાશને ઘટાડવામાં સહાય માટે, ડેટા સેવર કેટલીક ઍપને બૅકગ્રાઉન્ડમાં ડેટા મોકલવા અથવા પ્રાપ્ત કરવાથી અટકાવે છે. તમે હાલમાં ઉપયોગ કરી રહ્યાં છો તે ઍપ ડેટાને ઍક્સેસ કરી શકે છે, પરંતુ તે આ ક્યારેક જ કરી શકે છે. આનો અર્થ એ હોઈ શકે છે, ઉદાહરણ તરીકે, છબીઓ ત્યાં સુધી પ્રદર્શિત થશે નહીં જ્યાં સુધી તમે તેને ટૅપ નહીં કરો."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ડેટા સેવર ચાલુ કરીએ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ચાલુ કરો"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"કૅમેરા"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"માઇક્રોફોન"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"આ તમારી સ્ક્રીન પર અન્ય ઍપની ઉપર દેખાશે"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"પ્રતિસાદ આપો"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"રૂટિન મોડની માહિતીનું નોટિફિકેશન"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"સામાન્ય રીતે ચાર્જ કરવાના સમય પહેલાં બૅટરી સમાપ્ત થઈ શકે છે"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"બૅટરી આવરદા વધારવા માટે બૅટરી સેવર ચાલુ કર્યું"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 776e5ee56876..82a106295acb 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"लॉक खोलने के लिए मेन्यू दबाएं या आपातलकालीन कॉल करें."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"लॉक खोलने के लिए मेन्यू दबाएं."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलॉक करने के लिए आकार आरेखित करें"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आपातकाल"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कॉल पर वापस लौटें"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फिर से कोशिश करें"</string>
@@ -1548,7 +1549,7 @@
<string name="launchBrowserDefault" msgid="6328349989932924119">"ब्राउज़र लॉन्च करें?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"कॉल स्वीकार करें?"</string>
<string name="activity_resolver_use_always" msgid="5575222334666843269">"हमेशा"</string>
- <string name="activity_resolver_use_once" msgid="948462794469672658">"केवल एक बार"</string>
+ <string name="activity_resolver_use_once" msgid="948462794469672658">"सिर्फ़ एक बार"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s वर्क प्रोफ़ाइल का समर्थन नहीं करता"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"टैबलेट"</string>
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"टीवी"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"आपके व्यवस्थापक ने अपडेट किया है"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"आपके व्यवस्थापक ने हटा दिया है"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ठीक है"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"बैटरी लाइफ़ बढ़ाने के लिए, बैटरी सेवर:\n\n•गहरे रंग वाली थीम चालू करता है\n•बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और \"Hey Google\" जैसी दूसरी सुविधाएं इस्तेमाल करने से रोकता है या उन्हें बंद कर देता है\n\n"<annotation id="url">"ज़्यादा जानें"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"बैटरी लाइफ़ बढ़ाने के लिए, बैटरी सेवर:\n\n•गहरे रंग वाली थीम चालू करता है\n•बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और \"Hey Google\" जैसी दूसरी सुविधाएं इस्तेमाल करने से रोकता है या उन्हें बंद कर देता है"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"बैटरी लाइफ़ बढ़ाने के लिए, बैटरी सेवर:\n\n• गहरे रंग वाली थीम चालू करता है\n• बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और \"Hey Google\" जैसी दूसरी सुविधाएं इस्तेमाल करने से रोकता है या इन्हें बंद कर देता है\n\n"<annotation id="url">"ज़्यादा जानें"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"बैटरी लाइफ़ बढ़ाने के लिए, बैटरी सेवर:\n\n• गहरे रंग वाली थीम चालू करता है\n• बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और \"Hey Google\" जैसी दूसरी सुविधाएं इस्तेमाल करने से रोकता है या इन्हें बंद कर देता है"</string>
<string name="data_saver_description" msgid="4995164271550590517">"डेटा खर्च को कम करने के लिए, डेटा बचाने की सेटिंग कुछ ऐप्लिकेशन को बैकग्राउंड में डेटा भेजने या डेटा पाने से रोकती है. फ़िलहाल, आप जिस ऐप्लिकेशन का इस्तेमाल कर रहे हैं वह डेटा ऐक्सेस कर सकता है, लेकिन ऐसा कभी-कभी ही हो पाएगा. उदाहरण के लिए, इमेज तब तक दिखाई नहीं देंगी जब तक कि आप उन पर टैप नहीं करते."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा बचाने की सेटिंग चालू करें?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"चालू करें"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"कैमरा"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"माइक्रोफ़ोन"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"आपकी स्क्रीन पर, इस्तेमाल हो रहे दूसरे ऐप्लिकेशन के ऊपर दिखाया जा रहा है"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"सुझाव दें या शिकायत करें"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"रूटीन मोड जानकारी की सूचना"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"बैटरी आम तौर पर जितने समय चलती है, उससे पहले खत्म हो सकती है"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"बैटरी लाइफ़ बढ़ाने के लिए \'बैटरी सेवर\' चालू हो गया है"</string>
@@ -2031,7 +2031,7 @@
<item quantity="one"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> फ़ाइलें</item>
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> फ़ाइलें</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"शेयर करने के लिए किसी व्यक्ति का सुझाव नहीं दिया गया है"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"शेयर करने के लिए, किसी व्यक्ति का सुझाव नहीं दिया गया है"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"ऐप्लिकेशन की सूची"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"इस ऐप्लिकेशन को रिकॉर्ड करने की अनुमति नहीं दी गई है. हालांकि, ऐप्लिकेशन इस यूएसबी डिवाइस से ऐसा कर सकता है."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"होम"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 8240a6ae06b6..f1bcd398c65f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite Izbornik za otključavanje ili pozivanje hitnih službi."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite Izbornik za otključavanje."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Iscrtajte uzorak za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitne službe"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Uzvrati poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ispravno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Pokušajte ponovo"</string>
@@ -1815,8 +1816,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"U redu"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Da bi se produljilo trajanje baterije, Štednja baterije:\n\n• Uključuje Tamnu temu.\n• Isključuje ili ograničava aktivnosti u pozadini, neke vizualne efekte i druge značajke kao što je \"Hey Google\".\n\n"<annotation id="url">"Saznajte više"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Da bi se produljilo trajanje baterije, Štednja baterije:\n\n• Uključuje Tamnu temu.\n• Isključuje ili ograničava aktivnosti u pozadini, neke vizualne efekte i druge značajke kao što je \"Hey Google\"."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Da bi se produljilo trajanje baterije, Štednja baterije:\n\n• Uključuje Tamnu temu.\n• Isključuje ili ograničava aktivnosti u pozadini, neke vizualne efekte i druge značajke kao što je \"Hey Google\".\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Da bi se produljilo trajanje baterije, Štednja baterije:\n\n• Uključuje Tamnu temu.\n• Isključuje ili ograničava aktivnosti u pozadini, neke vizualne efekte i druge značajke kao što je \"Hey Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjio podatkovni promet, značajka Štednja podatkovnog prometa onemogućuje nekim aplikacijama slanje ili primanje podataka u pozadini. Aplikacija koju trenutačno upotrebljavate može pristupiti podacima, no možda će to činiti rjeđe. To može značiti da se, na primjer, slike neće prikazivati dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Štednju podatkovnog prometa?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string>
@@ -2030,8 +2031,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Fotoaparat"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"prikazuje se preko drugih aplikacija na zaslonu"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Slanje povratnih informacija"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Obavještavanje o informacijama u Rutinskom načinu rada"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterija se može isprazniti prije uobičajenog vremena punjenja"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Štednja baterije aktivirana je kako bi se produljilo trajanje baterije"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 78c84d9ff2c8..3106371a2a33 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"A feloldáshoz vagy segélyhívás kezdeményezéséhez nyomja meg a Menü gombot."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"A feloldáshoz nyomja meg a Menü gombot."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Rajzolja le a mintát a feloldáshoz"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Segélyhívás"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Hívás folytatása"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Helyes!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Próbálja újra"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"A rendszergazda által frissítve"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"A rendszergazda által törölve"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Az Akkumulátorkímélő mód az akkumulátor üzemidejének növelése érdekében a következőket teszi:\n\n• Bekapcsolja a sötét témát.\n• Kikapcsolja vagy korlátozza a háttérben futó tevékenységeket, egyes vizuális effekteket, az „Ok Google” parancsot és egyéb funkciókat.\n\n"<annotation id="url">"További információ"</annotation>"."</string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Az Akkumulátorkímélő mód az akkumulátor üzemidejének növelése érdekében a következőket teszi:\n\n• Bekapcsolja a sötét témát.\n• Kikapcsolja vagy korlátozza a háttérben futó tevékenységeket, egyes vizuális effekteket, az „Ok Google” parancsot és egyéb funkciókat."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Az Akkumulátorkímélő mód az akkumulátor üzemidejének növelése érdekében a következőket teszi:\n\n• Bekapcsolja a sötét témát.\n• Kikapcsolja vagy korlátozza a háttérben futó tevékenységeket, egyes vizuális effekteket, az „Ok Google” parancsot és egyéb funkciókat.\n\n"<annotation id="url">"További információ"</annotation>"."</string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Az Akkumulátorkímélő mód az akkumulátor üzemidejének növelése érdekében a következőket teszi:\n\n• Bekapcsolja a sötét témát.\n• Kikapcsolja vagy korlátozza a háttérben futó tevékenységeket, egyes vizuális effekteket, az „Ok Google” parancsot és egyéb funkciókat."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Az adatforgalom csökkentése érdekében az Adatforgalom-csökkentő megakadályozza, hogy egyes alkalmazások adatokat küldjenek vagy fogadjanak a háttérben. Az Ön által jelenleg használt alkalmazások hozzáférhetnek az adatokhoz, de csak ritkábban. Ez például azt jelentheti, hogy a képek csak rákoppintás után jelennek meg."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Bekapcsolja az Adatforgalom-csökkentőt?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Bekapcsolás"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"megjelenítés a képernyőn lévő egyéb alkalmazások előtt"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Visszajelzés küldése"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Információs értesítés a rutinmódról"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Előfordulhat, hogy az akkumulátor lemerül a szokásos töltési időszak előtt"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Akkumulátorkímélő mód aktiválva az akkumulátor üzemidejének növelése érdekében"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index fd6c8ed4b5d3..85afc27bb14c 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ապակողպելու կամ շտապ կանչ անելու համար սեղմեք «Ընտրացանկ»"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Հավաքեք սխեման` ապակողպելու համար"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Շտապ կանչ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Վերադառնալ զանգին"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ճիշտ է:"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Կրկին փորձեք"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Թարմացվել է ձեր ադմինիստրատորի կողմից"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Ջնջվել է ձեր ադմինիստրատորի կողմից"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Եղավ"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Մարտկոցի աշխատաժամանակը երկարացնելու համար «Մարտկոցի տնտեսում» գործառույթը.\n\n•Միացնում է մուգ թեման։\n•Անջատում կամ սահմանափակում է աշխատանքը ֆոնային ռեժիմում, որոշ վիզուալ էֆեկտներ և այլ գործառույթներ, օրինակ՝ «Ok Google» հրահանգը։\n\n"<annotation id="url">"Իմանալ ավելին"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Մարտկոցի աշխատաժամանակը երկարացնելու համար «Մարտկոցի տնտեսում» գործառույթը.\n\n•Միացնում է մուգ թեման։\n•Անջատում կամ սահմանափակում է աշխատանքը ֆոնային ռեժիմում, որոշ վիզուալ էֆեկտներ և այլ գործառույթներ, օրինակ՝ «Ok Google» հրահանգը:"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Մարտկոցի աշխատաժամանակը երկարացնելու համար «Մարտկոցի տնտեսում» գործառույթը՝\n\n• Միացնում է մուգ թեման։\n• Անջատում կամ սահմանափակում է աշխատանքը ֆոնային ռեժիմում, որոշ վիզուալ էֆեկտներ և այլ գործառույթներ, օրինակ՝ «Ok Google» հրահանգը։\n\n"<annotation id="url">"Իմանալ ավելին"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Մարտկոցի աշխատաժամանակը երկարացնելու համար «Մարտկոցի տնտեսում» գործառույթը՝\n\n• Միացնում է մուգ թեման։\n• Անջատում կամ սահմանափակում է աշխատանքը ֆոնային ռեժիմում, որոշ վիզուալ էֆեկտներ և այլ գործառույթներ, օրինակ՝ «Ok Google» հրահանգը։"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Թրաֆիկի տնտեսման ռեժիմում որոշ հավելվածների համար տվյալների ֆոնային փոխանցումն անջատված է։ Հավելվածը, որն օգտագործում եք, կարող է տվյալներ փոխանցել և ստանալ, սակայն ոչ այնքան հաճախ: Օրինակ՝ պատկերները կցուցադրվեն միայն դրանց վրա սեղմելուց հետո։"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Միացնե՞լ թրաֆիկի տնտեսումը"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Միացնել"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Տեսախցիկ"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Խոսափող"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ցուցադրվում է մյուս հավելվածների վրայից"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Կարծիք հայտնել"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ծանուցում լիցքավորման մասին"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Մարտկոցը կարող է սովորականից շուտ սպառվել"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Մարտկոցի կյանքը երկարացնելու համար ակտիվացվել է մարտկոցի տնտեսման ռեժիմը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index dc3ec4a929b8..8ec1f50eaceb 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tekan Menu untuk membuka atau melakukan panggilan darurat."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tekan Menu untuk membuka."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Buat pola untuk membuka"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Darurat"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kembali ke panggilan"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Perbaiki!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Coba lagi"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"MATI"</string>
<string name="checked" msgid="9179896827054513119">"dicentang"</string>
<string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Tindakan lengkap menggunakan"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Buka dengan"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Diupdate oleh admin Anda"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Dihapus oleh admin Anda"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Oke"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Untuk memperpanjang masa pakai baterai, Penghemat Baterai:\n\n•Mengaktifkan Tema gelap\n•Menonaktifkan atau membatasi aktivitas di latar belakang, beberapa efek visual, dan fitur lain seperti “Ok Google”\n\n"<annotation id="url">"Pelajari lebih lanjut"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Untuk memperpanjang masa pakai baterai, Penghemat Baterai:\n\n•Mengaktifkan Tema gelap\n•Menonaktifkan atau membatasi aktivitas di latar belakang, beberapa efek visual, dan fitur lain seperti “Ok Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Untuk memperpanjang masa pakai baterai, Penghemat Baterai akan:\n\n• Mengaktifkan Tema gelap\n• Menonaktifkan atau membatasi aktivitas di latar belakang, beberapa efek visual, dan fitur lain seperti “Ok Google”\n\n"<annotation id="url">"Pelajari lebih lanjut"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Untuk memperpanjang masa pakai baterai, Penghemat Baterai akan:\n\n• Mengaktifkan Tema gelap\n• Menonaktifkan atau membatasi aktivitas di latar belakang, beberapa efek visual, dan fitur lain seperti “Ok Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Untuk membantu mengurangi penggunaan data, Penghemat Data mencegah beberapa aplikasi mengirim atau menerima data di latar belakang. Aplikasi yang sedang digunakan dapat mengakses data, tetapi frekuensinya agak lebih jarang. Misalnya saja, gambar hanya akan ditampilkan setelah diketuk."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Aktifkan Penghemat Data?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktifkan"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ditampilkan di atas aplikasi lain di layar"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Beri Masukan"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notifikasi info Mode Rutinitas"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterai mungkin habis sebelum pengisian daya biasanya"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Penghemat Baterai diaktifkan untuk memperpanjang masa pakai baterai"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index dfc904f35136..1b0c2fe24521 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ýttu á valmyndartakkann til að taka úr lás eða hringja neyðarsímtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ýttu á valmyndartakkann til að taka úr lás."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Teiknaðu mynstur til að taka úr lás"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Neyðarsímtal"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Aftur í símtal"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Rétt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Reyndu aftur"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Kerfisstjóri uppfærði"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Kerfisstjóri eyddi"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Í lagi"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Til að auka rafhlöðuendingu gerir rafhlöðusparnaður eftirfarandi:\n\n•Kveikir á dökku þema\n•Slekkur á eða takmarkar bakgrunnsvirkni, tilteknar myndbrellur og aðra eiginleika eins og „Ok Google“\n\n"<annotation id="url">"Frekari upplýsingar"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Til að auka rafhlöðuendingu gerir rafhlöðusparnaður eftirfarandi:\n\n•Kveikir á dökku þema\n•Slekkur á eða takmarkar bakgrunnsvirkni, tilteknar myndbrellur og aðra eiginleika eins og „Ok Google“"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Til að auka rafhlöðuendingu gerir rafhlöðusparnaður eftirfarandi:\n\n•·Kveikir á dökku þema\n•·Slekkur á eða takmarkar bakgrunnsvirkni, tilteknar myndbrellur og aðra eiginleika eins og „Ok Google“\n\n"<annotation id="url">"Frekari upplýsingar"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Til að auka rafhlöðuendingu gerir rafhlöðusparnaður eftirfarandi:\n\n•·Kveikir á dökku þema\n•·Slekkur á eða takmarkar bakgrunnsvirkni, tilteknar myndbrellur og aðra eiginleika eins og „Ok Google“"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Gagnasparnaður getur hjálpað til við að draga úr gagnanotkun með því að hindra forrit í að senda eða sækja gögn í bakgrunni. Forrit sem er í notkun getur náð í gögn, en gerir það kannski sjaldnar. Niðurstaðan getur verið, svo dæmi sé tekið, að myndir eru ekki birtar fyrr en þú ýtir á þær."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Kveikja á gagnasparnaði?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Kveikja"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Myndavél"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Hljóðnemi"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"birt yfir öðrum forritum á skjánum"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Senda ábendingar"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Upplýsingatilkynning aðgerðastillingar"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Rafhlaðan kann að tæmast áður en hún kemst í hleðslu"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Kveikt á rafhlöðusparnaði til að lengja endingu rafhlöðunnar"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 1738ec05d0f8..d2bf62d94a40 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -203,7 +203,7 @@
<string name="factory_reset_message" msgid="2657049595153992213">"Impossibile usare l\'app di amministrazione. Il dispositivo verrà resettato.\n\nPer eventuali domande, contatta l\'amministratore della tua organizzazione."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Stampa disattivata da <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
<string name="personal_apps_suspension_title" msgid="7561416677884286600">"Attiva il profilo di lavoro"</string>
- <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Le tue app personali sono bloccate fino all\'attivazione del tuo profilo di lavoro"</string>
+ <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Le tue app personali saranno bloccate finché non attivi il tuo profilo di lavoro."</string>
<string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Le app personali verranno bloccate il giorno <xliff:g id="DATE">%1$s</xliff:g> alle ore <xliff:g id="TIME">%2$s</xliff:g>. L\'amministratore IT non consente di mantenere disattivato il profilo di lavoro per più di <xliff:g id="NUMBER">%3$d</xliff:g> giorni."</string>
<string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Attiva"</string>
<string name="me" msgid="6207584824693813140">"Io"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Premi Menu per sbloccare o effettuare chiamate di emergenza."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Premi Menu per sbloccare."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Traccia la sequenza di sblocco"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergenza"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Torna a chiamata"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corretta."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Riprova"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Aggiornato dall\'amministratore"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Per estendere la durata della batteria, la funzionalità di risparmio energetico:\n\n•Attiva il tema scuro\n•Disattiva o limita le attività in background, alcuni effetti visivi e altre funzionalità come \"Ok Google\"\n\n"<annotation id="url">"Ulteriori informazioni"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Per estendere la durata della batteria, la funzionalità di risparmio energetico:\n\n•Attiva il tema scuro\n•Disattiva o limita le attività in background, alcuni effetti visivi e altre funzionalità come \"Ok Google\""</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Per estendere la durata della batteria, la funzionalità di risparmio energetico:\n\n• Attiva il tema scuro\n• Disattiva o limita le attività in background, alcuni effetti visivi e altre funzionalità come \"Ok Google\"\n\n"<annotation id="url">"Ulteriori informazioni"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Per estendere la durata della batteria, la funzionalità di risparmio energetico:\n\n• Attiva il tema scuro\n• Disattiva o limita le attività in background, alcuni effetti visivi e altre funzionalità come \"Ok Google\""</string>
<string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzione Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Esempio: le immagini non vengono visualizzate finché non le tocchi."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Attivare Risparmio dati?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Attiva"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Fotocamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microfono"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"si sovrappone ad altre app sullo schermo"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Fornisci feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notifica di informazioni sulla modalità Routine"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"La batteria potrebbe esaurirsi prima della ricarica abituale"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Risparmio energetico attivo per far durare di più la batteria"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 567c2bbabee5..4d6d8155a981 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1203,7 +1203,7 @@
<string name="aerr_process_repeated" msgid="1153152413537954974">"האפליקציה <xliff:g id="PROCESS">%1$s</xliff:g> נעצרת שוב ושוב"</string>
<string name="aerr_restart" msgid="2789618625210505419">"פתח שוב את האפליקציה"</string>
<string name="aerr_report" msgid="3095644466849299308">"משוב"</string>
- <string name="aerr_close" msgid="3398336821267021852">"סגור"</string>
+ <string name="aerr_close" msgid="3398336821267021852">"סגירה"</string>
<string name="aerr_mute" msgid="2304972923480211376">"השתק עד הפעלה מחדש של המכשיר"</string>
<string name="aerr_wait" msgid="3198677780474548217">"המתן"</string>
<string name="aerr_close_app" msgid="8318883106083050970">"סגור את האפליקציה"</string>
@@ -1838,8 +1838,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"עודכנה על ידי מנהל המערכת"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"נמחקה על ידי מנהל המערכת"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"אישור"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"‏כדי להאריך את חיי הסוללה, התכונה \'חיסכון בסוללה\':\n\n•מפעילה עיצוב כהה\n•מכבה או מגבילה פעילות ברקע, חלק מהאפקטים החזותיים ותכונות אחרות כמו Ok Google\n\n"<annotation id="url">"מידע נוסף"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"‏כדי להאריך את חיי הסוללה, התכונה \'חיסכון בסוללה\':\n\n•מפעילה עיצוב כהה\n•מכבה או מגבילה פעילות ברקע, חלק מהאפקטים החזותיים ותכונות אחרות כמו Ok Google"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"‏כדי להאריך את חיי הסוללה, התכונה \'חיסכון בסוללה\':\n\n• מפעילה עיצוב כהה\n• מכבה או מגבילה פעילות ברקע, חלק מהאפקטים החזותיים ותכונות אחרות כמו Ok Google\n\n"<annotation id="url">"למידע נוסף"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"‏כדי להאריך את חיי הסוללה, התכונה \'חיסכון בסוללה\':\n\n• מפעילה עיצוב כהה\n• מכבה או מגבילה פעילות ברקע, חלק מהאפקטים החזותיים ותכונות אחרות כמו Ok Google"</string>
<string name="data_saver_description" msgid="4995164271550590517">"‏כדי לסייע בהפחתת השימוש בנתונים, חוסך הנתונים (Data Saver) מונע מאפליקציות מסוימות שליחה או קבלה של נתונים ברקע. אפליקציה שבה נעשה שימוש כרגע יכולה לגשת לנתונים, אבל בתדירות נמוכה יותר. המשמעות היא, למשל, שתמונות יוצגו רק לאחר שמקישים עליהן."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"להפעיל את חוסך הנתונים?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"הפעל"</string>
@@ -1925,7 +1925,7 @@
<string name="floating_toolbar_open_overflow_description" msgid="2260297653578167367">"אפשרויות נוספות"</string>
<string name="floating_toolbar_close_overflow_description" msgid="3949818077708138098">"סגור את האפשרויות הנוספות"</string>
<string name="maximize_button_text" msgid="4258922519914732645">"הגדל"</string>
- <string name="close_button_text" msgid="10603510034455258">"סגור"</string>
+ <string name="close_button_text" msgid="10603510034455258">"סגירה"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="two">בחרת <xliff:g id="COUNT_1">%1$d</xliff:g></item>
@@ -2063,8 +2063,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"מצלמה"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"מיקרופון"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"תצוגה מעל אפליקציות אחרות במסך"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"שליחת משוב"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"התראת מידע לגבי מצב שגרתי"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"הסוללה עלולה להתרוקן לפני המועד הרגיל של הטעינה"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"תכונת החיסכון בסוללה הופעלה כדי להאריך את חיי הסוללה"</string>
@@ -2099,7 +2098,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> קבצים</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> + קובץ אחד (<xliff:g id="COUNT_1">%d</xliff:g>)</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"אין אנשים מומלצים שניתן לשתף איתם"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"אין אנשים שניתן לשתף איתם"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"רשימת האפליקציות"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"‏לאפליקציה זו לא ניתנה הרשאת הקלטה, אבל אפשר להקליט אודיו באמצעות התקן ה-USB הזה."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"בית"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 17c41e72112a..f9674bd0db11 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"MENUキーでロック解除(または緊急通報)"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"MENUキーでロック解除"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"パターンを入力"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急通報"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"通話に戻る"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"一致しました"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"もう一度お試しください"</string>
@@ -1152,7 +1153,7 @@
<string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"「%1$s」を使用して画像をキャプチャ"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"画像をキャプチャ"</string>
<string name="alwaysUse" msgid="3153558199076112903">"常にこの操作で使用する"</string>
- <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリの使用"</string>
+ <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリを使用"</string>
<string name="clearDefaultHintMsg" msgid="1325866337702524936">"[システム設定]&gt;[アプリ]&gt;[ダウンロード済み]でデフォルト設定をクリアします。"</string>
<string name="chooseActivity" msgid="8563390197659779956">"操作の選択"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"USBデバイス用アプリを選択"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"管理者により更新されています"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"管理者により削除されています"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"電池を長持ちさせるためにバッテリー セーバーが行う操作:\n\n•ダークテーマをオンにする\n•バックグラウンド アクティビティ、一部の視覚効果や、「OK Google」などの機能をオフにする、または制限する\n\n"<annotation id="url">"詳細"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"電池を長持ちさせるためにバッテリー セーバーが行う操作:\n\n•ダークテーマをオンにする\n•バックグラウンド アクティビティ、一部の視覚効果や、「OK Google」などの機能をオフにする、または制限する"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"電池を長持ちさせるためにバッテリー セーバーが行う操作:\n\n• ダークテーマを ON にする\n• バックグラウンド アクティビティ、一部の視覚効果や、「OK Google」などの機能を OFF にする、または制限する\n\n"<annotation id="url">"詳細"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"電池を長持ちさせるためにバッテリー セーバーが行う操作:\n\n• ダークテーマを ON にする\n• バックグラウンド アクティビティ、一部の視覚効果や、「OK Google」などの機能を OFF にする、または制限する"</string>
<string name="data_saver_description" msgid="4995164271550590517">"データセーバーは、一部のアプリによるバックグラウンドでのデータ送受信を停止することでデータ使用量を抑制します。使用中のアプリからデータを送受信することはできますが、その頻度は低くなる場合があります。この影響として、たとえば画像はタップしないと表示されないようになります。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"データセーバーを ON にしますか?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ON にする"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"カメラ"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"マイク"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"画面の他のアプリの上に重ねて表示"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"フィードバックを送信"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ルーティン モード情報の通知"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"通常の充電を行う前に電池が切れる可能性があります"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"電池を長持ちさせるため、バッテリー セーバーが有効になりました"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index e48550265ef7..3d1e9c799a4d 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"განბლოკვისთვის ან გადაუდებელი ზარისთვის დააჭირეთ მენიუს."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"განბლოკვისთვის დააჭირეთ მენიუს."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"განსაბლოკად დახატეთ ნიმუში"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"საგანგებო სამსახურები"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ზარზე დაბრუნება"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"სწორია!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"კიდევ სცადეთ"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"განახლებულია თქვენი ადმინისტრატორის მიერ"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"წაიშალა თქვენი ადმინისტრატორის მიერ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"კარგი"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ბატარეის მუშაობის გახანგრძლივების მიზნით, ბატარეის დამზოგველი:\n\n•ჩართავს ბნელ თემას\n•გამორთავს ან ზღუდავს ფონურ აქტივობას, გარკვეულ ვიზუალურ ეფექტებს და სხვა ფუნქციებს, მაგალითად, „Hey Google“\n\n"<annotation id="url">"შეიტყვეთ მეტი"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ბატარეის მუშაობის გახანგრძლივების მიზნით, ბატარეის დამზოგველი:\n\n•ჩართავს ბნელ თემას\n•გამორთავს ან ზღუდავს ფონურ აქტივობას, გარკვეულ ვიზუალურ ეფექტებს და სხვა ფუნქციებს, მაგალითად, „Hey Google“"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ბატარეის მუშაობის გახანგრძლივების მიზნით, ბატარეის დამზოგველი:\n\n• ჩართავს ბნელ თემას\n• გამორთავს ან ზღუდავს ფონურ აქტივობას, გარკვეულ ვიზუალურ ეფექტებს და სხვა ფუნქციებს, მაგალითად, „Hey Google“\n\n"<annotation id="url">"შეიტყვეთ მეტი"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ბატარეის მუშაობის გახანგრძლივების მიზნით, ბატარეის დამზოგი:\n\n• ჩართავს ბნელ თემას\n• გამორთავს ან შეზღუდავს ფონურ აქტივობას, გარკვეულ ვიზუალურ ეფექტებს და სხვა ფუნქციებს, მაგალითად, „Hey Google“"</string>
<string name="data_saver_description" msgid="4995164271550590517">"მობილური ინტერნეტის მოხმარების შემცირების მიზნით, მონაცემთა დამზოგველი ზოგიერთ აპს ფონურ რეჟიმში მონაცემთა გაგზავნასა და მიღებას შეუზღუდავს. თქვენ მიერ ამჟამად გამოყენებული აპი მაინც შეძლებს მობილურ ინტერნეტზე წვდომას, თუმცა ამას ნაკლები სიხშირით განახორციელებს. ეს ნიშნავს, რომ, მაგალითად, სურათები არ გამოჩნდება მანამ, სანამ მათ საგანგებოდ არ შეეხებით."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ჩაირთოს მონაცემთა დამზოგველი?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ჩართვა"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"კამერა"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"მიკროფონი"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"სხვა აპების გადაფარვით ჩანს თქვენს ეკრანზე"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"გამოხმაურება"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"რუტინის რეჟიმის საინფორმაციო შეტყობინება"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ბატარეა შეიძლება დაჯდეს დატენის ჩვეულ დრომდე"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ბატარეის დამზოგი გააქტიურდა ბატარეის მუშაობის გასახანგრძლივლებლად"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 1b854ef6feaf..38a50abfe6e7 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Бекітпесін ашу үшін немесе төтенше қоңырауды табу үшін Мәзір тармағын басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ашу үшін Мәзір пернесін басыңыз."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Бекітпесін ашу үшін кескінді сызыңыз"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Төтенше жағдай"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Қоңырауға оралу"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Дұрыс!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Қайталап көріңіз"</string>
@@ -1547,7 +1548,7 @@
<string name="sending" msgid="206925243621664438">"Жіберілуде..."</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"Браузер қосылсын ба?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"Қоңырауды қабылдау?"</string>
- <string name="activity_resolver_use_always" msgid="5575222334666843269">"Үнемі"</string>
+ <string name="activity_resolver_use_always" msgid="5575222334666843269">"Әрқашан"</string>
<string name="activity_resolver_use_once" msgid="948462794469672658">"Бір рет қана"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жұмыс профилін қолдамайды"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Әкімші жаңартқан"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Әкімші жойған"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Жарайды"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Батарея жұмысының ұзақтығын арттыру үшін Battery Saver:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді немесе шектейді.\n\n"<annotation id="url">"Толығырақ"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Батарея жұмысының ұзақтығын арттыру үшін Battery Saver:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді немесе шектейді."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батарея жұмысының ұзақтығын арттыру үшін Battery Saver:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді не шектейді.\n\n"<annotation id="url">"Толығырақ"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Батарея ұзағырақ жұмыс істеуі үшін, Battery Saver:\n\n• қараңғы тақырыпты қосады;\n•фондық жұмысты, кейбір визуалды әсерлерді және \"Ok Google\" сияқты басқа функцияларды өшіреді не шектейді."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Дерек шығынын азайту үшін Data Saver функциясы кейбір қолданбаларға деректерді фондық режимде жіберуге және алуға жол бермейді. Ашық тұрған қолданба деректерді пайдаланады, бірақ шектеулі шамада (мысалы, кескіндер оларды түрткенге дейін көрсетілмейді)."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Data Saver функциясын қосу керек пе?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Қосу"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"экранда басқа қолданбалардың үстінен көрсету"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Пікір білдіру"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Режим туралы хабарландыру"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея заряды азаюы мүмкін"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батарея ұзаққа жетуі үшін, Battery Saver іске қосылды"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index c4a8ab3a084d..4e646c5d2a07 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ចុច​ម៉ឺនុយ ដើម្បី​ដោះ​សោ​ ឬ​ហៅ​ពេល​អាសន្ន។"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ចុច​ម៉ឺនុយ ដើម្បី​ដោះ​សោ។"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"គូរ​លំនាំ ដើម្បី​ដោះ​សោ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"បន្ទាន់"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ត្រឡប់​ទៅ​ការ​ហៅ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ត្រឹមត្រូវ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ព្យាយាម​ម្ដង​ទៀត"</string>
@@ -1126,9 +1127,7 @@
<string name="checked" msgid="9179896827054513119">"បានធីក​"</string>
<string name="not_checked" msgid="7972320087569023342">"មិន​បាន​ធីក​"</string>
<string name="whichApplication" msgid="5432266899591255759">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for whichApplicationNamed (6969946041713975681) -->
- <skip />
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ​ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"បញ្ចប់សកម្មភាព"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"បើក​ជា​មួយ"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"បើក​ជាមួយ %1$s"</string>
@@ -1794,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ធ្វើ​បច្ចុប្បន្នភាព​ដោយ​អ្នកគ្រប់គ្រង​របស់​អ្នក"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"លុប​ដោយ​អ្នកគ្រប់គ្រង​របស់​អ្នក"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"យល់ព្រម"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ដើម្បី​បង្កើនកម្រិត​ថាមពលថ្ម កម្មវិធី​សន្សំថ្ម៖\n\n•បើករចនាប័ទ្មងងឹត\n•បិទ ឬដាក់កំហិតលើ​សកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពល​ជារូបភាពមួយចំនួន និងមុខងារផ្សេងទៀត​ដូចជា “Ok Google” ជាដើម\n\n"<annotation id="url">"ស្វែងយល់​បន្ថែម"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ដើម្បី​បង្កើនកម្រិត​ថាមពលថ្ម កម្មវិធី​សន្សំថ្ម៖\n\n•បើករចនាប័ទ្មងងឹត\n•បិទ ឬដាក់កំហិតលើ​សកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពល​ជារូបភាពមួយចំនួន និងមុខងារផ្សេងទៀត​ដូចជា “Ok Google” ជាដើម"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ដើម្បី​បង្កើនកម្រិត​ថាមពលថ្ម កម្មវិធី​សន្សំថ្ម៖\n\n• បើករចនាប័ទ្មងងឹត\n• បិទ ឬដាក់កំហិតលើ​សកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពល​ជារូបភាពមួយចំនួន និងមុខងារផ្សេងទៀត​ដូចជា “Ok Google” ជាដើម\n\n"<annotation id="url">"ស្វែងយល់​បន្ថែម"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ដើម្បី​បង្កើនកម្រិត​ថាមពលថ្ម មុខងារ​សន្សំថ្ម៖\n\n• បើករចនាប័ទ្មងងឹត\n• បិទ ឬដាក់កំហិតលើ​សកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន និងមុខងារផ្សេងទៀត​ដូចជា “Ok Google” ជាដើម"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ដើម្បីជួយកាត់បន្ថយការប្រើប្រាស់ទិន្នន័យ កម្មវិធីសន្សំសំចៃទិន្នន័យរារាំងកម្មវិធីមួយចំនួនមិនឲ្យបញ្ជូន ឬទទួលទិន្នន័យនៅផ្ទៃខាងក្រោយទេ។ កម្មវិធីដែលអ្នកកំពុងប្រើនាពេលបច្ចុប្បន្នអាចចូលប្រើប្រាស់​ទិន្នន័យបាន ប៉ុន្តែអាចនឹងមិនញឹកញាប់ដូចមុនទេ។ ឧទាហរណ៍ រូបភាពមិនបង្ហាញទេ លុះត្រាតែអ្នកប៉ះរូបភាពទាំងនោះ។"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"បើកកម្មវិធីសន្សំសំចៃទិន្នន័យ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"បើក"</string>
@@ -1999,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"កាមេរ៉ា"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"មីក្រូហ្វូន"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"កំពុងបង្ហាញ​ពីលើកម្មវិធីផ្សេងទៀត​នៅលើអេក្រង់​របស់អ្នក"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ផ្ដល់​មតិកែលម្អ"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ការ​ជូនដំណឹង​ព័ត៌មាន​របស់​មុខងារ​ទម្លាប់"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ថ្ម​អាច​នឹង​អស់ មុនពេល​សាកថ្មធម្មតា"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"បាន​បើក​ដំណើរការកម្មវិធី​សន្សំ​ថ្ម ដើម្បីបង្កើនកម្រិត​ថាមពល​​ថ្ម"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 077f79b412ac..8d742156ca2c 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ ಇಲ್ಲವೇ ತುರ್ತು ಕರೆಯನ್ನು ಮಾಡಿ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಪ್ಯಾಟರ್ನ್ ಚಿತ್ರಿಸಿ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ತುರ್ತು"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ಕರೆಗೆ ಹಿಂತಿರುಗು"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ಸರಿಯಾಗಿದೆ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
@@ -1651,7 +1652,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"ಬಣ್ಣ ವಿಲೋಮ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸಲು ಎರಡೂ ಧ್ವನಿ ಕೀಗಳನ್ನು ಮೂರು ಸೆಕೆಂಡ್‌ಗಳ ಕಾಲ ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ನೀವು ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್ ಟ್ಯಾಪ್ ಮಾಡಿದಾಗ ಬಳಸುವುದಕ್ಕಾಗಿ ವೈಶಿಷ್ಟ್ಯವೊಂದನ್ನು ಆರಿಸಿ:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ಪ್ರವೇಶಿಸುವಿಕೆ ಗೆಸ್ಚರ್‌ನೊಂದಿಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯವೊಂದನ್ನು ಆಯ್ಕೆಮಾಡಿ (ಎರಡು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಪರದೆಯ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ):"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರಿಂದ ಅಪ್‌ಡೇಟ್ ಮಾಡಲ್ಪಟ್ಟಿದೆ"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ಅಳಿಸಿದ್ದಾರೆ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ಸರಿ"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯನ್ನು ವಿಸ್ತರಿಸಲು, ಬ್ಯಾಟರಿ ಸೇವರ್:\n\n•ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಆನ್ ಮಾಡುತ್ತದೆ\n•ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆ, ಕೆಲವು ದೃಶ್ಯಾತ್ಮಕ ಎಫೆಕ್ಟ್‌ಗಳು ಮತ್ತು “ಹೇ Google” ನಂತಹ ಇತರ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಫ್ ಮಾಡುತ್ತದೆ ಅಥವಾ ನಿರ್ಬಂಧಿಸುತ್ತದೆ\n\n"<annotation id="url">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯನ್ನು ವಿಸ್ತರಿಸಲು, ಬ್ಯಾಟರಿ ಸೇವರ್:\n\n•ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಆನ್ ಮಾಡುತ್ತದೆ\n•ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆ, ಕೆಲವು ವಿಷುವಲ್ ಎಫೆಕ್ಟ್‌ಗಳು ಮತ್ತು “ಹೇ Google” ನಂತಹ ಇತರ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಫ್ ಮಾಡುತ್ತದೆ ಅಥವಾ ನಿರ್ಬಂಧಿಸುತ್ತದೆ"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯನ್ನು ವಿಸ್ತರಿಸಲು, ಬ್ಯಾಟರಿ ಸೇವರ್:\n\n• ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಆನ್ ಮಾಡುತ್ತದೆ\n•ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆ, ಕೆಲವು ದೃಶ್ಯಾತ್ಮಕ ಎಫೆಕ್ಟ್‌ಗಳು ಮತ್ತು “ಹೇ Google” ನಂತಹ ಇತರ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಫ್ ಮಾಡುತ್ತದೆ ಅಥವಾ ನಿರ್ಬಂಧಿಸುತ್ತದೆ\n\n"<annotation id="url">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯನ್ನು ವಿಸ್ತರಿಸಲು, ಬ್ಯಾಟರಿ ಸೇವರ್:\n\n• ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಆನ್ ಮಾಡುತ್ತದೆ\n• ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆ, ಕೆಲವು ವಿಷುವಲ್ ಎಫೆಕ್ಟ್‌ಗಳು ಮತ್ತು “Ok Google” ನಂತಹ ಇತರ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಫ್ ಮಾಡುತ್ತದೆ ಅಥವಾ ನಿರ್ಬಂಧಿಸುತ್ತದೆ"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ಡೇಟಾ ಬಳಕೆ ಕಡಿಮೆ ಮಾಡುವ ನಿಟ್ಟಿನಲ್ಲಿ, ಡೇಟಾ ಸೇವರ್ ಕೆಲವು ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಡೇಟಾ ಕಳುಹಿಸುವುದನ್ನು ಅಥವಾ ಸ್ವೀಕರಿಸುವುದನ್ನು ತಡೆಯುತ್ತದೆ. ನೀವು ಪ್ರಸ್ತುತ ಬಳಸುತ್ತಿರುವ ಅಪ್ಲಿಕೇಶನ್ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು ಆದರೆ ಪದೇ ಪದೇ ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. ಇದರರ್ಥ, ಉದಾಹರಣೆಗೆ, ನೀವು ಅವುಗಳನ್ನು ಟ್ಯಾಪ್ ಮಾಡುವವರೆಗೆ ಆ ಚಿತ್ರಗಳು ಕಾಣಿಸಿಕೊಳ್ಳುವುದಿಲ್ಲ."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ಡೇಟಾ ಸೇವರ್ ಆನ್ ಮಾಡಬೇಕೇ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ಆನ್‌ ಮಾಡಿ"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"ಕ್ಯಾಮರಾ"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"ಮೈಕ್ರೋಫೋನ್‌"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಮೂಲಕ ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತಿದೆ"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ಪ್ರತಿಕ್ರಿಯೆ ಒದಗಿಸಿ"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ದೈನಂದಿನ ಸ್ಥಿತಿಯ ಮಾಹಿತಿಯ ಅಧಿಸೂಚನೆ"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ಚಾರ್ಜ್‌ಗೆ ಮೊದಲೆ ಬ್ಯಾಟರಿ ಮುಗಿದು ಬಿಡಬಹುದು"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ಬ್ಯಾಟರಿ ಅವಧಿ ಹೆಚ್ಚಿಸಲು ಬ್ಯಾಟರಿ ಸೇವರ್ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 18636c3a08de..5d960dcc0aa7 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"비상 전화를 걸거나 잠금해제하려면 메뉴를 누르세요."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"잠금해제하려면 메뉴를 누르세요."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"잠금해제를 위해 패턴 그리기"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"긴급 전화"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"통화로 돌아가기"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"맞습니다."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"다시 시도"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"관리자에 의해 업데이트되었습니다."</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"관리자에 의해 삭제되었습니다."</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"확인"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"배터리 수명을 늘리기 위해 절전 모드가 다음과 같이 작동합니다.\n\n•어두운 테마를 사용 설정합니다.\n•백그라운드 활동, 일부 시각 효과 및 \'Hey Google\'과 같은 기타 기능을 사용 중지하거나 제한합니다.\n\n"<annotation id="url">"자세히 알아보기"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"배터리 수명을 늘리기 위해 절전 모드가 다음과 같이 작동합니다.\n\n•어두운 테마를 사용 설정합니다.\n•백그라운드 활동, 일부 시각 효과 및 \'Hey Google\'과 같은 기타 기능을 사용 중지하거나 제한합니다."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"배터리 수명을 늘리기 위해 절전 모드가 다음과 같이 작동합니다.\n\n• 어두운 테마를 사용 설정합니다.\n• 백그라운드 활동, 일부 시각 효과 및 \'Hey Google\'과 같은 기타 기능을 사용 중지하거나 제한합니다.\n\n"<annotation id="url">"자세히 알아보기"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"배터리 수명을 연장하기 위해 절전 모드가 다음과 같이 작동합니다.\n\n• 어두운 테마를 사용 설정합니다.\n• 백그라운드 활동, 일부 시각 효과 및 \'Hey Google\' 등의 기타 기능을 사용 중지하거나 제한합니다."</string>
<string name="data_saver_description" msgid="4995164271550590517">"데이터 사용량을 줄이기 위해 데이터 절약 모드는 일부 앱이 백그라운드에서 데이터를 전송하거나 수신하지 못하도록 합니다. 현재 사용 중인 앱에서 데이터에 액세스할 수 있지만 빈도가 줄어듭니다. 예를 들면, 이미지를 탭하기 전에는 이미지가 표시되지 않습니다."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"데이터 절약 모드를 사용 설정하시겠습니까?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"사용 설정"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"카메라"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"마이크"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"화면에서 다른 앱 위에 표시"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"의견 보내기"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"루틴 모드 정보 알림"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"평소에 충전하는 시간 전에 배터리가 소진될 수 있습니다."</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"배터리 수명을 연장하기 위해 배터리 세이버가 활성화되었습니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 084c753f7989..c97324f738b3 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -538,7 +538,7 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Колдонмого сүрөт жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"медиа жыйнагыңыз сакталган жерлерди окуу"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."</string>
- <string name="biometric_dialog_default_title" msgid="55026799173208210">"Сиз экениңизди ырастаңыз"</string>
+ <string name="biometric_dialog_default_title" msgid="55026799173208210">"Өзүңүздү ырастаңыз"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалык аппарат жеткиликсиз"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аныктыгын текшерүү жокко чыгарылды"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Таанылган жок"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Кулпусун ачып же Шашылыш чалуу аткаруу үчүн менюну басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Бөгөттөн чыгаруу үчүн Менюну басыңыз."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Кулпуну ачуу үчүн, үлгүнү тартыңыз"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Тез жардам"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Чалууга кайтуу"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Туура!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дагы аракет кылыңыз"</string>
@@ -1111,7 +1112,7 @@
<string name="inputMethod" msgid="1784759500516314751">"Киргизүү ыкмасы"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Текст боюнча иштер"</string>
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string>
- <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
+ <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Айрым функциялар иштебеши мүмкүн"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> иштөөдө"</string>
<string name="app_running_notification_text" msgid="5120815883400228566">"Көбүрөөк маалымат үчүн же колдонмону токтотуш үчүн таптап коюңуз."</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"ӨЧҮК"</string>
<string name="checked" msgid="9179896827054513119">"белгиленген"</string>
<string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Аракет колдонууну бүтүрүү"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Аракетти аягына чыгаруу"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Төмөнкү менен ачуу"</string>
@@ -1547,7 +1548,7 @@
<string name="sending" msgid="206925243621664438">"Жөнөтүлүүдө…"</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"Серепчи иштетилсинби?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"Чалуу кабыл алынсынбы?"</string>
- <string name="activity_resolver_use_always" msgid="5575222334666843269">"Дайыма"</string>
+ <string name="activity_resolver_use_always" msgid="5575222334666843269">"Ар дайым"</string>
<string name="activity_resolver_use_once" msgid="948462794469672658">"Бир жолу гана"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жумуш профилин колдоого албайт"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Администраторуңуз жаңыртып койгон"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Администраторуңуз жок кылып салган"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ЖАРАЙТ"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Батареянын мөөнөтүн узартуу үчүн, Батареяны үнөмдөгүч режими төмөнкүлөрдү аткарат:\n\n•Караңгы теманы күйгүзөт\n•Фондогу аракеттерди, айрым визуалдык эффекттерди жана \"Окей Google\" сыяктуу башка функцияларды өчүрөт же чектейт\n\n"<annotation id="url">"Кеңири маалымат"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Батареянын иштешин узартуу үчүн, Батареяны үнөмдөөчү режим:\n\n•Караңгы теманы күйгүзөт\n•Фондогу аракеттерди, айрым визуалдык эффекттерди жана \"Окей Google\" сыяктуу башка функцияларды өчүрөт же чектейт"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батареянын мөөнөтүн узартуу үчүн, Батареяны үнөмдөгүч режими төмөнкүлөрдү аткарат:\n\n• Караңгы теманы күйгүзөт\n• Фондогу аракеттерди, айрым визуалдык эффекттерди жана \"Окей Google\" сыяктуу башка функцияларды өчүрөт же чектейт\n\n"<annotation id="url">"Кеңири маалымат"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Батареянын мөөнөтүн узартуу үчүн, Батареяны үнөмдөгүч режими:\n\n• Караңгы теманы күйгүзөт\n• Фондогу аракеттерди, айрым визуалдык эффекттерди жана \"Окей Google\" сыяктуу башка функцияларды өчүрөт же чектейт"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Трафикти үнөмдөө режиминде айрым колдонмолор дайын-даректерди фондо өткөрө алышпайт. Учурда сиз пайдаланып жаткан колдонмо дайын-даректерди жөнөтүп/ала алат, бирок адаттагыдан азыраак өткөргөндүктөн, анын айрым функциялары талаптагыдай иштебей коюшу мүмкүн. Мисалы, сүрөттөр басылмайынча жүктөлбөйт."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикти үнөмдөө режимин иштетесизби?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Күйгүзүү"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"экрандагы башка терезелердин үстүнөн көрсөтүлүүдө"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Пикир билдирүү"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Режимдин адаттагы билдирмеси"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея кубаттоого чейин отуруп калышы мүмкүн"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батареянын отуруп калбашы үчүн Батареяны үнөмдөгүч режими иштетилди"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 641470e7b3f7..c1b3fe473aed 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ກົດ ເມນູ ເພື່ອປົດລັອກ ຫຼື ໂທອອກຫາເບີສຸກເສີນ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ແຕ້ມຮູບແບບເພື່ອປົດລັອກ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ສຸກ​ເສີນ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ກັບໄປຫາການໂທ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ຖືກຕ້ອງ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ລອງໃໝ່ອີກຄັ້ງ"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ຖືກອັບໂຫລດໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ຖືກລຶບອອກໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ຕົກລົງ"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ເພື່ອຍືດອາຍຸແບັດເຕີຣີ, ຕົວປະຢັດແບັດເຕີຣີຈະ:\n\n•ເປີດໃຊ້ຮູບແບບສີສັນມືດ\n•ປິດ ຫຼື ຈຳກັດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກດ້ານພາບບາງຢ່າງ ແລະ ຄຸນສົມບັດອື່ນໆ ເຊັ່ນ: “Ok Google”\n\n"<annotation id="url">"ສຶກສາເພີ່ມເຕີມ"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ເພື່ອຍືດອາຍຸແບັດເຕີຣີ, ຕົວປະຢັດແບັດເຕີຣີຈະ:\n\n•ເປີດໃຊ້ຮູບແບບສີສັນມືດ\n•ປິດ ຫຼື ຈຳກັດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກດ້ານພາບບາງຢ່າງ ແລະ ຄຸນສົມບັດອື່ນໆ ເຊັ່ນ: “Ok Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ເພື່ອຍືດອາຍຸແບັດເຕີຣີ, ຕົວປະຢັດແບັດເຕີຣີຈະ:\n\n• ເປີດໃຊ້ຮູບແບບສີສັນມືດ\n• ປິດ ຫຼື ຈຳກັດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກດ້ານພາບບາງຢ່າງ ແລະ ຄຸນສົມບັດອື່ນໆ ເຊັ່ນ: “Ok Google”\n\n"<annotation id="url">"ສຶກສາເພີ່ມເຕີມ"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ເພື່ອຍືດອາຍຸແບັດເຕີຣີ, ຕົວປະຢັດແບັດເຕີຣີຈະ:\n\n• ເປີດໃຊ້ຮູບແບບສີສັນມືດ\n• ປິດ ຫຼື ຈຳກັດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກດ້ານພາບບາງຢ່າງ ແລະ ຄຸນສົມບັດອື່ນໆ ເຊັ່ນ: “Ok Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ເພື່ອຊ່ວຍຫຼຸດຜ່ອນການນຳໃຊ້ຂໍ້ມູນ, ຕົວປະຢັດອິນເຕີເນັດຈະປ້ອງກັນບໍ່ໃຫ້ບາງແອັບສົ່ງ ຫຼື ຮັບຂໍ້ມູນໃນພື້ນຫຼັງ. ແອັບໃດໜຶ່ງທີ່ທ່ານກຳລັງໃຊ້ຢູ່ຈະສາມາດເຂົ້າເຖິງຂໍ້ມູນໄດ້ ແຕ່ອາດເຂົ້າເຖິງໄດ້ຖີ່ໜ້ອຍລົງ. ນີ້ອາດໝາຍຄວາມວ່າ ຮູບພາບຕ່າງໆອາດບໍ່ສະແດງຈົນກວ່າທ່ານຈະແຕະໃສ່ກ່ອນ."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ເປີດຕົວປະຢັດອິນເຕີເນັດບໍ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ເປີດໃຊ້"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"ກ້ອງ"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"ໄມໂຄຣໂຟນ"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ສະແດງຜົນບັງແອັບອື່ນຢູ່ໜ້າຈໍຂອງທ່ານ"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ສົ່ງຄຳຕິຊົມ"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ການແຈ້ງເຕືອນຂໍ້ມູນໂໝດກິດຈະວັດປະຈຳວັນ"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ແບັດເຕີຣີອາດໝົດກ່ອນການສາກຕາມປົກກະຕິ"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ເປີດຕົວປະຢັດແບັດເຕີຣີເພື່ອຂະຫຍາຍອາຍຸແບັດເຕີຣີ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 88aa9523a9cb..fc5709c0e089 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Paspauskite „Meniu“, kad atrakintumėte ar skambintumėte pagalbos numeriu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Paspauskite „Meniu“, jei norite atrakinti."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Nustatyti modelį, kad atrakintų"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Skambutis pagalbos numeriu"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"grįžti prie skambučio"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Teisingai!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Bandykite dar kartą"</string>
@@ -1838,8 +1839,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atnaujino administratorius"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Ištrynė administratorius"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Gerai"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Kad akumuliatorius veiktų ilgiau, Akumuliatoriaus tausojimo priemonė:\n\n• įjungia tamsiąją temą;\n• išjungia arba apriboja veiklą fone, kai kuriuos vaizdinius efektus ir kitas funkcijas, pvz., „Ok Google“.\n\n"<annotation id="url">"Sužinokite daugiau"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Kad akumuliatorius veiktų ilgiau, Akumuliatoriaus tausojimo priemonė:\n\n• įjungia tamsiąją temą;\n• išjungia arba apriboja veiklą fone, kai kuriuos vaizdinius efektus ir kitas funkcijas, pvz., „Ok Google“."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Kad akumuliatorius veiktų ilgiau, Akumuliatoriaus tausojimo priemonė:\n\n• įjungia tamsiąją temą;\n• išjungia arba apriboja veiklą fone, kai kuriuos vaizdinius efektus ir kitas funkcijas, pvz., „Ok Google“.\n\n"<annotation id="url">"Sužinokite daugiau"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Kad akumuliatorius veiktų ilgiau, Akumuliatoriaus tausojimo priemonė:\n\n• įjungia tamsiąją temą;\n• išjungia arba apriboja veiklą fone, kai kuriuos vaizdinius efektus ir kitas funkcijas, pvz., „Ok Google“."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Kad padėtų sumažinti duomenų naudojimą, Duomenų taupymo priemonė neleidžia kai kurioms programoms siųsti ar gauti duomenų fone. Šiuo metu naudojama programa gali pasiekti duomenis, bet tai bus daroma rečiau. Tai gali reikšti, kad, pvz., vaizdai nebus pateikiami, jei jų nepaliesite."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Įj. Duomenų taupymo priemonę?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Įjungti"</string>
@@ -2063,8 +2064,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Fotoaparatas"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofonas"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"rodo virš kitų programų jūsų ekrane"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Pateikti atsiliepimą"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Veiksmų sekos režimo informacijos pranešimas"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Akumuliatoriaus energija gali išsekti prieš įprastą įkrovimą"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Akumuliatoriaus tausojimo priemonė suaktyvinta, kad akumuliatorius veiktų ilgiau"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index c24c39266f4a..d780b14ae11a 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Nospiediet Izvēlne, lai atbloķētu, vai veiciet ārkārtas zvanu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Zīmējiet kombināciju, lai atbloķētu."</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Ārkārtas situācija"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Atpakaļ pie zvana"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Pareizi!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Mēģināt vēlreiz"</string>
@@ -1815,8 +1816,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atjaunināja administrators"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Dzēsa administrators"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Labi"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Lai paildzinātu akumulatora darbību, akumulatora jaudas taupīšanas režīmā tiek veiktas tālāk norādītās darbības.\n\n• Tiek ieslēgts tumšais motīvs.\n• Tiek izslēgtas vai ierobežotas darbības fonā, noteikti vizuālie efekti un citas funkcijas, piemēram, “Ok Google”.\n\n"<annotation id="url">"Uzzināt vairāk"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Lai paildzinātu akumulatora darbību, akumulatora jaudas taupīšanas režīmā tiek veiktas tālāk norādītās darbības.\n\n• Tiek ieslēgts tumšais motīvs.\n• Tiek izslēgtas vai ierobežotas darbības fonā, noteikti vizuālie efekti un citas funkcijas, piemēram, “Ok Google”."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Lai paildzinātu akumulatora darbības laiku, ieslēdzot akumulatora jaudas taupīšanas režīmu, tiek veiktas tālāk norādītās darbības.\n\n• Tiek ieslēgts tumšais motīvs.\n• Tiek izslēgtas vai ierobežotas fonā veiktās darbības, daži vizuālie efekti un citas funkcijas, piemēram, “Ok Google”.\n\n"<annotation id="url">"Uzzināt vairāk"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Lai paildzinātu akumulatora darbības laiku, ieslēdzot akumulatora jaudas taupīšanas režīmu, tiek veiktas tālāk norādītās darbības.\n\n• Tiek ieslēgts tumšais motīvs.\n• Tiek izslēgtas vai ierobežotas fonā veiktās darbības, daži vizuālie efekti un citas funkcijas, piemēram, “Ok Google”."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Lai samazinātu datu lietojumu, datu lietojuma samazinātājs neļauj dažām lietotnēm fonā nosūtīt vai saņemt datus. Lietotne, kuru pašlaik izmantojat, var piekļūt datiem, bet, iespējams, piekļūs tiem retāk (piemēram, attēli tiks parādīti tikai tad, kad tiem pieskarsieties)."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vai ieslēgt datu lietojuma samazinātāju?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ieslēgt"</string>
@@ -2030,8 +2031,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofons"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"rāda pāri citām lietotnēm jūsu ekrānā"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Sniegt atsauksmes"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Informatīvs paziņojums par akumulatoru"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Akumulators var izlādēties pirms parastā uzlādes laika"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Aktivizēts akumulatora jaudas taupīšanas režīms, lai palielinātu akumulatora darbības ilgumu"</string>
diff --git a/packages/SystemUI/res/layout/tv_pip_custom_control.xml b/core/res/res/values-mcc260/config.xml
index dd0fce466576..79eefb7cecbe 100644
--- a/packages/SystemUI/res/layout/tv_pip_custom_control.xml
+++ b/core/res/res/values-mcc260/config.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-**
-** Copyright 2017, The Android Open Source Project
+/*
+** Copyright 2020, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -16,8 +16,10 @@
** limitations under the License.
*/
-->
-<com.android.systemui.pip.tv.PipControlButtonView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" />
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+ <!-- Set to false to disable emergency alert. -->
+ <bool name="config_cellBroadcastAppLinks">false</bool>
+</resources> \ No newline at end of file
diff --git a/core/res/res/values-mcc262/config.xml b/core/res/res/values-mcc262/config.xml
new file mode 100644
index 000000000000..79eefb7cecbe
--- /dev/null
+++ b/core/res/res/values-mcc262/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+ <!-- Set to false to disable emergency alert. -->
+ <bool name="config_cellBroadcastAppLinks">false</bool>
+</resources> \ No newline at end of file
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 4360bdf38669..b50c608190e4 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисни „Мени“ да се отклучи или да направи итен повик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притиснете „Мени“ за да се отклучи."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Употребете ја шемата за да се отклучи"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Итен случај"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Врати се на повик"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Точно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Обидете се повторно"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"ИСКЛУЧЕНО"</string>
<string name="checked" msgid="9179896827054513119">"штиклирано"</string>
<string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Заврши дејство со"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши го дејството"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Отвори со"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирано од администраторот"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Избришано од администраторот"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Во ред"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"За да го продолжи траењето на батеријата, „Штедачот на батерија“:\n\n•вклучува темна тема;\n•исклучува или ограничува активност во заднина, некои визуелни ефекти и други функции како „Ok Google“.\n\n"<annotation id="url">"Дознајте повеќе"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"За да го продолжи траењето на батеријата, „Штедачот на батерија“:\n\n•вклучува темна тема;\n•исклучува или ограничува активност во заднина, некои визуелни ефекти и други функции како „Ok Google“."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"За да го продолжи траењето на батеријата, „Штедачот на батерија“:\n\n• вклучува темна тема\n• исклучува или ограничува активност во заднина, некои визуелни ефекти и други функции како „Hey Google“\n\n"<annotation id="url">"Дознајте повеќе"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"За да го продолжи траењето на батеријата, „Штедачот на батерија“:\n\n• вклучува темна тема\n• исклучува или ограничува активност во заднина, некои визуелни ефекти и други функции како „Hey Google“"</string>
<string name="data_saver_description" msgid="4995164271550590517">"За да се намали користењето интернет, „Штедачот на интернет“ спречува дел од апликациите да испраќаат или да примаат податоци во заднина. Одредена апликација што ја користите ќе може да користи интернет, но можеби тоа ќе го прави поретко. Ова значи, на пример, дека сликите нема да се прикажуваат додека не ги допрете."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Да се вклучи „Штедач на интернет“?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Вклучи"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"се прикажува преку други апликации на вашиот екран"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Испратете повратни информации"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Известување за информации за режимот за рутини"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батеријата може да се потроши пред вообичаеното време за полнење"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Активиран е „Штедачот на батерија“ за да се продолжи траењето на батеријата"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 00d1b663cfa1..b769fd23ab1e 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"അൺലോക്ക് ചെയ്യുന്നതിനായി മെനു അമർത്തുക അല്ലെങ്കിൽ അടിയന്തര കോൾ വിളിക്കുക."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"അൺലോക്ക് ചെയ്യാൻ പാറ്റേൺ വരയ്‌ക്കുക"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"എമർജൻസി"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"കോളിലേക്ക് മടങ്ങുക"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ശരി!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"വീണ്ടും ശ്രമിക്കുക"</string>
@@ -1547,7 +1548,7 @@
<string name="sending" msgid="206925243621664438">"അയയ്‌ക്കുന്നു…"</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"ബ്രൗസർ സമാരംഭിക്കണോ?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"കോൾ സ്വീകരിക്കണോ?"</string>
- <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്പ്പോഴും"</string>
+ <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്‌പ്പോഴും"</string>
<string name="activity_resolver_use_once" msgid="948462794469672658">"ഒരിക്കൽ മാത്രം"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s, ഔദ്യോഗിക പ്രൊഫൈലിനെ പിന്തുണയ്‌ക്കുന്നില്ല"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"ടാബ്‌ലെറ്റ്"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"നിങ്ങളുടെ അഡ്‌മിൻ അപ്‌ഡേറ്റ് ചെയ്യുന്നത്"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"നിങ്ങളുടെ അഡ്‌മിൻ ഇല്ലാതാക്കുന്നത്"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ശരി"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ബാറ്ററി ലെെഫ് വികസിപ്പിക്കാൻ, \'ബാറ്ററി ലാഭിക്കൽ\':\n\n•ഡാർക്ക് തീം ഓണാക്കും\n•പശ്ചാത്തല പ്രവർത്തനം, ചില വിഷ്വൽ ഇഫക്റ്റുകൾ, “ഹേയ് Google” പോലുള്ള മറ്റ് ഫീച്ചറുകൾ എന്നിവ ഓഫാക്കുകയോ നിയന്ത്രിക്കുകയോ ചെയ്യും\n\n"<annotation id="url">"കൂടുതലറിയുക"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ബാറ്ററി ലെെഫ് വികസിപ്പിക്കാൻ, \'ബാറ്ററി ലാഭിക്കൽ\':\n\n•ഡാർക്ക് തീം ഓണാക്കും\n•പശ്ചാത്തല പ്രവർത്തനം, ചില വിഷ്വൽ ഇഫക്റ്റുകൾ, “ഹേയ് Google” പോലുള്ള മറ്റ് ഫീച്ചറുകൾ എന്നിവ ഓഫാക്കുകയോ നിയന്ത്രിക്കുകയോ ചെയ്യും"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ബാറ്ററി ലെെഫ് വർദ്ധിപ്പിക്കാൻ, \'ബാറ്ററി ലാഭിക്കൽ\' ഇനിപ്പറയുന്നവ ചെയ്യുന്നു:\n\n•ഡാർക്ക് തീം ഓണാക്കുന്നു\n•പശ്ചാത്തല ആക്‌റ്റിവിറ്റി, ചില വിഷ്വൽ ഇഫക്റ്റുകൾ, “Ok Google” പോലുള്ള മറ്റ് ഫീച്ചറുകൾ എന്നിവ ഓഫാക്കുകയോ നിയന്ത്രിക്കുകയോ ചെയ്യന്നു\n\n"<annotation id="url">"കൂടുതലറിയുക"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ബാറ്ററി ലെെഫ് വർദ്ധിപ്പിക്കാൻ, \'ബാറ്ററി ലാഭിക്കൽ\' ഇനിപ്പറയുന്നവ ചെയ്യുന്നു:\n\n• ഡാർക്ക് തീം ഓണാക്കുന്നു\n• പശ്ചാത്തല ആക്റ്റിവിറ്റി, ചില വിഷ്വൽ ഇഫക്റ്റുകൾ, “Ok Google” പോലുള്ള മറ്റ് ഫീച്ചറുകൾ എന്നിവ ഓഫാക്കുകയോ നിയന്ത്രിക്കുകയോ ചെയ്യുന്നു"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ഡാറ്റാ ഉപയോഗം കുറയ്ക്കാൻ സഹായിക്കുന്നതിനായി പശ്ചാത്തലത്തിൽ ഡാറ്റ അയയ്ക്കുകയോ സ്വീകരിക്കുകയോ ചെയ്യുന്നതിൽ നിന്ന് ചില ആപ്പുകളെ ഡാറ്റാ സേവർ തടയുന്നു. നിങ്ങൾ നിലവിൽ ഉപയോഗിക്കുന്ന ഒരു ആപ്പിന് ഡാറ്റ ആക്‌സസ് ചെയ്യാനാകും, എന്നാൽ വല്ലപ്പോഴും മാത്രമെ സംഭവിക്കുന്നുള്ളു. ഇതിനർത്ഥം, ഉദാഹരണമായി നിങ്ങൾ ടാപ്പ് ചെയ്യുന്നത് വരെ ചിത്രങ്ങൾ പ്രദ‍‍‍ർശിപ്പിക്കുകയില്ല എന്നാണ്."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ഡാറ്റ സേവർ ഓണാക്കണോ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ഓണാക്കുക"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"ക്യാമറ"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"മൈക്രോഫോൺ"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"നിങ്ങളുടെ സ്‌ക്രീനിലെ മറ്റ് ആപ്പുകൾക്ക് മുകളിൽ പ്രദർശിപ്പിക്കുന്നു"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ഫീഡ്‌ബാക്ക് നൽകുക"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ദിനചര്യ മോഡ് വിവരത്തെ കുറിച്ചുള്ള അറിയിപ്പ്"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"സാധാരണയുള്ളതിലും നേരത്തെ ബാറ്ററിയുടെ ചാർജ് തീർന്നേക്കാം"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ബാറ്ററി ലൈഫ് വര്‍ദ്ധിപ്പിക്കാൻ, ബാറ്ററി ലാഭിക്കൽ സജീവമാക്കി"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 9a4152e97572..7b41501e6a0f 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Яаралтай дуудлага хийх буюу эсвэл түгжээг тайлах бол цэсийг дарна уу."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Тайлах бол цэсийг дарна уу."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Тайлах хээгээ зурна уу"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Яаралтай тусламж"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Дуудлагаруу буцах"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Зөв!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дахин оролдох"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Таны админ шинэчилсэн"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Таны админ устгасан"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Батарейн ажиллах хугацааг уртасгахын тулд Батарей хэмнэгч:\n\n•Бараан загварыг асаана\n•Арын үйл ажиллагаа, зарим визуал эффект болон “Hey Google” зэрэг бусад онцлогийг унтрааж эсвэл хязгаарлана\n\n"<annotation id="url">"Нэмэлт мэдээлэл авах"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Батарейн ажиллах хугацааг уртасгахын тулд Батарей хэмнэгч:\n\n•Бараан загварыг асаана\n•Арын үйл ажиллагаа, зарим визуал эффект болон “Hey Google” зэрэг бусад онцлогийг унтрааж эсвэл хязгаарлана"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батарейн ажиллах хугацааг уртасгахын тулд Батарей хэмнэгч:\n\n•Бараан загварыг асаадаг\n•Арын үйл ажиллагаа, зарим визуал эффект болон “Hey Google” зэрэг бусад онцлогийг унтрааж эсвэл хязгаарладаг\n\n"<annotation id="url">"Нэмэлт мэдээлэл авах"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Батарей хэмнэгч нь батарейн ажиллах хугацааг уртасгахын тулд:\n\n• Бараан загварыг асаадаг\n• Арын үйл ажиллагаа, зарим визуал эффект болон “Hey Google” зэрэг бусад онцлогийг унтрааж эсвэл хязгаарладаг"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Дата ашиглалтыг багасгахын тулд дата хэмнэгч нь ар талд ажиллаж буй зарим апп-н өгөгдлийг илгээх болон авахаас сэргийлдэг. Таны одоогийн ашиглаж буй апп нь өгөгдөлд хандах боломжтой хэдий ч тогтмол хандахгүй. Энэ нь жишээлбэл зургийг товших хүртэл харагдахгүй гэсэн үг юм."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Дата хэмнэгчийг асаах уу?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Асаах"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камер"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"таны дэлгэцэд бусад аппын дээр харуулж байна"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Санал хүсэлт өгөх"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Хэвшлийн горимын мэдээллийн мэдэгдэл"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарей ихэвчлэн цэнэглэдэг хугацаанаас өмнө дуусаж болзошгүй"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батарейн ажиллах хугацааг уртасгахын тулд Батарей хэмнэгчийг идэвхжүүллээ"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index d304ba5639c9..577803334c33 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलॉक करण्‍यासाठी मेनू दाबा किंवा आणीबाणीचा कॉल करा."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलॉक करण्यासाठी मेनू दाबा."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलॉक करण्यासाठी पॅटर्न काढा"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आणीबाणी"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कॉलवर परत या"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"अचूक!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"पुन्हा प्रयत्न करा"</string>
@@ -1651,7 +1652,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"रंगांची उलटापालट"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रंग सुधारणा"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केला आहे."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> वापरण्यासाठी दोन्ही व्हॉल्युम की तीन सेकंद दाबा आणि धरून ठेवा"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"तुम्ही अ‍ॅक्सेसिबिलिटी बटणावर टॅप केल्यास वापरण्यासाठी एक वैशिष्ट्य निवडा:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"अ‍ॅक्सेसिबिलिटी जेश्चरसोबत वापराचे असलेले वैशिष्‍ट्य निवडा (दोन बोटांनी स्क्रीनच्या तळापासून वर स्वाइप करा):"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"आपल्या प्रशासकाने अपडेट केले"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"आपल्या प्रशासकाने हटवले"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ओके"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"बॅटरीचे आयुष्य वाढवण्यासाठी बॅटरी सेव्हर:\n\n•गडद थीम सुरू करते\n•बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट आणि \"Ok Google\" यांसारखी वैशिष्ट्ये बंद किंवा मर्यादित करते.\n\n"<annotation id="url">"अधिक जाणून घ्या"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"बॅटरीचे आयुष्य वाढवण्यासाठी बॅटरी सेव्हर:\n\n•गडद थीम सुरू करते\n•बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट आणि \"Ok Google\" यांसारखी वैशिष्ट्ये बंद किंवा मर्यादित करते."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"बॅटरी लाइफ वाढवण्यासाठी बॅटरी सेव्हर:\n\n•गडद थीम सुरू करते\n•बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट आणि \"Ok Google\" यांसारखी वैशिष्ट्ये बंद किंवा मर्यादित करते.\n\n"<annotation id="url">"अधिक जाणून घ्या"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"बॅटरी लाइफ वाढवण्यासाठी बॅटरी सेव्हर:\n\n• गडद थीम सुरू करते\n• बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट आणि “Ok Google” यांसारखी इतर वैशिष्ट्ये बंद किंवा मर्यादित करते"</string>
<string name="data_saver_description" msgid="4995164271550590517">"डेटाचा वापर कमी करण्यात मदत करण्यासाठी काही अ‍ॅप्सना बॅकग्राउंडमध्ये डेटा पाठवण्यास किंवा मिळवण्यास डेटा सर्व्हर प्रतिबंध करतो. तुम्ही सध्या वापरत असलेले अ‍ॅप डेटा अ‍ॅक्सेस करू शकते, पण तसे खूप कमी वेळा होते. याचाच अर्थ असा की, तुम्ही इमेजवर टॅप करेपर्यंत त्या डिस्प्ले होणार नाहीत असे होऊ शकते."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा सेव्हर सुरू करायचे?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"सुरू करा"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"कॅमेरा"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"मायक्रोफोन"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"तुमच्‍या स्‍क्रीनवर इतर ॲप्‍सवर डिस्‍प्‍ले करत आहे"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"फीडबॅक द्या"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"दिनक्रम मोडची माहिती सूचना"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"चार्जिंगची सामान्य पातळी गाठेपर्यंत कदाचित बॅटरी संपू शकते"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"बॅटरी लाइफ वाढवण्यासाठी बॅटरी सेव्हर सुरू केला आहे"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index bdcaa7aa2807..c30bcfc1800f 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tekan Menu untuk menyahsekat atau membuat panggilan kecemasan."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tekan Menu untuk membuka kunci."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Lukiskan corak untuk membuka kunci"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Kecemasan"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kembali ke panggilan"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Betul!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Cuba lagi"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Dikemas kini oleh pentadbir anda"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Dipadamkan oleh pentadbir anda"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Untuk melanjutkan hayat bateri, Penjimat Bateri:\n\n•Menghidupkan Tema gelap\n•Mematikan atau mengehadkan aktiviti latar belakang, sesetengah kesan visual dan ciri lain seperti “Ok Google”\n\n"<annotation id="url">"Ketahui lebih lanjut"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Untuk melanjutkan hayat bateri, Penjimat Bateri:\n\n•Menghidupkan Tema gelap\n•Mematikan atau mengehadkan aktiviti latar belakang, sesetengah kesan visual dan ciri lain seperti “Ok Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Untuk melanjutkan hayat bateri, Penjimat Bateri:\n\n• Menghidupkan Tema gelap\n• Mematikan atau mengehadkan aktiviti latar belakang, sesetengah kesan visual dan ciri lain seperti “Ok Google”\n\n"<annotation id="url">"Ketahui lebih lanjut"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Untuk melanjutkan hayat bateri, Penjimat Bateri:\n\n•Menghidupkan Tema gelap\n• Mematikan atau mengehadkan aktiviti latar belakang, sesetengah kesan visual dan ciri-ciri lain seperti “Ok Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Untuk membantu mengurangkan penggunaan data, Penjimat Data menghalang sesetengah apl daripada menghantar atau menerima data di latar. Apl yang sedang digunakan boleh mengakses data tetapi mungkin tidak secara kerap. Perkara ini mungkin bermaksud bahawa imej tidak dipaparkan sehingga anda mengetik pada imej itu, contohnya."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Hidupkan Penjimat Data?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Hidupkan"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"dipaparkan di atas apl lain pada skrin anda"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Berikan Maklum Balas"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Pemberitahuan maklumat Mod Rutin"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Bateri mungkin habis sebelum pengecasan biasa"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Penjimat Bateri diaktifkan untuk memanjangkan hayat bateri"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 6f77d1f4e2d2..243357eca3c2 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ဖွင့်ရန်သို့မဟုတ်အရေးပေါ်ခေါ်ဆိုခြင်းပြုလုပ်ရန် မီနူးကိုနှိပ်ပါ"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"မီးနူးကို နှိပ်ခြင်းဖြင့် သော့ဖွင့်ပါ"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ဖွင့်ရန်ပုံစံဆွဲပါ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"အရေးပေါ်"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ခေါ်ဆိုမှုထံပြန်သွားရန်"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"မှန်ပါသည်"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ထပ် စမ်းပါ"</string>
@@ -1102,7 +1103,7 @@
<string name="delete" msgid="1514113991712129054">"ဖျက်ရန်"</string>
<string name="copyUrl" msgid="6229645005987260230">"URLအား ကူးခြင်း"</string>
<string name="selectTextMode" msgid="3225108910999318778">"စာသား ရွေးရန်"</string>
- <string name="undo" msgid="3175318090002654673">"ပြန်ဖျက်ရန်"</string>
+ <string name="undo" msgid="3175318090002654673">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
<string name="redo" msgid="7231448494008532233">"ထပ်လုပ်ပါ"</string>
<string name="autofill" msgid="511224882647795296">"အော်တိုဖြည့်"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"စာတိုရွေးချယ်မှု"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"ပိတ်"</string>
<string name="checked" msgid="9179896827054513119">"အမှန်ခြစ်ပြီး"</string>
<string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
- <string name="whichApplication" msgid="5432266899591255759">"အသုံးပြု၍ ဆောင်ရွက်မှုအားပြီးဆုံးစေခြင်း"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"လုပ်ဆောင်ချက်ကို အပြီးသတ်ပါ"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"...ဖြင့် ဖွင့်မည်"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"သင်၏ စီမံခန့်ခွဲသူက အပ်ဒိတ်လုပ်ထားသည်"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"သင်၏ စီမံခန့်ခွဲသူက ဖျက်လိုက်ပါပြီ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ဘက်ထရီသက်တမ်း ပိုရှည်စေရန် \'ဘက်ထရီအားထိန်း\' က- \n\n•မှောင်သည့် အပြင်အဆင်ကို ဖွင့်သည်\n•နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့နှင့် “Ok Google” ကဲ့သို့ အခြား ဝန်ဆောင်မှုများကို ပိတ်သည် သို့မဟုတ် ကန့်သတ်သည်\n\n"<annotation id="url">"ပိုမိုလေ့လာရန်"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ဘက်ထရီသက်တမ်း ပိုရှည်စေရန် \'ဘက်ထရီအားထိန်း\' က- \n\n•မှောင်သည့် အပြင်အဆင်ကို ဖွင့်သည်\n•နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့နှင့် “Ok Google” ကဲ့သို့ အခြား ဝန်ဆောင်မှုများကို ပိတ်သည် သို့မဟုတ် ကန့်သတ်သည်"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ဘက်ထရီသက်တမ်း ပိုရှည်စေရန် \'ဘက်ထရီအားထိန်း\' က- \n\n• မှောင်သည့် အပြင်အဆင်ကို ဖွင့်သည်\n• နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့နှင့် “Ok Google” ကဲ့သို့ အခြား ဝန်ဆောင်မှုများကို ပိတ်သည် သို့မဟုတ် ကန့်သတ်သည်\n\n"<annotation id="url">"ပိုမိုလေ့လာရန်"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ဘက်ထရီသက်တမ်း ပိုရှည်စေရန် \'ဘက်ထရီအားထိန်း\' က-\n\n•မှောင်သည့် အပြင်အဆင်ကို ဖွင့်သည်\n•နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့နှင့် “Ok Google” ကဲ့သို့ အခြား ဝန်ဆောင်မှုများကို ပိတ်သည် သို့မဟုတ် ကန့်သတ်သည်"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ဒေတာအသုံးလျှော့ချနိုင်ရန်အတွက် အက်ပ်များကို နောက်ခံတွင် ဒေတာပို့ခြင်းနှင့် လက်ခံခြင်းမပြုရန် \'ဒေတာချွေတာမှု\' စနစ်က တားဆီးထားပါသည်။ ယခုအက်ပ်ဖြင့် ဒေတာအသုံးပြုနိုင်သော်လည်း အကြိမ်လျှော့၍သုံးရပါမည်။ ဥပမာ၊ သင်က မတို့မချင်း ပုံများပေါ်လာမည် မဟုတ်ပါ။"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ဒေတာချွေတာမှုစနစ် ဖွင့်မလား။"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ဖွင့်ပါ"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"ကင်မရာ"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"မိုက်ခရိုဖုန်း"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"သင့်မျက်နှာပြင်ပေါ်ရှိ အခြားအက်ပ်များပေါ်တွင် ပြသခြင်း"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"အကြံပြုချက် ပေးရန်"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ပုံမှန်မုဒ်အတွက် အချက်အလက်ပြသည့် အကြောင်းကြားချက်"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ပုံမှန်အားသွင်းမှုမပြုလုပ်မီ ဘက်ထရီကုန်သွားနိုင်သည်"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ဘက်ထရီသက်တမ်းကို တိုးမြှင့်ရန် \'ဘက်ထရီအားထိန်း\' စတင်ပြီးပါပြီ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ecf58d8bb1be..0a31b49c555c 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Trykk på menyknappen for å låse opp eller ringe et nødnummer."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Trykk på menyknappen for å låse opp."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Tegn mønster for å låse opp"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nødssituasjon"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tilbake til samtale"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Riktig!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Prøv på nytt"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Oppdatert av administratoren din"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Slettet av administratoren din"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"For å forlenge batterilevetiden gjør Batterisparing dette:\n\n• Slår på mørkt tema\n• Slår av eller begrenser bakgrunnsaktivitet, enkelte visuelle effekter og andre funksjoner, for eksempel «Hey Google»\n\n"<annotation id="url">"Finn ut mer"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"For å forlenge batterilevetiden gjør Batterisparing dette:\n\n• Slår på mørkt tema\n• Slår av eller begrenser bakgrunnsaktivitet, enkelte visuelle effekter og andre funksjoner, for eksempel «Hey Google»"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"For å forlenge batterilevetiden gjør Batterisparing dette:\n\n• Slår på mørkt tema\n• Slår av eller begrenser bakgrunnsaktivitet, enkelte visuelle effekter og andre funksjoner, for eksempel «Hey Google»\n\n"<annotation id="url">"Finn ut mer"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"For å forlenge batterilevetiden gjør Batterisparing dette:\n\n• Slår på mørkt tema\n• Slår av eller begrenser bakgrunnsaktivitet, enkelte visuelle effekter og andre funksjoner, for eksempel «Hey Google»"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Datasparing hindrer noen apper fra å sende og motta data i bakgrunnen, for å redusere dataforbruket. Aktive apper kan bruke data, men kanskje ikke så mye som ellers – for eksempel vises ikke bilder før du trykker på dem."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vil du slå på Datasparing?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Slå på"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"vises over andre apper på skjermen"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Gi tilbakemelding"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Varsel med informasjon om rutinemodus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batteriet kan gå tomt før den vanlige ladingen"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterisparing er aktivert for å forlenge batterilevetiden"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index c5e317a61d08..9712b480b97b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलक गर्नु ढाँचा खिच्नुहोस्"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आपतकालीन"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कलमा फर्किनुहोस्"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string>
@@ -1126,14 +1127,10 @@
<string name="checked" msgid="9179896827054513119">"जाँच गरिएको"</string>
<string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string>
<string name="whichApplication" msgid="5432266899591255759">"प्रयोग गरेर कारबाही पुरा गर्नुहोस्"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for whichApplicationNamed (6969946041713975681) -->
- <skip />
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"निम्न एपको प्रयोग गरी कारबाही पुरा गर्नुहोस्: %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"पूर्ण कारबाही"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"निम्नबाट खोल्नुहोस्"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for whichViewApplicationNamed (415164730629690105) -->
- <skip />
+ <string name="whichViewApplicationNamed" msgid="415164730629690105">"निम्न एपमा खोल्नुहोस्: %1$s"</string>
<string name="whichViewApplicationLabel" msgid="7367556735684742409">"खोल्नुहोस्"</string>
<string name="whichOpenHostLinksWith" msgid="7645631470199397485">"निम्नमार्फत <xliff:g id="HOST">%1$s</xliff:g> का लिंकहरू खोल्नुहोस्"</string>
<string name="whichOpenLinksWith" msgid="1120936181362907258">"निम्नमार्फत लिंकहरू खोल्नुहोस्"</string>
@@ -1796,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"तपाईंका प्रशासकले अद्यावधिक गर्नुभएको"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"तपाईंका प्रशासकले मेट्नुभएको"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ठिक छ"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ब्याट्रीको आयु बढाउन ब्याट्री सेभरले:\n\n•अँध्यारो थिम सक्रिय गर्छ\n•पृष्ठभूमिका गतिविधि, केही दृश्यात्मक प्रभाव तथा “Hey Google” जस्ता अन्य सुविधाहरू निष्क्रिय वा सीमित पार्छ\n\n"<annotation id="url">"थप जान्नुहोस्"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ब्याट्रीको आयु बढाउन ब्याट्री सेभरले:\n\n•अँध्यारो थिम सक्रिय गर्छ\n•पृष्ठभूमिका गतिविधि, केही दृश्यात्मक प्रभाव तथा “Hey Google” जस्ता अन्य सुविधाहरू निष्क्रिय वा सीमित पार्छ"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ब्याट्री सेभरले यन्त्रको ब्याट्री बढी समय टिकाउन:\n\n•अँध्यारो थिम सक्रिय गर्छ\n•पृष्ठभूमिका गतिविधि, केही दृश्यात्मक प्रभाव तथा “Hey Google” जस्ता अन्य सुविधाहरू निष्क्रिय वा सीमित पार्छ\n\n"<annotation id="url">"थप जान्नुहोस्"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ब्याट्री सेभरले यन्त्रको ब्याट्री बढी समय टिकाउन:\n\n• अँध्यारो थिम अन गर्छ\n• पृष्ठभूमिका क्रियाकलाप, केही दृश्यात्मक प्रभाव तथा “Hey Google” जस्ता अन्य सुविधाहरू अफ गर्छ वा सीमित पार्छ"</string>
<string name="data_saver_description" msgid="4995164271550590517">"डेटाको प्रयोगलाई कम गर्न डेटा सर्भरले केही एपलाई पृष्ठभूमिमा डेटा पठाउन वा प्राप्त गर्न दिँदैन। तपाईंले हाल प्रयोग गरिरहनुभएको अनु्प्रयोगले डेटा चलाउन सक्छ, तर पहिला भन्दा कम अन्तरालमा मात्र। उदाहरणका लागि, तपाईले छविहरूमा ट्याप नगरेसम्म ती छविहरू देखिँदैनन्।"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा सेभर सक्रिय गर्ने हो?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"सक्रिय गर्नुहोस्"</string>
@@ -2001,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"क्यामेरा"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"माइक्रोफोन"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"तपाईंको स्क्रिनका अन्य एपहरूमा प्रदर्शन गरिँदै छ"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"प्रतिक्रिया दिनुहोस्"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"दिनचर्या मोडको जानकारीमूलक सूचना"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"प्रायः चार्ज गर्ने समय हुनुभन्दा पहिले नै ब्याट्री सकिन सक्छ"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ब्याट्रीको आयु बढाउन ब्याट्री सेभर सक्रिय गरियो"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index bd324ab28828..d1a342c99590 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Druk op \'Menu\' om te ontgrendelen of noodoproep te plaatsen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Druk op \'Menu\' om te ontgrendelen."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Patroon tekenen om te ontgrendelen"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Noodgeval"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Terug naar gesprek"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Juist!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Opnieuw proberen"</string>
@@ -1786,14 +1787,14 @@
<string name="managed_profile_label_badge_2" msgid="5673187309555352550">"2e <xliff:g id="LABEL">%1$s</xliff:g>, werk"</string>
<string name="managed_profile_label_badge_3" msgid="6882151970556391957">"3e <xliff:g id="LABEL">%1$s</xliff:g>, werk"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Vraag pin voor losmaken"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Vraag patroon voor losmaken"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Vraag om ontgrendelingspatroon voor losmaken"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Vraag wachtwoord voor losmaken"</string>
<string name="package_installed_device_owner" msgid="7035926868974878525">"Geïnstalleerd door je beheerder"</string>
<string name="package_updated_device_owner" msgid="7560272363805506941">"Geüpdatet door je beheerder"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Verwijderd door je beheerder"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Batterijbesparing doet het volgende om de batterijduur te verlengen:\n\n•Het donkere thema aanzetten\n•Achtergrondactiviteit, bepaalde visuele effecten en andere functies (zoals \'Hey Google\') uitzetten of beperken\n\n"<annotation id="url">"Meer informatie"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Batterijbesparing doet het volgende om de batterijduur te verlengen:\n\n•Het donkere thema aanzetten\n•Achtergrondactiviteit, bepaalde visuele effecten en andere functies (zoals \'Hey Google\') uitzetten of beperken"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Batterijbesparing doet het volgende om de batterijduur te verlengen:\n\n• Het donkere thema inschakelen\n• Achtergrondactiviteit, bepaalde visuele effecten en andere functies (zoals \'Hey Google\') uitschakelen of beperken\n\n"<annotation id="url">"Meer informatie"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Batterijbesparing doet het volgende om de batterijduur te verlengen:\n\n• Het donkere thema inschakelen.\n• Achtergrondactiviteit, bepaalde visuele effecten en andere functies (zoals \'Hey Google\') uitschakelen of beperken."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Databesparing beperkt het datagebruik door te voorkomen dat sommige apps gegevens verzenden of ontvangen op de achtergrond. De apps die je open hebt, kunnen nog steeds data verbruiken, maar doen dit minder vaak. Afbeeldingen worden dan bijvoorbeeld niet weergegeven totdat je erop tikt."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Databesparing aanzetten?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Inschakelen"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Camera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microfoon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"wordt weergegeven vóór andere apps op je scherm"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Feedback geven"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Informatiemelding voor routinemodus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"De batterij raakt mogelijk leeg voordat deze normaal gesproken wordt opgeladen"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterijbesparing is geactiveerd om de batterijduur te verlengen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 8f1196bbe8f0..84815f7a6133 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ କିମ୍ବା ଜରୁରୀକାଳୀନ କଲ୍‌ କରନ୍ତୁ।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ଅନଲକ୍‌ କରିବା ପାଇଁ ପାଟର୍ନ ଆଙ୍କନ୍ତୁ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ଜରୁରୀକାଳୀନ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"କଲ୍‌କୁ ଫେରନ୍ତୁ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ଠିକ୍!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
@@ -1310,7 +1311,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"ଆନାଲଗ୍‍ ଅଡିଓ ଆକ୍ସେସରୀ ଚିହ୍ନଟ ହେଲା"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"ଏହି ଫୋନ୍‌ରେ କନେକ୍ଟ ଥିବା ଡିଭାଇସ୍‍ କମ୍ପାଟିବଲ୍‍ ନୁହେଁ। ଅଧିକ ଜାଣିବା ପାଇଁ ଟାପ୍‌ କରନ୍ତୁ।"</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"USB ଡିବଗିଂ ସଂଯୁକ୍ତ ହୋଇଛି"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"USB ଡିବଗିଂ ସୁବିଧାକୁ ବନ୍ଦ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"USB ଡିବଗିଂକୁ ବନ୍ଦ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB ଡିବଗିଙ୍ଗକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଚୟନ କରନ୍ତୁ।"</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"ୱାୟାରଲେସ୍ ଡିବଗିଂ ସଂଯୋଗ କରାଯାଇଛି"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"ୱାୟାରଲେସ୍ ଡିବଗିଂକୁ ବନ୍ଦ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ଆପଣଙ୍କ ଆଡମିନ୍‌‌ ଅପଡେଟ୍‍ କରିଛନ୍ତି"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ଆପଣଙ୍କ ଆଡମିନ୍‌‌ ଡିଲିଟ୍‍ କରିଛନ୍ତି"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ଠିକ୍ ଅଛି"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ବ୍ୟାଟେରୀ ଲାଇଫ୍ ବଢ଼ାଇବାକୁ ବ୍ୟାଟେରୀ ସେଭର୍:\n\n•ଗାଢ଼ା ଥିମ୍ ଚାଲୁ କରେ\n•ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ପ୍ରଭାବ ଏବଂ “Hey Google” ପରି ଅନ୍ୟ ଫିଚରଗୁଡ଼ିକୁ ବନ୍ଦ କିମ୍ବା ପ୍ରତିବନ୍ଧିତ କରିଥାଏ\n\n"<annotation id="url">"ଅଧିକ ଜାଣନ୍ତୁ"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ବ୍ୟାଟେରୀ ଲାଇଫ୍ ବଢ଼ାଇବାକୁ ବ୍ୟାଟେରୀ ସେଭର୍:\n\n•ଗାଢ଼ା ଥିମ୍ ଚାଲୁ କରେ\n•ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ପ୍ରଭାବ ଏବଂ “Hey Google” ପରି ଅନ୍ୟ ଫିଚରଗୁଡ଼ିକୁ ବନ୍ଦ କିମ୍ବା ପ୍ରତିବନ୍ଧିତ କରିଥାଏ"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ବ୍ୟାଟେରୀ ଲାଇଫ୍ ବଢ଼ାଇବାକୁ ବ୍ୟାଟେରୀ ସେଭର୍:\n\n•ଗାଢ଼ା ଥିମ୍ ଚାଲୁ କରେ\n•ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ପ୍ରଭାବ ଏବଂ “Hey Google” ପରି ଅନ୍ୟ ଫିଚରଗୁଡ଼ିକୁ ବନ୍ଦ କିମ୍ବା ପ୍ରତିବନ୍ଧିତ କରିଥାଏ\n\n"<annotation id="url">"ଅଧିକ ଜାଣନ୍ତୁ"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ବ୍ୟାଟେରୀ ଲାଇଫ୍ ବଢ଼ାଇବାକୁ ବ୍ୟାଟେରୀ ସେଭର୍:\n\n• ଗାଢ଼ା ଥିମ୍ ଚାଲୁ କରେ\n• ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ପ୍ରଭାବ ଏବଂ “Hey Google” ପରି ଅନ୍ୟ ଫିଚରଗୁଡ଼ିକୁ ବନ୍ଦ କିମ୍ବା ପ୍ରତିବନ୍ଧିତ କରିଥାଏ"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ଡାଟା ବ୍ୟବହାର କମ୍‍ କରିବାରେ ସାହାଯ୍ୟ କରିବାକୁ, ଡାଟା ସେଭର୍‍ ବ୍ୟାକ୍‌ଗ୍ରାଉଣ୍ଡରେ ଡାଟା ପଠାଇବା କିମ୍ବା ପ୍ରାପ୍ତ କରିବାକୁ କିଛି ଆପ୍‍କୁ ବାରଣ କରେ। ଆପଣ ବର୍ତ୍ତମାନ ବ୍ୟବହାର କରୁଥିବା ଆପ୍‍, ଡାଟା ଆକ୍ସେସ୍‍ କରିପାରେ, କିନ୍ତୁ ଏହା କମ୍‍ ଥର କରିପାରେ। ଏହାର ଅର୍ଥ ହୋଇପାରେ ଯେମିତି ଆପଣ ଇମେଜଗୁଡ଼ିକୁ ଟାପ୍‍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ସେଗୁଡ଼ିକ ଡିସପ୍ଲେ ହୁଏ ନାହିଁ।"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ଡାଟା ସେଭର୍‌ ଚାଲୁ କରିବେ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ଚାଲୁ କରନ୍ତୁ"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"କ୍ୟାମେରା"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"ମାଇକ୍ରୋଫୋନ୍"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍ ଉପରେ ଥିବା ଅନ୍ୟ ଆପ୍‌ ଉପରେ ଦେଖାଦେବ"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ମତାମତ ଦିଅନ୍ତୁ"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ନିୟମିତ ମୋଡ୍‍ ସୂଚନା ବିଜ୍ଞପ୍ତି"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ସାଧାରଣ ଭାବରେ ଚାର୍ଜ୍ କରିବା ପୂର୍ବରୁ ବ୍ୟାଟେରୀ ସରିଯାଇପାରେ"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ବ୍ୟାଟେରୀର ସମୟକୁ ବଢ଼ାଇବା ପାଇଁ ବ୍ୟଟେରୀ ସେଭର୍‍କୁ କାର୍ଯ୍ୟକାରୀ କରାଯାଇଛି"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index a3a6fb1120e3..c29308fd4c5b 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ ਜਾਂ ਸੰਕਟਕਾਲੀਨ ਕਾਲ ਕਰੋ।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪੈਟਰਨ ਡ੍ਰਾ ਕਰੋ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ਸੰਕਟਕਾਲ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ਕਾਲ ਤੇ ਵਾਪਸ ਜਾਓ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ਸਹੀ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਮਿਟਾਇਆ ਗਿਆ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ਠੀਕ ਹੈ"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"ਬੈਟਰੀ ਲਾਈਫ਼ ਵਧਾਉਣ ਲਈ, ਬੈਟਰੀ ਸੇਵਰ:\n\n•ਗੂੜ੍ਹਾ ਥੀਮ ਚਾਲੂ ਕਰਦਾ ਹੈ\n•ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ, ਅਤੇ \"Ok Google\" ਵਰਗੀਆਂ ਹੋਰ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਬੰਦ ਕਰਦਾ ਹੈ ਜਾਂ ਉਹਨਾਂ \'ਤੇ ਪਾਬੰਦੀ ਲਗਾਉਂਦਾ ਹੈ\n\n"<annotation id="url">"ਹੋਰ ਜਾਣੋ"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"ਬੈਟਰੀ ਲਾਈਫ਼ ਵਧਾਉਣ ਲਈ, ਬੈਟਰੀ ਸੇਵਰ:\n\n•ਗੂੜ੍ਹਾ ਥੀਮ ਚਾਲੂ ਕਰਦਾ ਹੈ\n•ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ, ਅਤੇ \"Ok Google\" ਵਰਗੀਆਂ ਹੋਰ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਬੰਦ ਕਰਦਾ ਹੈ ਜਾਂ ਉਹਨਾਂ \'ਤੇ ਪਾਬੰਦੀ ਲਗਾਉਂਦਾ ਹੈ"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ਬੈਟਰੀ ਲਾਈਫ਼ ਵਧਾਉਣ ਲਈ, ਬੈਟਰੀ ਸੇਵਰ:\n\n• ਗੂੜ੍ਹਾ ਥੀਮ ਚਾਲੂ ਕਰਦਾ ਹੈ\n• ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ, ਅਤੇ \"Ok Google\" ਵਰਗੀਆਂ ਹੋਰ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਬੰਦ ਕਰਦਾ ਹੈ ਜਾਂ ਉਹਨਾਂ \'ਤੇ ਪਾਬੰਦੀ ਲਗਾਉਂਦਾ ਹੈ\n\n"<annotation id="url">"ਹੋਰ ਜਾਣੋ"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"ਬੈਟਰੀ ਲਾਈਫ਼ ਵਧਾਉਣ ਲਈ, ਬੈਟਰੀ ਸੇਵਰ:\n\n• ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ\n• ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ, ਅਤੇ \"Ok Google\" ਵਰਗੀਆਂ ਹੋਰ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਬੰਦ ਕਰਦਾ ਹੈ ਜਾਂ ਉਹਨਾਂ \'ਤੇ ਪਾਬੰਦੀ ਲਗਾਉਂਦਾ ਹੈ"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ਡਾਟਾ ਵਰਤੋਂ ਘਟਾਉਣ ਵਿੱਚ ਮਦਦ ਲਈ, ਡਾਟਾ ਸੇਵਰ ਕੁਝ ਐਪਾਂ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡਾਟਾ ਭੇਜਣ ਜਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਤੋਂ ਰੋਕਦਾ ਹੈ। ਤੁਹਾਡੇ ਵੱਲੋਂ ਵਰਤਮਾਨ ਤੌਰ \'ਤੇ ਵਰਤੀ ਜਾ ਰਹੀ ਐਪ ਡਾਟਾ \'ਤੇ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ, ਪਰ ਉਹ ਇੰਝ ਕਦੇ-ਕਦਾਈਂ ਕਰ ਸਕਦੀ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਸ ਦਾ ਮਤਲਬ ਇਹ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਚਿੱਤਰ ਤਦ ਤੱਕ ਨਹੀਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਤੇ ਜਾਂਦੇ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ \'ਤੇ ਟੈਪ ਨਹੀਂ ਕਰਦੇ।"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ਕੀ ਡਾਟਾ ਸੇਵਰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ਚਾਲੂ ਕਰੋ"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"ਕੈਮਰਾ"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਹੋਰਾਂ ਐਪਾਂ ਉੱਪਰ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ਵਿਚਾਰ ਦਿਓ"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ਨਿਯਮਬੱਧ ਮੋਡ ਦੀ ਜਾਣਕਾਰੀ ਵਾਲੀ ਸੂਚਨਾ"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ਬੈਟਰੀ ਚਾਰਜ ਕਰਨ ਦੇ ਮਿੱਥੇ ਸਮੇਂ ਤੋਂ ਪਹਿਲਾਂ ਸ਼ਾਇਦ ਬੈਟਰੀ ਖਤਮ ਹੋ ਜਾਵੇ"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ਬੈਟਰੀ ਲਾਈਫ਼ ਵਧਾਉਣ ਲਈ ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index a88bd94a2ec2..df56457da65a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Naciśnij Menu, aby odblokować lub wykonać połączenie alarmowe."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Naciśnij Menu, aby odblokować."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Narysuj wzór, aby odblokować"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Alarmowe"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Powrót do połączenia"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Poprawnie!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Spróbuj ponownie."</string>
@@ -1838,8 +1839,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Zaktualizowany przez administratora"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Usunięty przez administratora"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Aby wydłużyć czas pracy na baterii, funkcja Oszczędzanie baterii:\n\n•włącza tryb ciemny\n•wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”\n\n"<annotation id="url">"Więcej informacji"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Aby wydłużyć czas pracy na baterii, funkcja Oszczędzanie baterii:\n\n•włącza tryb ciemny\n•wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Aby wydłużyć czas pracy na baterii, funkcja Oszczędzanie baterii:\n\n•włącza tryb ciemny,\n• wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”.\n\n"<annotation id="url">"Więcej informacji"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Aby wydłużyć czas pracy na baterii, Oszczędzanie baterii:\n\n• włącza tryb ciemny,\n• wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Oszczędzanie danych uniemożliwia niektórym aplikacjom wysyłanie i odbieranie danych w tle, zmniejszając w ten sposób ich użycie. Aplikacja, z której w tej chwili korzystasz, może uzyskiwać dostęp do danych, ale rzadziej. Może to powodować, że obrazy będą się wyświetlać dopiero po kliknięciu."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Włączyć Oszczędzanie danych?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Włącz"</string>
@@ -2063,8 +2064,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Aparat"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"wyświetla się nad innymi aplikacjami na ekranie"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Prześlij opinię"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Powiadomienie z informacją o trybie rutynowym"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Bateria może się wyczerpać przed zwykłą porą ładowania"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Włączono Oszczędzanie baterii, by wydłużyć czas pracy na baterii"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 1d7c4894a968..3ecf28d34ffd 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pressione Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhe o padrão para desbloquear"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retornar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
@@ -1651,7 +1652,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"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="8343213623338605305">"Escolha um recurso a ser usado quando você toca no botão de acessibilidade:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Escolha um recurso para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Excluído pelo seu administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Para prolongar a duração da carga, a \"Economia de bateria\":\n\n•ativa o tema escuro;\n•desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos, como o \"Ok Google\".\n\n"<annotation id="url">"Saiba mais"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Para prolongar a duração da carga, a \"Economia de bateria\":\n\n•ativa o tema escuro;\n•desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos, como o \"Ok Google\"."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Para prolongar a duração da carga, a \"Economia de bateria\":\n\n• ativa o tema escuro;\n• desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos, como o \"Ok Google\".\n\n"<annotation id="url">"Saiba mais"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Para prolongar a duração da carga, a \"Economia de bateria\":\n\n• ativa o tema escuro;\n• desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos, como o \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não sejam exibidas até que você toque nelas."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar \"Economia de dados\"?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Câmera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microfone"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"exibindo sobre outros apps na sua tela"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Enviar feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 444bf0abf920..dcc8e17609ac 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -286,7 +286,7 @@
<string name="notification_channel_retail_mode" msgid="3732239154256431213">"Demonstração para retalho"</string>
<string name="notification_channel_usb" msgid="1528280969406244896">"Ligação USB"</string>
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicação em execução"</string>
- <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicações que estão a consumir bateria"</string>
+ <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão a consumir bateria"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a consumir bateria."</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicações estão a consumir bateria."</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Prima Menu para desbloquear ou efectuar uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Prima Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhar padrão para desbloquear"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Regressar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tentar novamente"</string>
@@ -1153,7 +1154,7 @@
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Capturar imagem"</string>
<string name="alwaysUse" msgid="3153558199076112903">"Utilizar por predefinição para esta ação."</string>
<string name="use_a_different_app" msgid="4987790276170972776">"Utilizar outra app"</string>
- <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Limpar a predefinição nas Definições do Sistema &gt; Aplicações &gt; Transferidas."</string>
+ <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Limpar a predefinição nas Definições do Sistema &gt; Apps &gt; Transferidas."</string>
<string name="chooseActivity" msgid="8563390197659779956">"Escolha uma ação"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"Escolher uma app para o dispositivo USB"</string>
<string name="noApplications" msgid="1186909265235544019">"Nenhuma app pode efetuar esta ação."</string>
@@ -1181,7 +1182,7 @@
<string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> foi originalmente iniciado."</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"Escala"</string>
<string name="screen_compat_mode_show" msgid="5080361367584709857">"Mostrar sempre"</string>
- <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reative este modo nas Definições do Sistema &gt; Aplicações &gt; Transferidas."</string>
+ <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reative este modo nas Definições do Sistema &gt; Apps &gt; Transferidas."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> não suporta a definição de Tamanho do ecrã atual e pode ter um comportamento inesperado."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"Mostrar sempre"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> foi concebida para uma versão incompatível do SO Android e pode ter um comportamento inesperado. Pode estar disponível uma versão atualizada da app."</string>
@@ -1274,7 +1275,7 @@
<string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Enviar"</string>
<string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Cancelar"</string>
<string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Memorizar a minha escolha"</string>
- <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Pode alterar mais tarde em Definições &gt; Aplicações"</string>
+ <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Pode alterar mais tarde em Definições &gt; Apps"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Permitir Sempre"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nunca Permitir"</string>
<string name="sim_removed_title" msgid="5387212933992546283">"Cartão SIM removido"</string>
@@ -1284,8 +1285,8 @@
<string name="sim_added_message" msgid="6602906609509958680">"Reinicie o aparelho para aceder à rede de telemóvel."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Reiniciar"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar o serviço móvel"</string>
- <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Transfira a app do operador para ativar o seu novo SIM."</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Transfira a app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar o novo SIM."</string>
+ <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Descarregue a app do operador para ativar o seu novo SIM."</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Descarregue a app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar o novo SIM."</string>
<string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Transferir app"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo SIM inserido"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
@@ -1336,7 +1337,7 @@
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Toque para selecionar o idioma e o esquema"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
- <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Sobrepor a outras aplicações"</string>
+ <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Sobrepor a outras apps"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"A app <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras aplicações"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"O <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras app"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"Se não pretende que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu gestor"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado pelo seu gestor"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Para prolongar a autonomia da bateria, a Poupança de bateria:\n\n•Ativa o tema escuro.\n•Desativa ou restringe a atividade em segundo plano, alguns efeitos visuais e outras funcionalidades como \"Ok Google\".\n\n"<annotation id="url">"Saber mais"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Para prolongar a autonomia da bateria, a Poupança de bateria:\n\n•Ativa o tema escuro.\n•Desativa ou restringe a atividade em segundo plano, alguns efeitos visuais e outras funcionalidades como \"Ok Google\"."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Para prolongar a autonomia da bateria, a Poupança de bateria:\n\n•Ativa o tema escuro.\n•Desativa ou restringe a atividade em segundo plano, alguns efeitos visuais e outras funcionalidades como \"Ok Google\".\n\n"<annotation id="url">"Saiba mais"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Para prolongar a autonomia da bateria, a Poupança de bateria:\n\n• Ativa o tema escuro.\n•·Desativa ou restringe a atividade em segundo plano, alguns efeitos visuais e outras funcionalidades como \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir a utilização de dados, a Poupança de dados impede que algumas aplicações enviem ou recebam dados em segundo plano. Uma determinada app que esteja a utilizar atualmente pode aceder aos dados, mas é possível que o faça com menos frequência. Isto pode significar, por exemplo, que as imagens não são apresentadas até que toque nas mesmas."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Pretende ativar a Poupança de dados?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Câmara"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microfone"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"sobrepõe-se a outras aplicações no ecrã"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Fornecer feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informações do Modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Pode ficar sem bateria antes do carregamento habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Poupança de bateria ativada para prolongar a duração da bateria"</string>
@@ -2036,7 +2036,7 @@
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Esta app não recebeu autorização de gravação, mas pode capturar áudio através deste dispositivo USB."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Página inicial"</string>
<string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Anterior"</string>
- <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Aplicações recentes"</string>
+ <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Apps recentes"</string>
<string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Notificações"</string>
<string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Definições rápidas"</string>
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Caixa de diálogo de energia"</string>
@@ -2064,7 +2064,7 @@
<string name="resolver_cant_share_with_personal_apps_explanation" msgid="2959282422751315171">"O seu administrador de TI não lhe permite partilhar este conteúdo com apps no seu perfil pessoal."</string>
<string name="resolver_cant_access_personal_apps" msgid="648291604475669395">"Não é possível abrir este conteúdo com apps pessoais"</string>
<string name="resolver_cant_access_personal_apps_explanation" msgid="2298773629302296519">"O seu administrador de TI não lhe permite abrir este conteúdo com apps no seu perfil pessoal."</string>
- <string name="resolver_turn_on_work_apps" msgid="884910835250037247">"Perfil de trabalho em pausa."</string>
+ <string name="resolver_turn_on_work_apps" msgid="884910835250037247">"Perfil de trabalho em pausa"</string>
<string name="resolver_switch_on_work" msgid="2873009160846966379">"Ativar"</string>
<string name="resolver_no_work_apps_available_share" msgid="7933949011797699505">"Este conteúdo não é suportado por nenhuma app de trabalho."</string>
<string name="resolver_no_work_apps_available_resolve" msgid="1244844292366099399">"Este conteúdo não pode ser aberto por nenhuma app de trabalho."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 1d7c4894a968..3ecf28d34ffd 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pressione Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhe o padrão para desbloquear"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retornar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
@@ -1651,7 +1652,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"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="8343213623338605305">"Escolha um recurso a ser usado quando você toca no botão de acessibilidade:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Escolha um recurso para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Excluído pelo seu administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Para prolongar a duração da carga, a \"Economia de bateria\":\n\n•ativa o tema escuro;\n•desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos, como o \"Ok Google\".\n\n"<annotation id="url">"Saiba mais"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Para prolongar a duração da carga, a \"Economia de bateria\":\n\n•ativa o tema escuro;\n•desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos, como o \"Ok Google\"."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Para prolongar a duração da carga, a \"Economia de bateria\":\n\n• ativa o tema escuro;\n• desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos, como o \"Ok Google\".\n\n"<annotation id="url">"Saiba mais"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Para prolongar a duração da carga, a \"Economia de bateria\":\n\n• ativa o tema escuro;\n• desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos, como o \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não sejam exibidas até que você toque nelas."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar \"Economia de dados\"?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Câmera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microfone"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"exibindo sobre outros apps na sua tela"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Enviar feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index af03d47bcec9..64936689ee6e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Apăsați Meniu pentru deblocare."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenați modelul pentru a debloca"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgență"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Reveniți la apel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corect!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Încercați din nou"</string>
@@ -1815,8 +1816,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizat de administratorul dvs."</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Șters de administratorul dvs."</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Pentru a mări autonomia bateriei, Economisirea bateriei:\n\n•·activează tema întunecată;\n•·dezactivează sau restricționează activitatea în fundal, unele efecte vizuale și alte funcții, cum ar fi „Ok Google”.\n\n"<annotation id="url">"Aflați mai multe"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Pentru a mări autonomia bateriei, Economisirea bateriei:\n\n• activează tema întunecată;\n• dezactivează sau restricționează activitatea în fundal, unele efecte vizuale și alte funcții, cum ar fi „Ok Google”."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Pentru a mări autonomia bateriei, Economisirea bateriei:\n\n•·activează tema întunecată;\n•·dezactivează sau restricționează activitatea în fundal, unele efecte vizuale și alte funcții, cum ar fi „Ok Google”.\n\n"<annotation id="url">"Aflați mai multe"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Pentru a mări autonomia bateriei, Economisirea bateriei:\n\n• activează tema întunecată;\n• dezactivează sau restricționează activitatea în fundal, unele efecte vizuale și alte funcții, cum ar fi „Ok Google”."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Pentru a contribui la reducerea utilizării de date, Economizorul de date împiedică unele aplicații să trimită sau să primească date în fundal. O aplicație pe care o folosiți poate accesa datele, însă mai rar. Aceasta poate însemna, de exemplu, că imaginile se afișează numai după ce le atingeți."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activați Economizorul de date?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activați"</string>
@@ -2030,8 +2031,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Cameră foto"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microfon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"se afișează peste alte aplicații de pe ecran"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Oferiți feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificare pentru informații despre modul Rutină"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Bateria se poate descărca înainte de încărcarea obișnuită"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Economisirea bateriei este activată pentru a prelungi durata de funcționare a bateriei"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 497fa4c4f0fb..07abf6dbb28d 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Нажмите \"Меню\", чтобы разблокировать экран или вызвать службу экстренной помощи."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Для разблокировки нажмите \"Меню\"."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Введите графический ключ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Экстренный вызов"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Вернуться к вызову"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Повторите попытку"</string>
@@ -1352,7 +1353,7 @@
<string name="adb_active_notification_title" msgid="408390247354560331">"Отладка по USB разрешена"</string>
<string name="adb_active_notification_message" msgid="5617264033476778211">"Нажмите, чтобы отключить отладку по USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Нажмите, чтобы отключить отладку по USB."</string>
- <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Отладка по Wi-Fi активна"</string>
+ <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Отладка по Wi-Fi включена"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Нажмите, чтобы отключить отладку по Wi-Fi."</string>
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Нажмите, чтобы отключить отладку по Wi-Fi."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Тестовый режим включен"</string>
@@ -1667,7 +1668,7 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Использовать быстрое включение?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Чтобы использовать функцию специальных возможностей, когда она включена, нажмите и удерживайте обе кнопки регулировки громкости в течение трех секунд."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Включить специальные возможности?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Чтобы включить специальные возможности, нажмите обе кнопки регулировки громкости на несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nТекущие функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nЧтобы изменить выбранные функции, перейдите в настройки и нажмите \"Специальные возможности\"."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Чтобы включить специальные возможности, нажмите обе кнопки регулировки громкости и удерживайте несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nТекущие функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nЧтобы изменить выбранные функции, перейдите в настройки и нажмите \"Специальные возможности\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Включить функцию \"<xliff:g id="SERVICE">%1$s</xliff:g>\"?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Чтобы включить функцию \"<xliff:g id="SERVICE">%1$s</xliff:g>\", нажмите обе кнопки регулировки громкости на несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nЧтобы назначить это сочетание клавиш другой функции, перейдите в настройки и выберите \"Специальные возможности\"."</string>
@@ -1838,8 +1839,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Обновлено администратором"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Удалено администратором"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Чтобы продлить время работы от батареи, в режиме энергосбережения:\n\n• включается тёмная тема;\n• отключаются или ограничиваются фоновые процессы, некоторые визуальные эффекты и другие функции (например, распознавание команды \"Окей, Google\").\n\n"<annotation id="url">"Подробнее…"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Чтобы продлить время работы от батареи, в режиме энергосбережения:\n\n• включается тёмная тема;\n• отключаются или ограничиваются фоновые процессы, некоторые визуальные эффекты и другие функции (например, распознавание команды \"Окей, Google\")."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Чтобы продлить время работы от батареи, в режиме энергосбережения:\n\n• включается тёмная тема;\n• отключаются или ограничиваются фоновые процессы, некоторые визуальные эффекты и другие функции (например, распознавание команды \"Окей, Google\").\n\n"<annotation id="url">"Подробнее…"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Чтобы продлить время работы от батареи, в режиме энергосбережения:\n\n• включается тёмная тема;\n• отключаются или ограничиваются фоновые процессы, некоторые визуальные эффекты и другие функции (например, распознавание команды \"Окей, Google\")."</string>
<string name="data_saver_description" msgid="4995164271550590517">"В режиме экономии трафика фоновая передача данных для некоторых приложений отключена. Приложение, которым вы пользуетесь, может получать и отправлять данные, но реже, чем обычно. Например, изображения могут не загружаться, пока вы не нажмете на них."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Включить экономию трафика?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Включить"</string>
@@ -2063,8 +2064,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"показ поверх других окон"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Отправить отзыв"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Уведомление о батарее"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея может разрядиться"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Чтобы увеличить время работы от батареи, был включен режим энергосбережения."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 7a07a90055ea..d494a36520bf 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"අගුළු හැරීමට මෙනුව ඔබන්න හෝ හදිසි ඇමතුම ලබාගන්න."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"අගුළු හැරීමට මෙනු ඔබන්න."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"අගුළු ඇරීමට රටාව අඳින්න"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"හදිසි"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ඇමතුම වෙත නැවත යන්න"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"නිවැරදියි!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"නැවත උත්සාහ කරන්න"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"ඔබගේ පරිපාලක මඟින් යාවත්කාලීන කර ඇත"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ඔබගේ පරිපාලක මඟින් මකා දමා ඇත"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"හරි"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"බැටරියේ ජීව කාලය දික් කිරීමට, බැටරි සුරැකුම:\n\n•අඳුරු තේමාව ක්‍රියාත්මක කරයි\n•පසුබිමේ ක්‍රියාකාරකම, සමහර දෘෂ්‍ය ප්‍රයෝග සහ “Hey Google” වැනි වෙනත් විශේෂාංග ක්‍රියාවිරහිත කරයි නැතහොත් අවහිර කරයි\n\n"<annotation id="url">"තව දැන ගන්න"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"බැටරියේ ජීව කාලය දික් කිරීමට, බැටරි සුරැකුම:\n\n•අඳුරු තේමාව ක්‍රියාත්මක කරයි\n•පසුබිමේ ක්‍රියාකාරකම, සමහර දෘෂ්‍ය ප්‍රයෝග සහ “Hey Google” වැනි වෙනත් විශේෂාංග ක්‍රියාවිරහිත කරයි නැතහොත් අවහිර කරයි"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"බැටරියේ ජීව කාලය දික් කිරීමට, බැටරි සුරැකුම:\n\n•අඳුරු තේමාව ක්‍රියාත්මක කරයි\n•පසුබිමේ ක්‍රියාකාරකම, සමහර දෘශ්‍ය ප්‍රයෝග සහ “Hey Google” වැනි වෙනත් විශේෂාංග ක්‍රියාවිරහිත කරයි නැතහොත් අවහිර කරයි\n\n"<annotation id="url">"තව දැන ගන්න"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"බැටරියේ ජීව කාලය දික් කිරීමට, බැටරි සුරැකුම:\n\n•අඳුරු තේමාව ක්‍රියාත්මක කරයි\n•පසුබිමේ ක්‍රියාකාරකම, සමහර දෘශ්‍ය ප්‍රයෝග සහ “Hey Google” වැනි වෙනත් විශේෂාංග ක්‍රියාවිරහිත කරයි නැතහොත් අවහිර කරයි"</string>
<string name="data_saver_description" msgid="4995164271550590517">"දත්ත භාවිතය අඩු කිරීමට උදවු වීමට, දත්ත සුරැකුම සමහර යෙදුම් පසුබිමින් දත්ත යැවීම සහ ලබා ගැනීම වළක්වයි. ඔබ දැනට භාවිත කරන යෙදුමකට දත්ත වෙත පිවිසීමට හැකිය, නමුත් එසේ කරන්නේ කලාතුරකින් විය හැකිය. මෙයින් අදහස් වන්නේ, උදාහරණයක් ලෙස, එම රූප ඔබ ඒවාට තට්ටු කරන තෙක් සංදර්ශනය නොවන බවය."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"දත්ත සුරැකුම ක්‍රියාත්මක කරන්නද?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ක්‍රියාත්මක කරන්න"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"කැමරාව"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"මයික්‍රෆෝනය"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ඔබගේ තිරය මත වෙනත් යෙදුම්වලට උඩින් සංදර්ශනය කරමින්"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ප්‍රතිපෝෂණ සපයන්න"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"දිනචරියා ප්‍රකාර තතු දැනුම්දීම"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"බැටරිය සුපුරුදු ආරෝපණයට පෙර ඉවර විය හැක"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"බැටරි සුරැකුම බැටරි ආයු කාලය දීර්ඝ කිරීමට සක්‍රිය කෙරිණි"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index af35f7fb544f..52c53015743b 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ak chcete odomknúť telefón alebo uskutočniť tiesňové volanie, stlačte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Telefón odomknete stlačením tlačidla Menu."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Odomknite nakreslením vzoru"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Stav tiesne"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zavolať späť"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Správne!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Skúsiť znova"</string>
@@ -1838,8 +1839,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Aktualizoval správca"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Odstránil správca"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Šetrič batérie predlžuje výdrž batérie:\n\n• zapnutím tmavého motívu;\n•vypnutím alebo obmedzením aktivity na pozadí, niektorých vizuálnych efektov a ďalších funkcií, ako napríklad kľúčového slova „Hey Google“.\n\n"<annotation id="url">"Ďalšie informácie"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Šetrič batérie predlžuje výdrž batérie:\n\n• zapnutím tmavého motívu;\n• vypnutím alebo obmedzením aktivity na pozadí, niektorých vizuálnych efektov a ďalších funkcií, ako napríklad kľúčového slova „Hey Google“."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Šetrič batérie predlžuje výdrž batérie:\n\n• zapnutím tmavého motívu;\n• vypnutím alebo obmedzením aktivity na pozadí, niektorých vizuálnych efektov a ďalších funkcií, ako napríklad kľúčového výrazu „Hey Google“.\n\n"<annotation id="url">"Ďalšie informácie"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Šetrič batérie predlžuje výdrž batérie:\n\n• zapnutím tmavého motívu;\n• vypnutím alebo obmedzením aktivity na pozadí, niektorých vizuálnych efektov a ďalších funkcií, ako napríklad kľúčového výrazu „Hey Google“."</string>
<string name="data_saver_description" msgid="4995164271550590517">"S cieľom znížiť spotrebu dát bráni šetrič dát niektorým aplikáciám odosielať alebo prijímať dáta na pozadí. Aplikácia, ktorú práve používate, môže využívať dáta, ale možno to bude robiť menej často. Znamená to napríklad, že sa nezobrazia obrázky, kým na ne neklepnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Chcete zapnúť šetrič dát?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Zapnúť"</string>
@@ -2063,8 +2064,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Fotoaparát"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofón"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"sa zobrazuje cez ďalšie aplikácie na obrazovke"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Poskytnúť spätnú väzbu"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Upozornenie s informáciami o rutinnom režime"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batéria sa môže vybiť pred obvyklým nabitím"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Bol aktivovaný šetrič batérie na predĺženie výdrže batérie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 968b5909bd55..a43c900fb585 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Če želite odkleniti napravo ali opraviti klic v sili, pritisnite meni."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Če želite odkleniti, pritisnite meni."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Če želite odkleniti, narišite vzorec"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Klic v sili"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazaj na klic"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Pravilno."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Poskusi znova"</string>
@@ -1838,8 +1839,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Posodobil skrbnik"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisal skrbnik"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"V redu"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Funkcija varčevanja z energijo baterije podaljša čas delovanja baterije tako:\n\n•Vklopi temno temo,\n•izklopi ali omeji izvajanje dejavnosti v ozadju, nekaterih vizualnih učinkov in drugih funkcij, kot je »Hey Google«.\n\n"<annotation id="url">"Več o tem"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Funkcija varčevanja z energijo baterije podaljša čas delovanja baterije tako:\n\n•Vklopi temno temo,\n•izklopi ali omeji izvajanje dejavnosti v ozadju, nekaterih vizualnih učinkov in drugih funkcij, kot je »Hey Google«."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Funkcija varčevanja z energijo baterije podaljša čas delovanja baterije tako:\n\n•vklopi temno temo,\n•izklopi ali omeji dejavnost v ozadju, nekatere vizualne učinke in druge funkcije, kot je »Hey Google«.\n\n"<annotation id="url">"Več o tem"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Funkcija varčevanja z energijo baterije podaljša čas delovanja baterije tako:\n\n• vklopi temno temo;\n• izklopi ali omeji dejavnost v ozadju, nekatere vizualne učinke in druge funkcije, kot je »Hey Google«."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Zaradi zmanjševanja prenesene količine podatkov funkcija varčevanja s podatki nekaterim aplikacijam preprečuje, da bi v ozadju pošiljale ali prejemale podatke. Aplikacija, ki jo trenutno uporabljate, lahko prenaša podatke, vendar to morda počne manj pogosto. To na primer pomeni, da se slike ne prikažejo, dokler se jih ne dotaknete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vklop varčevanja s podatki?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Vklop"</string>
@@ -2063,8 +2064,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Fotoaparat"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"prekriva druge aplikacije na zaslonu"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Pošlji povratne informacije"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Rutinsko informativno obvestilo o načinu delovanja"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterija se bo morda izpraznila, preden jo običajno priključite na polnjenje"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Vklopilo se je varčevanje z energijo baterije za podaljšanje časa delovanja baterije"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 52b7d9e246e7..625962fb9bd1 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Shtyp \"Meny\" për të shkyçur ose për të kryer telefonatë urgjence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Shtyp \"Meny\" për të shkyçur."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Vizato modelin për ta shkyçur"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgjenca"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kthehu te telefonata"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Saktë!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Provo sërish"</string>
@@ -1314,7 +1315,7 @@
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Përzgjidhe për të çaktivizuar korrigjimin e gabimeve të USB-së"</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Korrigjimi përmes Wi-Fi është lidhur"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Trokit për të çaktivizuar korrigjimin përmes Wi-Fi"</string>
- <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Zgjidh për të çaktivizuar korrigjimin përmes Wi-Fi"</string>
+ <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Zgjidh për të çaktivizuar korrigjimin përmes Wi-Fi."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modaliteti i lidhjes së testimit është aktivizuar"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Kryej një rivendosje në cilësimet e fabrikës për të çaktivizuar \"Modalitetin e lidhjes së testimit\"."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Paneli komandues i serisë është aktivizuar"</string>
@@ -1651,7 +1652,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kthimi i ngjyrës"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korrigjimi i ngjyrës"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i çaktivizuar."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Shtyp dhe mbaj shtypur të dy butonat e volumit për tre sekonda për të përdorur <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Zgjidh një funksion për ta përdorur kur troket butonin e qasshmërisë:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Zgjidh një veçori për ta përdorur me gjestin e qasshmërisë (rrëshqit shpejt lart nga fundi i ekranit me dy gishta):"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Përditësuar nga administratori"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Fshirë nga administratori"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Në rregull"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Për të rritur kohëzgjatjen e baterisë, \"Kursyesi i baterisë\":\n\n• Aktivizon \"Temën e errët\"\n•Çaktivizon ose kufizon aktivitetin në sfond, disa efekte vizuale dhe veçori të tjera si “Ok Google”\n\n"<annotation id="url">"Mëso më shumë"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Për të rritur kohëzgjatjen e baterisë, \"Kursyesi i baterisë\":\n\n• Aktivizon \"Temën e errët\"\n•Çaktivizon ose kufizon aktivitetin në sfond, disa efekte vizuale dhe veçori të tjera si “Ok Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Për të rritur kohëzgjatjen e baterisë, \"Kursyesi i baterisë\":\n\n• Aktivizon \"Temën e errët\"\n• Çaktivizon ose kufizon aktivitetin në sfond, disa efekte vizuale dhe veçori të tjera si “Ok Google”\n\n"<annotation id="url">"Mëso më shumë"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Për të rritur kohëzgjatjen e baterisë, \"Kursyesi i baterisë\":\n\n• Aktivizon \"Temën e errët\"\n• Çaktivizon ose kufizon aktivitetin në sfond, disa efekte vizuale dhe veçori të tjera si “Ok Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Për të ndihmuar në reduktimin e përdorimit të të dhënave, \"Kursyesi i të dhënave\" pengon që disa aplikacione të dërgojnë apo të marrin të dhëna në sfond. Një aplikacion që po përdor aktualisht mund të ketë qasje te të dhënat, por këtë mund ta bëjë më rrallë. Kjo mund të nënkuptojë, për shembull, se imazhet nuk shfaqen kur troket mbi to."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Të aktivizohet \"Kursyesi i të dhënave\"?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivizo"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofoni"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"po shfaqet mbi aplikacionet e tjera në ekranin tënd"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Jep komentet"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Njoftimi i informacionit të \"Modalitetit rutinë\""</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Bateria mund të mbarojë përpara ngarkimit të zakonshëm"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"\"Kursyesi i baterisë\" u aktivizua për të zgjatur jetëgjatësinë e baterisë"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 72522807dc32..6101dac65f95 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притисните „Мени“ за откључавање."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Унесите шаблон за откључавање"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Хитне службе"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад на позив"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Тачно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Пробајте поново"</string>
@@ -1145,7 +1146,7 @@
<string name="capital_off" msgid="7443704171014626777">"НЕ"</string>
<string name="checked" msgid="9179896827054513119">"означено је"</string>
<string name="not_checked" msgid="7972320087569023342">"није означено"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Довршавање радње помоћу"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Отворите помоћу"</string>
@@ -1179,7 +1180,7 @@
<string name="noApplications" msgid="1186909265235544019">"Ниједна апликација не може да обавља ову радњу."</string>
<string name="aerr_application" msgid="4090916809370389109">"Апликација <xliff:g id="APPLICATION">%1$s</xliff:g> је заустављена"</string>
<string name="aerr_process" msgid="4268018696970966407">"Процес <xliff:g id="PROCESS">%1$s</xliff:g> је заустављен"</string>
- <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> се стално зауставља(ју)"</string>
+ <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> се стално зауставља"</string>
<string name="aerr_process_repeated" msgid="1153152413537954974">"Процес <xliff:g id="PROCESS">%1$s</xliff:g> се стално зауставља"</string>
<string name="aerr_restart" msgid="2789618625210505419">"Поново отвори апликацију"</string>
<string name="aerr_report" msgid="3095644466849299308">"Пошаљите повратне информације"</string>
@@ -1815,8 +1816,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирао је администратор"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Избрисао је администратор"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Потврди"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Да би се продужило трајање батерије, Уштеда батерије:\n\n•укључује тамну тему\n•искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“\n\n"<annotation id="url">"Сазнајте више"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Да би се продужило трајање батерије, Уштеда батерије:\n\n•укључује тамну тему\n•искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“\n\n"<annotation id="url">"Сазнајте више"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Да би се смањила потрошња података, Уштеда података спречава неке апликације да шаљу или примају податке у позадини. Апликација коју тренутно користите може да приступа подацима, али ће то чинити ређе. На пример, слике се неће приказивати док их не додирнете."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Желите да укључите Уштеду података?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Укључи"</string>
@@ -2030,8 +2031,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"приказује се на екрану док користите друге апликације"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Пошаљите повратне информације"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Обавештење о информацијама Рутинског режима"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батерија ће се можда испразнити пре уобичајеног пуњења"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Уштеда батерије је активирана да би се продужило трајање батерије"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index aad996bf87c4..e30bb14384f9 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tryck på Menu för att låsa upp eller ringa nödsamtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tryck på Menu för att låsa upp."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Rita grafiskt lösenord för att låsa upp"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nödsamtal"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tillbaka till samtal"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Försök igen"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Administratören uppdaterade paketet"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratören raderade paketet"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Batterisparläget förlänger batteritiden genom att:\n\n• aktivera mörkt tema\n• inaktivera eller begränsa aktivitet i bakgrunden, vissa visuella effekter och andra funktioner, som ”Hey Google”\n\n"<annotation id="url">"Läs mer"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Batterisparläget förlänger batteritiden genom att:\n\n• aktivera mörkt tema\n• inaktivera eller begränsa aktivitet i bakgrunden, vissa visuella effekter och andra funktioner, som ”Hey Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Batterisparläget förlänger batteritiden genom att\n\n• aktivera mörkt tema\n• inaktivera eller begränsa aktivitet i bakgrunden, vissa visuella effekter och andra funktioner, som ”Hey Google”\n\n"<annotation id="url">"Läs mer"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Batterisparläget förlänger batteritiden genom att\n\n• aktivera Mörkt tema\n• inaktivera eller begränsa aktivitet i bakgrunden, vissa visuella effekter och andra funktioner, som ”Hey Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Med databesparing kan du minska dataanvändningen genom att hindra en del appar från att skicka eller ta emot data i bakgrunden. Appar som du använder kan komma åt data, men det sker kanske inte lika ofta. Detta innebär t.ex. att bilder inte visas förrän du trycker på dem."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vill du aktivera Databesparing?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivera"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"visar över andra appar på mobilen"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Lämna feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Avisering om rutinläge"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batteriet kan ta slut innan du brukar ladda det"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterisparläget har aktiverats för att utöka batteritiden"</string>
@@ -2031,7 +2031,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> filer</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> + <xliff:g id="COUNT_1">%d</xliff:g> fil</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Det finns inga rekommendationer för delning"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Inga rekommenderade personer att dela med"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Applista"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Appen har inte fått inspelningsbehörighet men kan spela in ljud via denna USB-enhet."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Startsida"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 29420f97b7bf..4e4c0f656c9f 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Bonyeza Menyu ili kufungua au kupiga simu ya dharura."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Bonyeza Menyu ili kufungua."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Chora ruwaza ili kufungua"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Dharura"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Rudi kwa kupiga simu"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Sahihi!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Jaribu tena"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Imesasishwa na msimamizi wako"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Imefutwa na msimamizi wako"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Sawa"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Ili kuongeza muda wa matumizi ya betri, Kiokoa Betri:\n\n•Huwasha Mandhari meusi\n•Huzima au kuzuia shughuli za chinichini, baadhi ya madoido yanayoonekana na vipengele vingine kama vile \"Ok Google\"\n\n"<annotation id="url">"Pata maelezo zaidi"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Ili kuongeza muda wa matumizi ya betri, Kiokoa Betri:\n\n•Huwasha Mandhari meusi\n•Huzima au kuzuia shughuli za chinichini, baadhi ya madoido yanayoonekana na vipengele vingine kama vile \"Ok Google\""</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Ili kuongeza muda wa matumizi ya betri, Kiokoa Betri:\n\n• Huwasha Mandhari meusi\n• Huzima au kuzuia shughuli za chinichini, baadhi ya madoido yanayoonekana na vipengele vingine kama vile \"Ok Google\"\n\n"<annotation id="url">"Pata maelezo zaidi"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Ili kuongeza muda wa matumizi ya betri, Kiokoa Betri:\n\n• Huwasha Mandhari meusi\n• Huzima au kudhibiti shughuli za chinichini, baadhi ya madoido yanayoonekana na vipengele vingine kama vile \"Ok Google\""</string>
<string name="data_saver_description" msgid="4995164271550590517">"Ili kusaidia kupunguza matumizi ya data, Kiokoa Data huzuia baadhi ya programu kupokea na kutuma data chinichini. Programu ambayo unatumia sasa inaweza kufikia data, lakini si kila wakati. Kwa mfano, haitaonyesha picha hadi utakapozifungua."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ungependa Kuwasha Kiokoa Data?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Washa"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Maikrofoni"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"inachomoza kwenye programu zingine katika skrini yako"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Toa Maoni"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Arifa ya maelezo ya Hali ya Kawaida"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Huenda betri itakwisha chaji mapema"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Imewasha Kiokoa Betri ili kurefusha muda wa matumizi ya betri"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index d8b36f780498..a617248071e0 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"தடைநீக்க மெனுவை அழுத்தவும் அல்லது அவசர அழைப்பை மேற்கொள்ளவும்."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"திறக்க, மெனுவை அழுத்தவும்."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"திறக்க வடிவத்தை வரையவும்"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"அவசர அழைப்பு"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"அழைப்பிற்குத் திரும்பு"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"சரி!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"மீண்டும் முயற்சிக்கவும்"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"உங்கள் நிர்வாகி புதுப்பித்துள்ளார்"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"உங்கள் நிர்வாகி நீக்கியுள்ளார்"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"சரி"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"பேட்டரி நிலையை நீட்டிப்பதற்காக, பேட்டரி சேமிப்பான்:\n\n•டார்க் தீமினை ஆன் செய்யும்\n•பின்னணி செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், “Hey Google” போன்ற பிற அம்சங்களை ஆஃப் செய்யும் அல்லது கட்டுப்படுத்தும்\n\n"<annotation id="url">"மேலும் அறிக"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"பேட்டரி நிலையை நீட்டிப்பதற்காக, பேட்டரி சேமிப்பான்:\n\n•டார்க் தீமினை ஆன் செய்யும்\n•பின்னணி செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், “Hey Google” போன்ற பிற அம்சங்களை ஆஃப் செய்யும் அல்லது கட்டுப்படுத்தும்"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"பேட்டரி நிலையை நீட்டிப்பதற்காக, பேட்டரி சேமிப்பான்:\n\n• டார்க் தீமினை ஆன் செய்யும்\n• பின்னணி செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், “Ok Google” போன்ற பிற அம்சங்களை ஆஃப் செய்யும் அல்லது கட்டுப்படுத்தும்\n\n"<annotation id="url">"மேலும் அறிக"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"பேட்டரி நிலையை நீட்டிப்பதற்காக, பேட்டரி சேமிப்பான்:\n\n• டார்க் தீமினை ஆன் செய்யும்\n• பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், “Ok Google” போன்ற பிற அம்சங்களை ஆஃப் செய்யும் அல்லது கட்டுப்படுத்தும்"</string>
<string name="data_saver_description" msgid="4995164271550590517">"டேட்டா உபயோகத்தைக் குறைப்பதற்கு உதவ, பின்புலத்தில் டேட்டாவை அனுப்புவது அல்லது பெறுவதிலிருந்து சில ஆப்ஸை டேட்டா சேமிப்பான் தடுக்கும். தற்போது பயன்படுத்தும் ஆப்ஸானது எப்போதாவது டேட்டாவை அணுகலாம். எடுத்துக்காட்டாக, படங்களை நீங்கள் தட்டும் வரை அவை காட்டப்படாது."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"டேட்டா சேமிப்பானை இயக்கவா?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"இயக்கு"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"கேமரா"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"மைக்ரோஃபோன்"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"உங்கள் திரையில் உள்ள பிற ஆப்ஸின் மேல் காட்டுகிறது"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"கருத்து தெரிவியுங்கள்"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"வழக்கமான பேட்டரி சேமிப்பானுக்கான விவர அறிவிப்பு"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"வழக்கமாகச் சார்ஜ் செய்வதற்கு முன்பே பேட்டரி தீர்ந்துபோகக்கூடும்"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"பேட்டரி நிலையை நீட்டிக்க பேட்டரி சேமிப்பான் இயக்கப்பட்டுள்ளது"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 91d7e31464e5..794fec85bcf7 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"అన్‌లాక్ చేయడానికి లేదా అత్యవసర కాల్ చేయడానికి మెను నొక్కండి."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"అన్‌లాక్ చేయడానికి మెను నొక్కండి."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"అన్‌లాక్ చేయడానికి నమూనాను గీయండి"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"అత్యవసరం"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"కాల్‌కు తిరిగి వెళ్లు"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"సరైనది!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"మళ్లీ ప్రయత్నించండి"</string>
@@ -1622,8 +1623,8 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"వాల్యూమ్‌ను సిఫార్సు చేయబడిన స్థాయి కంటే ఎక్కువగా పెంచాలా?\n\nసుదీర్ఘ వ్యవధుల పాటు అధిక వాల్యూమ్‌లో వినడం వలన మీ వినికిడి శక్తి దెబ్బ తినవచ్చు."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"యాక్సెస్ సామర్థ్యం షార్ట్‌కట్‌ను ఉపయోగించాలా?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"షార్ట్‌కట్ ఆన్ చేసి ఉన్నప్పుడు, రెండు వాల్యూమ్ బటన్‌లను 3 సెకన్ల పాటు నొక్కి ఉంచితే యాక్సెస్ సౌలభ్య ఫీచర్ ప్రారంభం అవుతుంది."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"యాక్సెసిబిలిటీ‌లను ఆన్ చేయాలా?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"రెండు వాల్యూమ్ కీలను కొంత సేపు నొక్కి పట్టుకుంటే యాక్సెసిబిలిటీలు ఆన్ అవుతాయి. ఇది మీ పరికరం పనిచేసే విధానాన్ని మార్చవచ్చు.\n\nప్రస్తుత ఫీచర్లు:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nఎంపిక చేసిన ఫీచర్లను మీరు సెట్టింగ్‌లు&gt;యాక్సెసిబిలిటీలో మార్చవచ్చు."</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"యాక్సెసిబిలిటీ‌ ఫీచ‌ర్‌ల‌ను ఆన్ చేయాలా?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"రెండు వాల్యూమ్ కీలను కొంత సేపు నొక్కి పట్టుకుంటే యాక్సెసిబిలిటీ ఫీచ‌ర్‌లు ఆన్ అవుతాయి. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nప్రస్తుత ఫీచర్లు:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nఎంపిక చేసిన ఫీచర్లను మీరు సెట్టింగ్‌లు&gt;యాక్సెసిబిలిటీలో మార్చవచ్చు."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ఆన్ చేయాాలా?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"రెండు వాల్యూమ్ కీలను కొన్ని సెకన్ల పాటు నొక్కి పట్టుకోవడం ద్వారా యాక్సెసిబిలిటీ అయిన <xliff:g id="SERVICE">%1$s</xliff:g> ఆన్ అవుతుంది. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nసెట్టింగ్‌లు &gt; యాక్సెసిబిలిటీలో, వేరొక ఫీచర్‌ను ప్రారంభించేలా ఈ షార్ట్ కట్‌ను మీరు మార్చవచ్చు."</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"మీ నిర్వాహకులు నవీకరించారు"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"మీ నిర్వాహకులు తొలగించారు"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"బ్యాటరీ జీవితకాలాన్ని పెంచడానికి, బ్యాటరీ సేవర్ వీటిని చేస్తుంది:\n\n•ముదురు రంగు రూపాన్ని ఆన్ చేస్తుంది\n•బ్యాక్‌గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్‌లతో పాటు “Ok Google” వంటి ఇతర ఫీచర్‌లను ఆఫ్ చేస్తుంది లేదా పరిమితం చేస్తుంది\n\n"<annotation id="url">"మరింత తెలుసుకోండి"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"బ్యాటరీ జీవితకాలాన్ని పెంచడానికి, బ్యాటరీ సేవర్ వీటిని చేస్తుంది:\n\n•ముదురు రంగు రూపాన్ని ఆన్ చేస్తుంది\n•బ్యాక్‌గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్‌లతో పాటు “Ok Google” వంటి ఇతర ఫీచర్‌లను ఆఫ్ చేస్తుంది లేదా పరిమితం చేస్తుంది"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"బ్యాటరీ జీవితకాలాన్ని పెంచడానికి, బ్యాటరీ సేవర్ వీటిని చేస్తుంది:\n\n• ముదురు రంగు రూపాన్ని ఆన్ చేస్తుంది\n•బ్యాక్‌గ్రౌండ్ యాక్టివిటీని, కొన్ని విజువల్ ఎఫెక్ట్‌లను, అలాగే “Ok Google” వంటి ఇతర ఫీచర్‌లను ఆఫ్ చేస్తుంది లేదా పరిమితం చేస్తుంది\n\n"<annotation id="url">"మరింత తెలుసుకోండి"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"బ్యాటరీ జీవితకాలాన్ని పెంచడానికి, బ్యాటరీ సేవర్ వీటిని చేస్తుంది:\n\n•ముదురు రంగు రూపాన్ని ఆన్ చేస్తుంది\n•బ్యాక్‌గ్రౌండ్ యాక్టివిటీని, కొన్ని విజువల్ ఎఫెక్ట్‌లను, అలాగే “Ok Google” వంటి ఇతర ఫీచర్‌లను ఆఫ్ చేస్తుంది లేదా పరిమితం చేస్తుంది"</string>
<string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్‌గ్రౌండ్‌లో కొన్ని యాప్‌లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్‌, డేటాను యాక్సెస్ చేయగలదు. కానీ త‌క్కువ సార్లు మాత్ర‌మే అలా చేయవచ్చు. ఉదాహరణకు, మీరు నొక్కే వరకు ఫోటోలు ప్రదర్శించబడవు."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"డేటా సేవర్‌ను ఆన్ చేయాలా?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ఆన్ చేయి"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"కెమెరా"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"మైక్రోఫోన్"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"మీ స్క్రీన్‌పై ఇతర యాప్‌ల ద్వారా ప్రదర్శించబడుతోంది"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"ఫీడ్‌బ్యాక్‌ను అందించండి"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"రొటీన్ మోడ్ సమాచార నోటిఫికేషన్"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"మామూలుగా ఛార్జ్ చేసేలోపు బ్యాటరీ ఖాళీ కావచ్చు"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"బ్యాటరీ జీవితకాలాన్ని పెంచడానికి బ్యాటరీ సేవర్ యాక్టివేట్ చేయబడింది"</string>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 3ecb1dddd916..0382dd3b9103 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -42,4 +42,13 @@
<!-- Allow SystemUI to show the shutdown dialog -->
<bool name="config_showSysuiShutdown">true</bool>
+
+ <!-- Control the behavior when the user long presses the power button.
+ 0 - Nothing
+ 1 - Global actions menu
+ 2 - Power off (with confirmation)
+ 3 - Power off (without confirmation)
+ 4 - Go to voice assist
+ 5 - Go to assistant (Settings.Secure.ASSISTANT -->
+ <integer name="config_longPressOnPowerBehavior">3</integer>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 51dcca54422d..3deb9b2b7b82 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"กด เมนู เพื่อปลดล็อกหรือโทรฉุกเฉิน"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"กด เมนู เพื่อปลดล็อก"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"วาดรูปแบบเพื่อปลดล็อก"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"เหตุฉุกเฉิน"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"กลับสู่การโทร"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ถูกต้อง!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ลองอีกครั้ง"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"อัปเดตโดยผู้ดูแลระบบ"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"ลบโดยผู้ดูแลระบบ"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ตกลง"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"โหมดประหยัดแบตเตอรี่จะดำเนินการดังต่อไปนี้เพื่อยืดอายุการใช้งานแบตเตอรี่\n\n•เปิดธีมมืด\n•ปิดหรือจำกัดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง และฟีเจอร์อื่นๆ อย่างเช่น “Ok Google”\n\n"<annotation id="url">"ดูข้อมูลเพิ่มเติม"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"โหมดประหยัดแบตเตอรี่จะดำเนินการดังต่อไปนี้เพื่อยืดอายุการใช้งานแบตเตอรี่\n\n•เปิดธีมมืด\n•ปิดหรือจำกัดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง และฟีเจอร์อื่นๆ อย่างเช่น “Ok Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"โหมดประหยัดแบตเตอรี่จะดำเนินการดังต่อไปนี้เพื่อยืดอายุการใช้งานแบตเตอรี่\n\n• เปิดธีมมืด\n•ปิดหรือจำกัดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง และฟีเจอร์อื่นๆ อย่างเช่น “Ok Google”\n\n"<annotation id="url">"ดูข้อมูลเพิ่มเติม"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"โหมดประหยัดแบตเตอรี่จะดำเนินการดังต่อไปนี้เพื่อยืดอายุการใช้งานแบตเตอรี่\n\n• เปิดธีมมืด\n• ปิดหรือจำกัดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง และฟีเจอร์อื่นๆ อย่างเช่น \"Ok Google\""</string>
<string name="data_saver_description" msgid="4995164271550590517">"เพื่อช่วยลดปริมาณการใช้อินเทอร์เน็ต โปรแกรมประหยัดอินเทอร์เน็ตจะช่วยป้องกันไม่ให้บางแอปส่งหรือรับข้อมูลโดยการใช้อินเทอร์เน็ตอยู่เบื้องหลัง แอปที่คุณกำลังใช้งานสามารถเข้าถึงอินเทอร์เน็ตได้ แต่อาจไม่บ่อยเท่าเดิม ตัวอย่างเช่น ภาพต่างๆ จะไม่แสดงจนกว่าคุณจะแตะที่ภาพเหล่านั้น"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"เปิดการประหยัดอินเทอร์เน็ตไหม"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"เปิด"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"กล้อง"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"ไมโครโฟน"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"แสดงทับแอปอื่นๆ บนหน้าจอ"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"แสดงความคิดเห็น"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"การแจ้งเตือนข้อมูลโหมดกิจวัตร"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"แบตเตอรี่อาจหมดก่อนการชาร์จปกติ"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"เปิดใช้งานโหมดประหยัดแบตเตอรี่แล้วเพื่อยืดอายุการใช้งานแบตเตอรี่"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 0aadfb16f27e..ccda1c46059e 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pindutin ang Menu upang i-unlock o magsagawa ng pang-emergency na tawag."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pindutin ang Menu upang i-unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Iguhit ang pattern upang i-unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Bumalik sa tawag"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tama!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Subukang muli"</string>
@@ -1651,7 +1652,7 @@
<string name="color_inversion_feature_name" msgid="326050048927789012">"Pag-invert ng Kulay"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Pagwawasto ng Kulay"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Mga volume key na pinipindot nang matagal. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pindutin nang matagal ang parehong volume key sa loob ng tatlong segundo para magamit ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Pumili ng feature na gagana sa pamamagitan ng pag-tap mo sa button ng accessibility:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Pumili ng feature na gagana sa pamamagitan ng galaw ng accessibility (pag-swipe pataas mula sa ibaba ng screen gamit ang dalawang daliri):"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Na-update ng iyong admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Na-delete ng iyong admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Para patagalin ang baterya, ginagawa ng Pangtipid sa Baterya na:\n\n•I-on ang Madilim na tema\n•I-off o paghigpitan ang aktibidad sa background, ilang visual effect, at iba pang feature gaya ng “Hey Google”\n\n"<annotation id="url">"Matuto pa"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Para patagalin ang baterya, ginagawa ng Pangtipid sa Baterya na:\n\n•I-on ang Madilim na tema\n•I-off o paghigpitan ang aktibidad sa background, ilang visual effect, at iba pang feature gaya ng “Hey Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Para patagalin ang baterya, ginagawa ng Pangtipid sa Baterya na:\n\n• I-on ang Madilim na tema\n• I-off o paghihigpitan ang aktibidad sa background, ilang visual effect, at iba pang feature gaya ng “Hey Google”\n\n"<annotation id="url">"Matuto pa"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Para patagalin ang baterya, ginagawa ng Pangtipid sa Baterya na:\n\n• I-on ang Madilim na tema\n• I-off o paghihigpitan ang aktibidad sa background, ilang visual effect, at iba pang feature gaya ng “Hey Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Upang makatulong na mabawasan ang paggamit ng data, pinipigilan ng Data Saver ang ilang app na magpadala o makatanggap ng data sa background. Maaaring mag-access ng data ang isang app na ginagamit mo sa kasalukuyan, ngunit mas bihira na nito magagawa iyon. Halimbawa, maaaring hindi lumabas ang mga larawan hangga\'t hindi mo nata-tap ang mga ito."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"I-on ang Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"I-on"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Camera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikropono"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ipinapakita sa ibabaw ng ibang app sa iyong screen"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Magbigay ng Feedback"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification ng impormasyon ng Routine Mode"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Maaaring maubos ang baterya bago ang karaniwang pag-charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Na-activate ang Pangtipid sa Baterya para patagalin ang buhay ng baterya"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index ed4137838195..85c69860eb81 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Kilidi açmak veya acil çağrı yapmak için Menü\'ye basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Kilidi açmak için Menü\'ye basın."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Kilit açmak için deseni çizin"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Acil durum çağrısı"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Çağrıya dön"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Doğru!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tekrar deneyin"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Yöneticiniz tarafından güncellendi"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Yöneticiniz tarafından silindi"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Tamam"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Pil ömrünü uzatmak için Pil Tasarrufu:\n\n•Koyu temayı açar\n•Arka plan etkinliğini, bazı görsel efektleri ve \"Ok Google\" gibi diğer özellikleri kapatır veya kısıtlar\n\n"<annotation id="url">"Daha fazla bilgi"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Pil ömrünü uzatmak için Pil Tasarrufu:\n\n•Koyu temayı açar\n•Arka plan etkinliğini, bazı görsel efektleri ve \"Ok Google\" gibi diğer özellikleri kapatır veya kısıtlar"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Pil ömrünü uzatmak için Pil Tasarrufu:\n\n• Koyu temayı açar\n• Arka plan etkinliğini, bazı görsel efektleri ve \"Ok Google\" gibi diğer özellikleri kapatır veya kısıtlar\n\n"<annotation id="url">"Daha fazla bilgi"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Pil ömrünü uzatmak için Pil Tasarrufu:\n\n• Koyu temayı açar\n• Arka plan etkinliğini, bazı görsel efektleri ve \"Ok Google\" gibi diğer özellikleri kapatır veya kısıtlar"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Veri kullanımını azaltmaya yardımcı olması için Veri Tasarrufu, bazı uygulamaların arka planda veri göndermesini veya almasını engeller. Şu anda kullandığınız bir uygulama veri bağlantısına erişebilir, ancak bunu daha seyrek yapabilir. Bu durumda örneğin, siz resimlere dokunmadan resimler görüntülenmez."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Veri Tasarrufu açılsın mı?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aç"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ekranınızdaki diğer uygulamaların üzerinde görüntüleniyor"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Geri Bildirim Gönder"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Rutin Modu bilgi bildirimi"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Pil normal şarjdan önce bitebilir"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Pilin ömrünü uzatmak için Pil Tasarrufu etkinleştirildi"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 03bf132cc811..8ae20a50f7f3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Натис. меню, щоб розбл. чи зробити авар. виклик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Натисн. меню, щоб розбл."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Намал. ключ, щоб розбл."</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Екстрений виклик"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Поверн. до дзвін."</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Повторіть спробу"</string>
@@ -1165,7 +1166,7 @@
<string name="capital_off" msgid="7443704171014626777">"ВИМК"</string>
<string name="checked" msgid="9179896827054513119">"вибрано"</string>
<string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Завершити дію за доп."</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завершити дію"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Відкрити за допомогою"</string>
@@ -1838,8 +1839,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Оновлено адміністратором"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Видалено адміністратором"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Щоб подовжити час роботи акумулятора, режим енергозбереження:\n\n•вмикає темну тему;\n•припиняє або обмежує фонову активність, вимикає деякі візуальні ефекти та інші енергозатратні функції, зокрема команду \"Ok Google\".\n\n"<annotation id="url">"Докладніше"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Щоб подовжити час роботи акумулятора, режим енергозбереження:\n\n•вмикає темну тему;\n•припиняє або обмежує фонову активність, вимикає деякі візуальні ефекти та інші енергозатратні функції, зокрема команду \"Ok Google\"."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Щоб подовжити час роботи акумулятора, режим енергозбереження:\n\n• вмикає темну тему;\n• припиняє або обмежує фонову активність, вимикає деякі візуальні ефекти та інші енергозатратні функції, зокрема команду \"Ok Google\".\n\n"<annotation id="url">"Докладніше"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Щоб подовжити час роботи акумулятора, режим енергозбереження:\n\n• вмикає темну тему;\n• припиняє або обмежує фонову активність, вимикає деякі візуальні ефекти та інші енергозатратні функції, зокрема команду \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Щоб зменшити використання трафіку, функція \"Заощадження трафіку\" не дозволяє деяким додаткам надсилати чи отримувати дані у фоновому режимі. Поточний додаток зможе отримувати доступ до таких даних, але рідше. Наприклад, зображення не відображатиметься, доки ви не торкнетеся його."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Увімкнути заощадження трафіку?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Увімкнути"</string>
@@ -2063,8 +2064,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Мікрофон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"показ поверх інших додатків на екрані"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Надати відгук"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Сповіщення про послідовнсть дій"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Акумулятор може розрядитися раніше ніж зазвичай"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Режим енергозбереження активовано для збільшення часу роботи акумулятора"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index bcf6ca942a67..94c6cd450a6c 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -222,7 +222,7 @@
<string name="reboot_to_update_prepare" msgid="6978842143587422365">"اپ ڈیٹ کرنے کی تیاری ہو رہی ہے…"</string>
<string name="reboot_to_update_package" msgid="4644104795527534811">"اپ ڈیٹ پیکج پر کاروائی کی جارہی ہے…"</string>
<string name="reboot_to_update_reboot" msgid="4474726009984452312">"دوبارہ شروع ہو رہا ہے…"</string>
- <string name="reboot_to_reset_title" msgid="2226229680017882787">"فیکٹری ڈیٹا کی دوبارہ ترتیب"</string>
+ <string name="reboot_to_reset_title" msgid="2226229680017882787">"فیکٹری ڈیٹا ری سیٹ"</string>
<string name="reboot_to_reset_message" msgid="3347690497972074356">"دوبارہ شروع ہو رہا ہے…"</string>
<string name="shutdown_progress" msgid="5017145516412657345">"بند ہو رہا ہے…"</string>
<string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"آپ کا ٹیبلیٹ بند ہو جائے گا۔"</string>
@@ -686,9 +686,9 @@
<string name="policylab_forceLock" msgid="7360335502968476434">"اسکرین مقفل کریں"</string>
<string name="policydesc_forceLock" msgid="1008844760853899693">"اسکرین کب اور کس طرح مقفل ہوتی ہے اس کو کنٹرول کریں۔"</string>
<string name="policylab_wipeData" msgid="1359485247727537311">"سبھی ڈیٹا صاف کریں"</string>
- <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"فیکٹری ڈیٹا کی دوبارہ ترتیب انجام دے کر وارننگ کے بغیر ٹیبلٹ کا ڈیٹا مٹائیں۔"</string>
+ <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"فیکٹری ڈیٹا ری سیٹ انجام دے کر وارننگ کے بغیر ٹیبلٹ کا ڈیٹا مٹائیں۔"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"‏فیکٹری ڈیٹا ری سیٹ کو انجام دے کر انتباہ کیے بغیر اپنے Android TV آلہ کا ڈیٹا مٹائیں۔"</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"فیکٹری ڈیٹا کی دوبارہ ترتیب انجام دے کر وارننگ کے بغیر فون کا ڈیٹا مٹائیں۔"</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"فیکٹری ڈیٹا ری سیٹ انجام دے کر وارننگ کے بغیر فون کا ڈیٹا مٹائیں۔"</string>
<string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"صارف کا ڈیٹا ہٹائیں"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"وارننگ کے بغیر اس ٹیبلٹ پر موجود اس صارف کا ڈیٹا ہٹائیں۔"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"‏انتباہ کے بغیر اس Android TV آلہ پر اس صارف کا ڈیٹا ہٹائیں۔"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"غیر مقفل کرنے کیلئے مینو دبائیں یا ہنگامی کال کریں۔"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"غیر مقفل کرنے کیلئے پیٹرن کو ڈرا کریں"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ہنگامی"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"کال پر واپس جائیں"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحیح!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوبارہ کوشش کریں"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"آپ کے منتظم کے ذریعے اپ ڈیٹ کیا گیا"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"آپ کے منتظم کے ذریعے حذف کیا گیا"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ٹھیک ہے"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"‏بیٹری لائف کو بڑھانے کے لیے، بیٹری سیور:\n\n•گہری تھیم کو آن کرتی ہے\n•پس منظر کی سرگرمی، کچھ بصری اثرات اور دیگر خصوصیات جیسے کہ \"Ok Google\" کو آف یا محدود کرتی ہے\n\n"<annotation id="url">"مزید جانیں"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"‏بیٹری لائف کو بڑھانے کے لیے، بیٹری سیور:\n\n•گہری تھیم کو آن کرتی ہے\n•پس منظر کی سرگرمی، کچھ بصری اثرات اور دیگر خصوصیات جیسے کہ \"Ok Google\" کو آف یا محدود کرتی ہے"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"‏بیٹری لائف کو بڑھانے کے لیے، بیٹری سیور:\n\n• گہری تھیم کو آن کرتی ہے\n• پس منظر کی سرگرمی، کچھ بصری اثرات اور دیگر خصوصیات جیسے کہ \"Ok Google\" کو آف یا محدود کرتی ہے\n\n"<annotation id="url">"مزید جانیں"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"‏بیٹری لائف کو بڑھانے کے لیے، بیٹری سیور: \n\n• گہری تھیم کو آن کرتی ہے\n• پس منظر کی سرگرمی، کچھ ویژوئل اثرات اور دیگر خصوصیات جیسے کہ \"Ok Google\" کو آف یا محدود کرتی ہے"</string>
<string name="data_saver_description" msgid="4995164271550590517">"ڈیٹا کے استعمال کو کم کرنے میں مدد کیلئے، ڈیٹا سیور پس منظر میں کچھ ایپس کو ڈیٹا بھیجنے یا موصول کرنے سے روکتی ہے۔ آپ جو ایپ فی الحال استعمال کر رہے ہیں وہ ڈیٹا تک رسائی کر سکتی ہے مگر ہو سکتا ہے ایسا اکثر نہ ہو۔ اس کا مطلب مثال کے طور پر یہ ہو سکتا ہے کہ تصاویر تھپتھپانے تک ظاہر نہ ہوں۔"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ڈیٹا سیور آن کریں؟"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"آن کریں"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"کیمرا"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"مائیکروفون"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"آپ کی اسکرین پر دیگر ایپس پر دکھایا جا رہا ہے"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"تاثرات فراہم کریں"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"روٹین موڈ معلومات کی اطلاع"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"معمول چارج سے پہلے بیٹری ختم ہو سکتی ہے"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"بیٹری لائف کو بڑھانے کے لیے بیٹری سیور کو فعال کر دیا گیا ہے"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 0f0d5f87dd07..74676dfcf226 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Qulfdan chiqarish yoki favqulodda qo‘ng‘iroqni amalga oshirish uchun \"Menyu\"ni bosing."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Qulfni ochish uchun \"Menyu\"ga bosing."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Qulfni ochish uchun grafik kalitni chizing"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Favqulodda chaqiruv"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Qo‘ng‘iroqni qaytarish"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"To‘g‘ri!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Qaytadan urining"</string>
@@ -1093,7 +1094,7 @@
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="1532369154488982046">"Hammasini belgilash"</string>
- <string name="cut" msgid="2561199725874745819">"Kesish"</string>
+ <string name="cut" msgid="2561199725874745819">"Kesib olish"</string>
<string name="copy" msgid="5472512047143665218">"Nusxa olish"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Vaqtinchalik xotiraga nusxalab bo‘lmadi"</string>
<string name="paste" msgid="461843306215520225">"Joylash"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"O"</string>
<string name="checked" msgid="9179896827054513119">"belgilandi"</string>
<string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Ilovani tanlang"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Amalni bajarish"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Ochish…"</string>
@@ -1314,7 +1315,7 @@
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB orqali nosozliklarni tuzatishni o‘chirib qo‘yish uchun bosing."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Wi-Fi orqali debagging yoqildi"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Wi-Fi orqali debagging uzilishi uchun bosing"</string>
- <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Wi-Fi orqali debaggingni faolsizlantirish uchun bosing."</string>
+ <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Uni faolsizlantirish uchun bosing."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Xavfsizlik sinovi rejimi yoqildi"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Xavfsizlik sinovi rejimini faolsizlantirish uchun zavod sozlamalariga qaytaring."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Ketma-ket port konsoli yoqildi"</string>
@@ -1548,7 +1549,7 @@
<string name="launchBrowserDefault" msgid="6328349989932924119">"Brauzer ishga tushirilsinmi?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"Qo‘ng‘iroqni qabul qilasizmi?"</string>
<string name="activity_resolver_use_always" msgid="5575222334666843269">"Har doim"</string>
- <string name="activity_resolver_use_once" msgid="948462794469672658">"Faqat hozir"</string>
+ <string name="activity_resolver_use_once" msgid="948462794469672658">"Faqat shu safar"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"“%1$s” ishchi profilni qo‘llab-quvvatlamaydi"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Planshet"</string>
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Administrator tomonidan yangilangan"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administrator tomonidan o‘chirilgan"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n•Tungi mavzuni yoqadi\n•Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi\n\n"<annotation id="url">"Batafsil"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n•Tungi mavzuni yoqadi\n•Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi\n\n"<annotation id="url">"Batafsil"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Trafik tejash rejimida ayrim ilovalar uchun orqa fonda internetdan foydalanish imkoniyati cheklanadi. Siz ishlatayotgan ilova zaruratga qarab internet-trafik sarflashi mumkin, biroq cheklangan miqdorda. Masalan, rasmlar ustiga bosmaguningizcha ular yuklanmaydi."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Trafik tejash yoqilsinmi?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Yoqish"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"ekranda boshqa ilovalar ustidan ochiladi"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Fikr-mulohaza yuborish"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Kun tartibi rejimi haqidagi bildirishnoma"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batareya quvvati odatdagidan ertaroq tugashi mumkin"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batareya quvvati uzoqroq ishlashi uchun Tejamkor rejim yoqildi"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 2db9b8a20b7d..48f38583220d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -538,7 +538,7 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"đọc vị trí từ bộ sưu tập phương tiện"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string>
- <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh đó là bạn"</string>
+ <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh danh tính của bạn"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Không có phần cứng sinh trắc học"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Đã hủy xác thực"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Không nhận dạng được"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Nhấn vào Menu để mở khóa hoặc thực hiện cuộc gọi khẩn cấp."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Nhấn vào Menu để mở khóa."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Vẽ hình để mở khóa"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Khẩn cấp"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Quay lại cuộc gọi"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Chính xác!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Thử lại"</string>
@@ -1016,7 +1017,7 @@
<string name="weeks" msgid="3516247214269821391">"tuần"</string>
<string name="year" msgid="5182610307741238982">"năm"</string>
<string name="years" msgid="5797714729103773425">"năm"</string>
- <string name="now_string_shortest" msgid="3684914126941650330">"bây giờ"</string>
+ <string name="now_string_shortest" msgid="3684914126941650330">"ngay lúc này"</string>
<plurals name="duration_minutes_shortest" formatted="false" msgid="7519574894537185135">
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>ph</item>
<item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>ph</item>
@@ -1125,8 +1126,8 @@
<string name="capital_off" msgid="7443704171014626777">"TẮT"</string>
<string name="checked" msgid="9179896827054513119">"đã chọn"</string>
<string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Hoàn tất tác vụ đang sử dụng"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất tác vụ bằng %1$s"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Hoàn thành tác vụ"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Mở bằng"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"Mở bằng %1$s"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Do quản trị viên của bạn cập nhật"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Do quản trị viên của bạn xóa"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Để tăng thời lượng pin, Trình tiết kiệm pin sẽ:\n\n•Bật Giao diện tối\n•Tắt hoặc hạn chế hoạt động chạy trong nền, một số hiệu ứng hình ảnh và các tính năng khác như lệnh “Ok Google”\n\n"<annotation id="url">"Tìm hiểu thêm"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Để tăng thời lượng pin, Trình tiết kiệm pin sẽ:\n\n•Bật Giao diện tối\n•Tắt hoặc hạn chế hoạt động chạy trong nền, một số hiệu ứng hình ảnh và các tính năng khác như lệnh “Ok Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Để tăng thời lượng pin, Trình tiết kiệm pin sẽ:\n\n• Bật Giao diện tối\n• Tắt hoặc hạn chế hoạt động chạy trong nền, một số hiệu ứng hình ảnh và các tính năng khác như lệnh “Ok Google”\n\n"<annotation id="url">"Tìm hiểu thêm"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Để tăng thời lượng pin, Trình tiết kiệm pin sẽ:\n\n• Bật Giao diện tối\n• Tắt hoặc hạn chế hoạt động chạy trong nền, một số hiệu ứng hình ảnh và các tính năng khác như lệnh “Ok Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Để giúp giảm mức sử dụng dữ liệu, Trình tiết kiệm dữ liệu sẽ chặn một số ứng dụng gửi hoặc nhận dữ liệu trong nền. Ứng dụng mà bạn hiện sử dụng có thể dùng dữ liệu nhưng tần suất sẽ giảm. Ví dụ: hình ảnh sẽ không hiển thị cho đến khi bạn nhấn vào hình ảnh đó."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Bật Trình tiết kiệm dữ liệu?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Bật"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Máy ảnh"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Micrô"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"hiển thị qua các ứng dụng khác trên màn hình của bạn"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Gửi ý kiến phản hồi"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Thông báo cung cấp thông tin về chế độ sạc thông thường"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Pin có thể hết trước khi sạc bình thường"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Trình tiết kiệm pin được kích hoạt để kéo dài thời lượng pin"</string>
@@ -2053,7 +2053,7 @@
<string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Cuộc trò chuyện nhóm"</string>
<string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
<string name="resolver_personal_tab" msgid="2051260504014442073">"Cá nhân"</string>
- <string name="resolver_work_tab" msgid="2690019516263167035">"Cơ quan"</string>
+ <string name="resolver_work_tab" msgid="2690019516263167035">"Công việc"</string>
<string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Chế độ xem cá nhân"</string>
<string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Chế độ xem công việc"</string>
<string name="resolver_cant_share_with_work_apps" msgid="637686613606502219">"Không thể chia sẻ nội dung này với các ứng dụng công việc"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index bb336be40833..bf381ec53751 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -92,7 +92,7 @@
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"紧急回拨模式"</string>
<string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"移动数据状态"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"短信"</string>
- <string name="notification_channel_voice_mail" msgid="8457433203106654172">"语音邮件"</string>
+ <string name="notification_channel_voice_mail" msgid="8457433203106654172">"语音信息"</string>
<string name="notification_channel_wfc" msgid="9048240466765169038">"WLAN 通话"</string>
<string name="notification_channel_sim" msgid="5098802350325677490">"SIM 卡状态"</string>
<string name="notification_channel_sim_high_prio" msgid="642361929452850928">"高优先顺序 SIM 卡状态"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按 Menu 解锁或进行紧急呼救。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按 MENU 解锁。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"绘制解锁图案"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"紧急呼救"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通话"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正确!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"重试"</string>
@@ -961,7 +962,7 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"允许该应用修改您手机上存储的浏览器历史记录或浏览器书签。此权限可让该应用清除或修改浏览器数据。请注意:此权限可能不适用于第三方浏览器或具备网页浏览功能的其他应用。"</string>
<string name="permlab_setAlarm" msgid="1158001610254173567">"设置闹钟"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"允许应用在已安装的闹钟应用中设置闹钟。有些闹钟应用可能无法实现此功能。"</string>
- <string name="permlab_addVoicemail" msgid="4770245808840814471">"添加语音邮件"</string>
+ <string name="permlab_addVoicemail" msgid="4770245808840814471">"添加语音信息"</string>
<string name="permdesc_addVoicemail" msgid="5470312139820074324">"允许应用在您的语音信箱中留言。"</string>
<string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"修改“浏览器”地理位置的权限"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"允许应用修改“浏览器”的地理位置权限。恶意应用可能借此向任意网站发送位置信息。"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理员更新"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"已由您的管理员删除"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"确定"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"为了延长电池续航时间,省电模式会执行以下操作:\n\n• 开启深色主题\n• 关闭或限制后台活动、部分视觉效果和其他功能,例如“Ok Google”\n\n"<annotation id="url">"了解详情"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"为了延长电池续航时间,省电模式会执行以下操作:\n\n• 开启深色主题\n• 关闭或限制后台活动、部分视觉效果和其他功能,例如“Ok Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"为了延长电池续航时间,省电模式会执行以下操作:\n\n• 开启深色主题\n• 关闭或限制后台活动、部分视觉效果和其他功能,例如“Ok Google”\n\n"<annotation id="url">"了解详情"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"为了延长电池续航时间,省电模式会执行以下操作:\n\n• 开启深色主题\n• 关闭或限制后台活动、部分视觉效果和其他功能,例如“Ok Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"为了减少流量消耗,流量节省程序会阻止某些应用在后台收发数据。您当前使用的应用可以收发数据,但频率可能会降低。举例而言,这可能意味着图片只有在您点按之后才会显示。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"要开启流量节省程序吗?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"开启"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"相机"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"麦克风"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"显示在屏幕上其他应用的上层"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"提供反馈"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式信息通知"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"电池电量可能会在您平时的充电时间之前耗尽"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已启用省电模式以延长电池续航时间"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 7b91664f0438..a3b20d128726 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按選單鍵解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按選單鍵解鎖。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"畫出解鎖圖形以解除鎖定螢幕"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急電話"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通話"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正確!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"再試一次"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理員更新"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"已由您的管理員刪除"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"好"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為延長電池壽命,「省電模式」會:\n\n•開啟深色主題背景\n•關閉或限制背景活動、某些視覺效果和其他功能 (例如「Hey Google」)\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"為延長電池壽命,「省電模式」會:\n\n•開啟深色主題背景\n•關閉或限制背景活動、某些視覺效果和其他功能 (例如「Hey Google」)"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"為延長電池壽命,「省電模式」會:\n\n• 開啟深色主題背景\n• 關閉或限制背景活動、某些視覺效果和其他功能 (例如「Hey Google」)\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"為延長電池壽命,「省電模式」會:\n\n• 開啟深色主題背景\n• 停用或限制背景活動、部分視覺效果和其他功能 (例如「Hey Google」)"</string>
<string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。您正在使用的應用程式可存取資料,但次數可能會減少。例如,圖片可能需要輕按才會顯示。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟「數據節省模式」嗎?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"相機"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"麥克風"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"顯示在畫面上的其他應用程式上層"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"提供意見"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"「日常安排模式」資料通知"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"電量可能會在日常充電前耗盡"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"「省電模式」已啟用,以便延長電池壽命"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 00f0100078eb..f6543c9285d2 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按下 [Menu] 解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按下 Menu 鍵解鎖。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"畫出解鎖圖案"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急撥號"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通話"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正確!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"再試一次"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"為了延長電池續航力,節約耗電量模式會執行以下動作:\n\n• 開啟深色主題\n• 關閉或限制背景活動、某些視覺效果和其他功能,例如「Ok Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"為了延長電池續航力,節約耗電量模式會執行以下動作:\n\n• 開啟深色主題\n• 關閉或限制背景活動、某些視覺效果和其他功能,例如「Ok Google」啟動字詞"</string>
<string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你目前使用的應用程式可以存取資料,但存取頻率可能不如平時高。舉例來說,圖片可能不會自動顯示,在你輕觸後才會顯示。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string>
@@ -1997,13 +1998,12 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"相機"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"麥克風"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"顯示在畫面上的其他應用程式上層"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"提供意見"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式資訊通知"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"電池電力可能會在你平常的充電時間前耗盡"</string>
- <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用節約耗電量模式以延長電池續航力"</string>
- <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"節約耗電量"</string>
- <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"節約耗電量模式已關閉"</string>
+ <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用省電模式以延長電池續航力"</string>
+ <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"省電模式"</string>
+ <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"省電模式已關閉"</string>
<string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"手機電力充足,各項功能不再受到限制。"</string>
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"平板電腦電力充足,各項功能不再受到限制。"</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"裝置電力充足,各項功能不再受到限制。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index aa47b1f3663b..85f54e9f9f5e 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Chofoza Menyu ukuvula noma ukwenza ikholi ephuthumayo."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Chofoza Menyu ukuvula."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dweba iphathini ukuvula"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Isimo esiphuthumayo"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Buyela ekholini"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Lungile!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Zama futhi"</string>
@@ -1312,7 +1313,7 @@
<string name="adb_active_notification_title" msgid="408390247354560331">"Ukulungisa iphutha le-USB kuxhunyiwe"</string>
<string name="adb_active_notification_message" msgid="5617264033476778211">"Thepha ukuze uvale ukulungisa amaphutha kwe-USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Khetha ukuvimbela ukulungisa iphutha le-USB."</string>
- <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Ukulungisa amaphutha okungenantambo kuxhunyiwe"</string>
+ <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Ukulungisa amaphutha e-wireless kuxhunyiwe"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Thepha ukuze ucishe ukulungisa amaphutha okungenantambo"</string>
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Khetha ukukhubaza ukulungisa amaphutha okungenantambo."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Imodi yokuhlola i-harness inikwe amandla"</string>
@@ -1792,8 +1793,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Kubuyekezwe umlawuli wakho"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Kususwe umlawuli wakho"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"KULUNGILE"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"Ukuze unwebe impilo yebhethri, Isilondolozi Sebhethri:\n\n•Sivula itimu emnyama\n•Sivala noma sibeka umkhawulo emsebenzini wangemuva, kweminye imithelela yokubuka, nakwezinye izici ezifana nokuthi “Ok Google”\n\n"<annotation id="url">"Funda kabanzi"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"Ukuze unwebe impilo yebhethri, Isilondolozi sebhethri:\n\n•Sivula itimu emnyama\n•Sivala noma sibeka umkhawulo emsebenzini wangemuva, kweminye imithelela yokubuka, nakwezinye izici ezifana nokuthi “Ok Google”"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Ukuze unwebe impilo yebhethri, Isilondolozi Sebhethri:\n\n•Sivula itimu emnyama\n• Sivala noma sibeka umkhawulo emsebenzini wangemuva, kweminye imithelela yokubuka, nakwezinye izici ezifana nokuthi “Ok Google”\n\n"<annotation id="url">"Funda kabanzi"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Ukuze unwebe impilo yebhethri, Isilondolozi sebhethri:\n\n•Sivula itimu emnyama\n• Sivala noma sibeka umkhawulo emsebenzini wangemuva, kweminye imithelela yokubuka, nakwezinye izici ezifana nokuthi “Ok Google”"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Ukusiza ukwehlisa ukusetshenziswa kwedatha, iseva yedatha igwema ezinye izinhlelo zokusebenza ukuthi zithumele noma zamukele idatha ngasemuva. Uhlelo lokusebenza olisebenzisa okwamanje lingafinyelela idatha, kodwa lingenza kanjalo kancane. Lokhu kungachaza, isibonelo, ukuthi izithombe azibonisi uze uzithephe."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vula iseva yedatha?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Vula"</string>
@@ -1997,8 +1998,7 @@
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Ikhamera"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Imakrofoni"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"iboniswa ngaphezulu kwezinye izinhlelo zokusebenza kusikrini sakho"</string>
- <!-- no translation found for notification_feedback_indicator (663476517711323016) -->
- <skip />
+ <string name="notification_feedback_indicator" msgid="663476517711323016">"Nikeza impendulo"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Isaziso solwazi lwe-Routine Mode"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Ibhethri lingaphela ngaphambi kokushaja okuvamile"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Isilondolozi sebhethri siyasebenza ngaphandle kwempilo yebhethri"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index eb30c9be4eba..ac08d96ab303 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2173,6 +2173,29 @@
<attr name="required" />
</declare-styleable>
+ <!-- The <code>uses-native-library</code> specifies a native shared library that this
+ package requires to be linked against. Specifying this flag tells the
+ system to make the native library to be available to your app.
+
+ <p>On devices running R or lower, this is ignored and the app has access to all
+ the public native shared libraries that are exported from the platform. This is
+ also ignored if the app is targeting R or lower.
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestUsesNativeLibrary" parent="AndroidManifestApplication">
+ <!-- Required name of the library you use. -->
+ <attr name="name" />
+ <!-- Specify whether this native library is required for the application.
+ The default is true, meaning the application requires the
+ library, and does not want to be installed on devices that
+ don't support it. If you set this to false, then this will
+ allow the application to be installed even if the library
+ doesn't exist, and you will need to check for its presence
+ dynamically at runtime. -->
+ <attr name="required" />
+ </declare-styleable>
+
<!-- The <code>uses-static-library</code> specifies a shared <strong>static</strong>
library that this package requires to be statically linked against. Specifying
this tag tells the system to include this library's code in your class loader.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9f4b9b34856e..bc4c099cbceb 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -712,10 +712,17 @@
case, this can be disabled (set to false). -->
<bool name="config_enableCarDockHomeLaunch">true</bool>
- <!-- Control whether to force the display of System UI Bars at all times regardless of
- System Ui Flags. This can be useful in the Automotive case if there's a requirement for
- a UI element to be on screen at all times. -->
- <bool name="config_forceShowSystemBars">false</bool>
+ <!-- Control whether to force apps to give up control over the display of system bars at all
+ times regardless of System Ui Flags.
+ In the Automotive case, this is helpful if there's a requirement for an UI element to be on
+ screen at all times. Setting this to true also gives System UI the ability to override the
+ visibility controls for the system through the usage of the
+ "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting.
+ Ex: Only setting the config to true will force show system bars for the entire system.
+ Ex: Setting the config to true and the "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting to
+ "immersive.status=apps" will force show navigation bar for all apps and force hide status
+ bar for all apps. -->
+ <bool name="config_remoteInsetsControllerControlsSystemBars">false</bool>
<!-- HDMI behavior -->
@@ -2279,7 +2286,7 @@
</integer-array>
<!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
- <bool name="config_cellBroadcastAppLinks">false</bool>
+ <bool name="config_cellBroadcastAppLinks">true</bool>
<!-- The default value if the SyncStorageEngine should sync automatically or not -->
<bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
@@ -2905,6 +2912,11 @@
empty string is passed in -->
<string name="config_wlan_data_service_package" translatable="false"></string>
+ <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
+ tunnels across service restart. If iwlan tunnels are not persisted across restart,
+ Framework will clean up dangling data connections when service restarts -->
+ <bool name="config_wlan_data_service_conn_persistence_on_restart">true</bool>
+
<!-- Cellular data service class name to bind to by default. If none is specified in an overlay, an
empty string is passed in -->
<string name="config_wwan_data_service_class" translatable="false"></string>
@@ -4358,4 +4370,29 @@
<bool name="config_pdp_reject_enable_retry">false</bool>
<!-- pdp data reject retry delay in ms -->
<integer name="config_pdp_reject_retry_delay_ms">-1</integer>
+
+ <!-- Whether or not to enable the binder heavy hitter watcher by default -->
+ <bool name="config_defaultBinderHeavyHitterWatcherEnabled">true</bool>
+
+ <!-- The default batch size for the binder heavy hitter watcher -->
+ <integer name="config_defaultBinderHeavyHitterWatcherBatchSize">2000</integer>
+
+ <!-- The default threshold for the binder heavy hitter watcher -->
+ <item name="config_defaultBinderHeavyHitterWatcherThreshold" format="float" type="dimen">
+ 0.333
+ </item>
+
+ <!-- Whether or not to enable the binder heavy hitter auto sampler by default -->
+ <bool name="config_defaultBinderHeavyHitterAutoSamplerEnabled">true</bool>
+
+ <!-- The default batch size for the binder heavy hitter auto sampler -->
+ <integer name="config_defaultBinderHeavyHitterAutoSamplerBatchSize">400</integer>
+
+ <!-- The default threshold for the binder heavy hitter auto sampler -->
+ <item name="config_defaultBinderHeavyHitterAutoSamplerThreshold" format="float" type="dimen">
+ 0.333
+ </item>
+
+ <!-- Component names of the services which will keep critical code path warm -->
+ <string-array name="config_keep_warming_services" translatable="false" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 37d1eccefd4a..303fde6705c2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3024,6 +3024,8 @@
<!-- @hide @TestApi -->
<public type="bool" name="config_assistantOnTopOfDream" id="0x01110005" />
+ <!-- @hide @TestApi -->
+ <public type="bool" name="config_remoteInsetsControllerControlsSystemBars" id="0x01110006" />
<!-- ===============================================================
Resources added in version S of the platform
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 367cff930689..4d74cf7d79bc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2128,7 +2128,7 @@
<!-- On the unlock pattern screen, shown at the top of the unlock screen to tell the user what to do. Below this text is the place for theu ser to draw the pattern. -->
<string name="lockscreen_pattern_instructions">Draw pattern to unlock</string>
<!-- Button at the bottom of the unlock screen to make an emergency call or access other emergency assistance functions. -->
- <string name="lockscreen_emergency_call">Emergency</string>
+ <string name="lockscreen_emergency_call">Emergency call</string>
<!-- Button at the bottom of the unlock screen that lets the user return to a call -->
<string name="lockscreen_return_to_call">Return to call</string>
<!-- Shown to confirm that the user entered their lock pattern correctly. -->
@@ -4828,10 +4828,10 @@
<string name="confirm_battery_saver">OK</string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, with a "learn more" link. -->
- <string name="battery_saver_description_with_learn_more">To extend battery life, Battery Saver:\n\n\u2022Turns on Dark theme\n\u2022Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d\n\n<annotation id="url">Learn more</annotation></string>
+ <string name="battery_saver_description_with_learn_more">To extend battery life, Battery Saver:\n\n\u2022 Turns on Dark theme\n\u2022 Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d\n\n<annotation id="url">Learn more</annotation></string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, without a "learn more" link. -->
- <string name="battery_saver_description">To extend battery life, Battery Saver:\n\n\u2022Turns on Dark theme\n\u2022Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d</string>
+ <string name="battery_saver_description">To extend battery life, Battery Saver:\n\n\u2022 Turns on Dark theme\n\u2022 Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d</string>
<!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
<string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ad3e09d75c5b..16427cd50577 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -303,6 +303,7 @@
<java-symbol type="string" name="config_wlan_network_service_package" />
<java-symbol type="string" name="config_wwan_network_service_class" />
<java-symbol type="string" name="config_wlan_network_service_class" />
+ <java-symbol type="bool" name="config_wlan_data_service_conn_persistence_on_restart" />
<java-symbol type="string" name="config_wwan_data_service_package" />
<java-symbol type="string" name="config_wlan_data_service_package" />
<java-symbol type="string" name="config_wwan_data_service_class" />
@@ -1668,7 +1669,7 @@
<java-symbol type="bool" name="config_enableCarDockHomeLaunch" />
<java-symbol type="bool" name="config_enableLockBeforeUnlockScreen" />
<java-symbol type="bool" name="config_enableLockScreenRotation" />
- <java-symbol type="bool" name="config_forceShowSystemBars" />
+ <java-symbol type="bool" name="config_remoteInsetsControllerControlsSystemBars" />
<java-symbol type="bool" name="config_lidControlsScreenLock" />
<java-symbol type="bool" name="config_lidControlsSleep" />
<java-symbol type="bool" name="config_lockDayNightMode" />
@@ -4048,4 +4049,14 @@
<java-symbol type="string" name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" />
<java-symbol type="array" name="config_notificationMsgPkgsAllowedAsConvos" />
+
+ <!-- Binder heavy hitter watcher configs -->
+ <java-symbol type="bool" name="config_defaultBinderHeavyHitterWatcherEnabled" />
+ <java-symbol type="integer" name="config_defaultBinderHeavyHitterWatcherBatchSize" />
+ <java-symbol type="dimen" name="config_defaultBinderHeavyHitterWatcherThreshold" />
+ <java-symbol type="bool" name="config_defaultBinderHeavyHitterAutoSamplerEnabled" />
+ <java-symbol type="integer" name="config_defaultBinderHeavyHitterAutoSamplerBatchSize" />
+ <java-symbol type="dimen" name="config_defaultBinderHeavyHitterAutoSamplerThreshold" />
+
+ <java-symbol type="array" name="config_keep_warming_services" />
</resources>
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
index 7f9d8749358c..6849a90f5010 100644
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -15,14 +15,18 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.bluetooth.tests" >
+ package="com.android.bluetooth.tests"
+ android:sharedUserId="android.uid.bluetooth" >
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
index 4b32ceae0617..89dbe3f75b56 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
@@ -360,6 +360,30 @@ public class BluetoothStressTest extends InstrumentationTestCase {
mTestUtils.unpair(mAdapter, device);
}
+ /* Make sure there is at least 1 unread message in the last week on remote device */
+ public void testMceSetMessageStatus() {
+ int iterations = BluetoothTestRunner.sMceSetMessageStatusIterations;
+ if (iterations == 0) {
+ return;
+ }
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.MAP_CLIENT, null);
+ mTestUtils.mceGetUnreadMessage(mAdapter, device);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.READ);
+ mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.UNREAD);
+ }
+
+ /**
+ * It is hard to find device to support set undeleted status, so just
+ * set deleted in 1 iteration
+ **/
+ mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.DELETED);
+ }
+
private void sleep(long time) {
try {
Thread.sleep(time);
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
index 56e691d8c246..d19c2c3e7e24 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
@@ -40,6 +40,7 @@ import android.util.Log;
* [-e connect_input_iterations <iterations>] \
* [-e connect_pan_iterations <iterations>] \
* [-e start_stop_sco_iterations <iterations>] \
+ * [-e mce_set_message_status_iterations <iterations>] \
* [-e pair_address <address>] \
* [-e headset_address <address>] \
* [-e a2dp_address <address>] \
@@ -64,6 +65,7 @@ public class BluetoothTestRunner extends InstrumentationTestRunner {
public static int sConnectInputIterations = 100;
public static int sConnectPanIterations = 100;
public static int sStartStopScoIterations = 100;
+ public static int sMceSetMessageStatusIterations = 100;
public static String sDeviceAddress = "";
public static byte[] sDevicePairPin = {'1', '2', '3', '4'};
@@ -173,6 +175,15 @@ public class BluetoothTestRunner extends InstrumentationTestRunner {
}
}
+ val = arguments.getString("mce_set_message_status_iterations");
+ if (val != null) {
+ try {
+ sMceSetMessageStatusIterations = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ // Invalid argument, fall back to default value
+ }
+ }
+
val = arguments.getString("device_address");
if (val != null) {
sDeviceAddress = val;
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index ed613c36b89b..409025bc670d 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -56,6 +56,10 @@ public class BluetoothTestUtils extends Assert {
private static final int CONNECT_PROXY_TIMEOUT = 5000;
/** Time between polls in ms. */
private static final int POLL_TIME = 100;
+ /** Timeout to get map message in ms. */
+ private static final int GET_UNREAD_MESSAGE_TIMEOUT = 10000;
+ /** Timeout to set map message status in ms. */
+ private static final int SET_MESSAGE_STATUS_TIMEOUT = 2000;
private abstract class FlagReceiver extends BroadcastReceiver {
private int mExpectedFlags = 0;
@@ -98,6 +102,8 @@ public class BluetoothTestUtils extends Assert {
private static final int STATE_TURNING_ON_FLAG = 1 << 6;
private static final int STATE_ON_FLAG = 1 << 7;
private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+ private static final int STATE_GET_MESSAGE_FINISHED_FLAG = 1 << 9;
+ private static final int STATE_SET_MESSAGE_STATUS_FINISHED_FLAG = 1 << 10;
public BluetoothReceiver(int expectedFlags) {
super(expectedFlags);
@@ -231,6 +237,9 @@ public class BluetoothTestUtils extends Assert {
case BluetoothProfile.PAN:
mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED;
break;
+ case BluetoothProfile.MAP_CLIENT:
+ mConnectionAction = BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED;
+ break;
default:
mConnectionAction = null;
}
@@ -308,6 +317,34 @@ public class BluetoothTestUtils extends Assert {
}
}
+
+ private class MceSetMessageStatusReceiver extends FlagReceiver {
+ private static final int MESSAGE_RECEIVED_FLAG = 1;
+ private static final int STATUS_CHANGED_FLAG = 1 << 1;
+
+ public MceSetMessageStatusReceiver(int expectedFlags) {
+ super(expectedFlags);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (BluetoothMapClient.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
+ String handle = intent.getStringExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE);
+ assertNotNull(handle);
+ setFiredFlag(MESSAGE_RECEIVED_FLAG);
+ mMsgHandle = handle;
+ } else if (BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED.equals(intent.getAction())) {
+ int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
+ assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
+ setFiredFlag(STATUS_CHANGED_FLAG);
+ } else if (BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED.equals(intent.getAction())) {
+ int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
+ assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
+ setFiredFlag(STATUS_CHANGED_FLAG);
+ }
+ }
+ }
+
private BluetoothProfile.ServiceListener mServiceListener =
new BluetoothProfile.ServiceListener() {
@Override
@@ -326,6 +363,9 @@ public class BluetoothTestUtils extends Assert {
case BluetoothProfile.PAN:
mPan = (BluetoothPan) proxy;
break;
+ case BluetoothProfile.MAP_CLIENT:
+ mMce = (BluetoothMapClient) proxy;
+ break;
}
}
}
@@ -346,6 +386,9 @@ public class BluetoothTestUtils extends Assert {
case BluetoothProfile.PAN:
mPan = null;
break;
+ case BluetoothProfile.MAP_CLIENT:
+ mMce = null;
+ break;
}
}
}
@@ -362,6 +405,8 @@ public class BluetoothTestUtils extends Assert {
private BluetoothHeadset mHeadset = null;
private BluetoothHidHost mInput = null;
private BluetoothPan mPan = null;
+ private BluetoothMapClient mMce = null;
+ private String mMsgHandle = null;
/**
* Creates a utility instance for testing Bluetooth.
@@ -898,7 +943,7 @@ public class BluetoothTestUtils extends Assert {
* @param adapter The BT adapter.
* @param device The remote device.
* @param profile The profile to connect. One of {@link BluetoothProfile#A2DP},
- * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}.
+ * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#HID_HOST} or {@link BluetoothProfile#MAP_CLIENT}..
* @param methodName The method name to printed in the logs. If null, will be
* "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
*/
@@ -941,6 +986,8 @@ public class BluetoothTestUtils extends Assert {
assertTrue(((BluetoothHeadset)proxy).connect(device));
} else if (profile == BluetoothProfile.HID_HOST) {
assertTrue(((BluetoothHidHost)proxy).connect(device));
+ } else if (profile == BluetoothProfile.MAP_CLIENT) {
+ assertTrue(((BluetoothMapClient)proxy).connect(device));
}
break;
default:
@@ -1016,6 +1063,8 @@ public class BluetoothTestUtils extends Assert {
assertTrue(((BluetoothHeadset)proxy).disconnect(device));
} else if (profile == BluetoothProfile.HID_HOST) {
assertTrue(((BluetoothHidHost)proxy).disconnect(device));
+ } else if (profile == BluetoothProfile.MAP_CLIENT) {
+ assertTrue(((BluetoothMapClient)proxy).disconnect(device));
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
@@ -1373,6 +1422,89 @@ public class BluetoothTestUtils extends Assert {
}
}
+ public void mceGetUnreadMessage(BluetoothAdapter adapter, BluetoothDevice device) {
+ int mask;
+ String methodName = "getUnreadMessage";
+
+ if (!adapter.isEnabled()) {
+ fail(String.format("%s bluetooth not enabled", methodName));
+ }
+
+ if (!adapter.getBondedDevices().contains(device)) {
+ fail(String.format("%s device not paired", methodName));
+ }
+
+ mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
+ assertNotNull(mMce);
+
+ if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
+ fail(String.format("%s device is not connected", methodName));
+ }
+
+ mMsgHandle = null;
+ mask = MceSetMessageStatusReceiver.MESSAGE_RECEIVED_FLAG;
+ MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
+ assertTrue(mMce.getUnreadMessages(device));
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < GET_UNREAD_MESSAGE_TIMEOUT) {
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ writeOutput(String.format("%s completed", methodName));
+ removeReceiver(receiver);
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
+ methodName, mMce.getConnectionState(device), BluetoothMapClient.STATE_CONNECTED, firedFlags, mask));
+ }
+
+ /**
+ * Set a message to read/unread/deleted/undeleted
+ */
+ public void mceSetMessageStatus(BluetoothAdapter adapter, BluetoothDevice device, int status) {
+ int mask;
+ String methodName = "setMessageStatus";
+
+ if (!adapter.isEnabled()) {
+ fail(String.format("%s bluetooth not enabled", methodName));
+ }
+
+ if (!adapter.getBondedDevices().contains(device)) {
+ fail(String.format("%s device not paired", methodName));
+ }
+
+ mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
+ assertNotNull(mMce);
+
+ if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
+ fail(String.format("%s device is not connected", methodName));
+ }
+
+ assertNotNull(mMsgHandle);
+ mask = MceSetMessageStatusReceiver.STATUS_CHANGED_FLAG;
+ MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
+
+ assertTrue(mMce.setMessageStatus(device, mMsgHandle, status));
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < SET_MESSAGE_STATUS_TIMEOUT) {
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ writeOutput(String.format("%s completed", methodName));
+ removeReceiver(receiver);
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
+ methodName, mMce.getConnectionState(device), BluetoothPan.STATE_CONNECTED, firedFlags, mask));
+ }
+
private void addReceiver(BroadcastReceiver receiver, String[] actions) {
IntentFilter filter = new IntentFilter();
for (String action: actions) {
@@ -1408,7 +1540,8 @@ public class BluetoothTestUtils extends Assert {
String[] actions = {
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED,
BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
- BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED};
+ BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED,
+ BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED};
ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile,
expectedFlags);
addReceiver(receiver, actions);
@@ -1430,6 +1563,16 @@ public class BluetoothTestUtils extends Assert {
return receiver;
}
+ private MceSetMessageStatusReceiver getMceSetMessageStatusReceiver(BluetoothDevice device,
+ int expectedFlags) {
+ String[] actions = {BluetoothMapClient.ACTION_MESSAGE_RECEIVED,
+ BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED,
+ BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED};
+ MceSetMessageStatusReceiver receiver = new MceSetMessageStatusReceiver(expectedFlags);
+ addReceiver(receiver, actions);
+ return receiver;
+ }
+
private void removeReceiver(BroadcastReceiver receiver) {
mContext.unregisterReceiver(receiver);
mReceivers.remove(receiver);
@@ -1456,6 +1599,10 @@ public class BluetoothTestUtils extends Assert {
if (mPan != null) {
return mPan;
}
+ case BluetoothProfile.MAP_CLIENT:
+ if (mMce != null) {
+ return mMce;
+ }
break;
default:
return null;
@@ -1483,6 +1630,11 @@ public class BluetoothTestUtils extends Assert {
sleep(POLL_TIME);
}
return mPan;
+ case BluetoothProfile.MAP_CLIENT:
+ while (mMce == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
+ sleep(POLL_TIME);
+ }
+ return mMce;
default:
return null;
}
diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp
index e9d5bb135e02..1edd9623ed2d 100644
--- a/core/tests/bugreports/Android.bp
+++ b/core/tests/bugreports/Android.bp
@@ -15,12 +15,18 @@
android_test {
name: "BugreportManagerTestCases",
srcs: ["src/**/*.java"],
+ data: [":bugreport_artifacts"],
libs: [
"android.test.runner",
"android.test.base",
],
static_libs: ["androidx.test.rules", "truth-prebuilt"],
+ test_suites: ["general-tests"],
sdk_version: "test_current",
platform_apis: true,
}
+filegroup {
+ name: "bugreport_artifacts",
+ srcs: ["config/test-sysconfig.xml"],
+}
diff --git a/core/tests/bugreports/AndroidTest.xml b/core/tests/bugreports/AndroidTest.xml
index 410ca6043583..2c2f0d69e347 100644
--- a/core/tests/bugreports/AndroidTest.xml
+++ b/core/tests/bugreports/AndroidTest.xml
@@ -21,11 +21,16 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="BugreportManagerTestCases.apk"/>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="test-sysconfig.xml" value="/system/etc/sysconfig/allow-br-from-tests.xml" />
+ <option name="push-file" key="BugreportManagerTestCases.apk" value="/system/priv-app/BugreportManagerTestCases.apk" />
+ <option name="abort-on-push-failure" value="true" />
+ <option name="cleanup" value="true" />
+ <option name="remount-system" value="true" />
</target_preparer>
-
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.os.bugreports.tests"/>
<!-- test-timeout unit is ms, value = 30 min -->
diff --git a/core/tests/bugreports/run.sh b/core/tests/bugreports/run.sh
deleted file mode 100755
index 010339836538..000000000000
--- a/core/tests/bugreports/run.sh
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/bash
-
-# 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.
-
-# Script to run bugreport unitests
-# Must run on a rooted device.
-# Must run lunch before running the script
-# Usage: ${ANDROID_BUILD_TOP}/frameworks/base/core/tests/bugreports/run.sh
-
-# NOTE: This script replaces the framework-sysconfig.xml on your device, so use with caution.
-# It tries to replace it when done, but if the script does not finish cleanly
-# (for e.g. force stopped mid-way) your device will be left in an inconsistent state.
-# Reflashing will restore the right config.
-
-TMP_SYS_CONFIG=/var/tmp/framework-sysconfig.xml
-
-if [[ -z $ANDROID_PRODUCT_OUT ]]; then
- echo "Please lunch before running this test."
- exit 0
-fi
-
-# Print every command to console.
-set -x
-
-make -j BugreportManagerTestCases &&
- adb root &&
- adb remount &&
- adb wait-for-device &&
- # Save the sysconfig file in a tmp location and push the test config in
- adb pull /system/etc/sysconfig/framework-sysconfig.xml "${TMP_SYS_CONFIG}" &&
- adb push $ANDROID_BUILD_TOP/frameworks/base/core/tests/bugreports/config/test-sysconfig.xml /system/etc/sysconfig/framework-sysconfig.xml &&
- # The test app needs to be a priv-app.
- adb push $OUT/testcases/BugreportManagerTestCases/*/BugreportManagerTestCases.apk /system/priv-app ||
- exit 1
-
-adb reboot &&
-adb wait-for-device &&
-atest BugreportManagerTest || echo "Tests FAILED!"
-
-# Restore the saved config file
-if [ -f "${TMP_SYS_CONFIG}" ]; then
- SIZE=$(stat --printf="%s" "${TMP_SYS_CONFIG}")
- if [ SIZE > 0 ]; then
- adb remount &&
- adb wait-for-device &&
- adb push "${TMP_SYS_CONFIG}" /system/etc/sysconfig/framework-sysconfig.xml &&
- rm "${TMP_SYS_CONFIG}"
- fi
-fi
diff --git a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index c72707db9560..153337727e96 100644
--- a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -58,6 +58,8 @@ public class BugreportManagerTest {
private Handler mHandler;
private Executor mExecutor;
private BugreportManager mBrm;
+ private File mBugreportFile;
+ private File mScreenshotFile;
private ParcelFileDescriptor mBugreportFd;
private ParcelFileDescriptor mScreenshotFd;
@@ -73,8 +75,10 @@ public class BugreportManagerTest {
};
mBrm = getBugreportManager();
- mBugreportFd = parcelFd("bugreport_" + name.getMethodName(), ".zip");
- mScreenshotFd = parcelFd("screenshot_" + name.getMethodName(), ".png");
+ mBugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
+ mScreenshotFile = createTempFile("screenshot_" + name.getMethodName(), ".png");
+ mBugreportFd = parcelFd(mBugreportFile);
+ mScreenshotFd = parcelFd(mScreenshotFile);
getPermissions();
}
@@ -121,6 +125,21 @@ public class BugreportManagerTest {
}
@Test
+ public void normalFlow_full() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback);
+
+ waitTillDoneOrTimeout(callback);
+ assertThat(callback.isDone()).isTrue();
+ assertThat(callback.getErrorCode()).isEqualTo(
+ BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+ // bugreport and screenshot files should be empty when user consent timed out.
+ assertThat(mBugreportFile.length()).isEqualTo(0);
+ assertThat(mScreenshotFile.length()).isEqualTo(0);
+ assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+ }
+
+ @Test
public void simultaneousBugreportsNotAllowed() throws Exception {
// Start bugreport #1
BugreportCallbackImpl callback = new BugreportCallbackImpl();
@@ -129,9 +148,10 @@ public class BugreportManagerTest {
// Before #1 is done, try to start #2.
assertThat(callback.isDone()).isFalse();
BugreportCallbackImpl callback2 = new BugreportCallbackImpl();
- ParcelFileDescriptor bugreportFd2 = parcelFd("bugreport_2_" + name.getMethodName(), ".zip");
- ParcelFileDescriptor screenshotFd2 =
- parcelFd("screenshot_2_" + name.getMethodName(), ".png");
+ File bugreportFile2 = createTempFile("bugreport_2_" + name.getMethodName(), ".zip");
+ File screenshotFile2 = createTempFile("screenshot_2_" + name.getMethodName(), ".png");
+ ParcelFileDescriptor bugreportFd2 = parcelFd(bugreportFile2);
+ ParcelFileDescriptor screenshotFd2 = parcelFd(screenshotFile2);
mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2);
Thread.sleep(500 /* .5s */);
@@ -271,12 +291,16 @@ public class BugreportManagerTest {
return bm;
}
- private static ParcelFileDescriptor parcelFd(String prefix, String extension) throws Exception {
- File f = File.createTempFile(prefix, extension);
+ private static File createTempFile(String prefix, String extension) throws Exception {
+ final File f = File.createTempFile(prefix, extension);
f.setReadable(true, true);
f.setWritable(true, true);
+ f.deleteOnExit();
+ return f;
+ }
- return ParcelFileDescriptor.open(f,
+ private static ParcelFileDescriptor parcelFd(File file) throws Exception {
+ return ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
}
@@ -342,4 +366,13 @@ public class BugreportManagerTest {
private static BugreportParams interactive() {
return new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE);
}
+
+ /*
+ * Returns a {@link BugreportParams} for full bugreport that includes a screenshot.
+ *
+ * <p> This can take on the order of minutes to finish
+ */
+ private static BugreportParams full() {
+ return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
+ }
}
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index 17d1389a6602..777f4a3e03a8 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.ActivityThread;
@@ -180,4 +181,26 @@ public class ContextTest {
VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
return virtualDisplay.getDisplay();
}
+
+ @Test
+ public void testIsUiContext_ContextWrapper() {
+ ContextWrapper wrapper = new ContextWrapper(null /* base */);
+
+ assertFalse(wrapper.isUiContext());
+
+ wrapper = new ContextWrapper(new TestUiContext());
+
+ assertTrue(wrapper.isUiContext());
+ }
+
+ private static class TestUiContext extends ContextWrapper {
+ TestUiContext() {
+ super(null /* base */);
+ }
+
+ @Override
+ public boolean isUiContext() {
+ return true;
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/graphics/PaintNativeInstanceTest.kt b/core/tests/coretests/src/android/graphics/PaintNativeInstanceTest.kt
new file mode 100644
index 000000000000..ac88601a83f0
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/PaintNativeInstanceTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.Callable
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+
+// Verify that various calls to getNativeInstance do not deadlock or otherwise fail.
+@RunWith(AndroidJUnit4::class)
+class PaintNativeInstanceTest {
+
+ // Force a GC after each test, so that if there was a double free, it would happen now, rather
+ // than later during other tests.
+ @After
+ fun runGcAndFinalizersSync() {
+ Runtime.getRuntime().gc()
+ Runtime.getRuntime().runFinalization()
+ val fence = CountDownLatch(1)
+ object : Any() {
+ @Throws(Throwable::class)
+ protected fun finalize() = fence.countDown()
+ }
+ try {
+ do {
+ Runtime.getRuntime().gc()
+ Runtime.getRuntime().runFinalization()
+ } while (!fence.await(100, TimeUnit.MILLISECONDS))
+ } catch (ex: InterruptedException) {
+ throw RuntimeException(ex)
+ }
+ }
+
+ private fun setupComposeShader(test: (Paint, ComposeShader, Shader, Shader) -> Unit) {
+ val size = 255f
+ val blue = LinearGradient(0f, 0f, size, 0f, Color.GREEN, Color.BLUE,
+ Shader.TileMode.MIRROR)
+ val red = LinearGradient(0f, 0f, 0f, size, Color.GREEN, Color.RED,
+ Shader.TileMode.MIRROR)
+ val compose = ComposeShader(blue, red, BlendMode.SCREEN)
+ val paint = Paint().apply {
+ shader = compose
+ }
+ test(paint, compose, blue, red)
+ }
+
+ // Change the matrix arbitrarily to invalidate the shader.
+ private fun Shader.changeMatrix() {
+ val matrix = Matrix().apply {
+ setScale(2f, 2f)
+ }
+ setLocalMatrix(matrix)
+ }
+
+ @Test
+ fun testUnchangedPaintNativeInstance() = setupComposeShader {
+ paint, compose, shaderA, shaderB ->
+ val nativeInstance = paint.nativeInstance
+ for (shader in listOf(compose, shaderA, shaderB)) {
+ shader.changeMatrix()
+ // Although the shader is invalidated, the Paint's nativeInstance remains the same.
+ assertEquals(nativeInstance, paint.nativeInstance)
+ }
+ }
+
+ @Test
+ fun testInvalidateSubShader() = setupComposeShader {
+ paint, compose, shaderA, shaderB ->
+ // Trigger the creation of native objects.
+ shaderA.nativeInstance
+ compose.nativeInstance
+ val instanceB = shaderB.nativeInstance
+
+ // Changing shaderA's matrix invalidates shaderA and compose. A new instance will be lazily
+ // created for each of them. We cannot assert that the new nativeInstance does not match,
+ // since it might be allocated at the same location. But we can verify that shaderB did not
+ // change, and that there was no deadlock.
+ shaderA.changeMatrix()
+ assertEquals(instanceB, shaderB.nativeInstance)
+ paint.nativeInstance
+ }
+
+ @Test
+ fun testInvalidateSubShaderDraw() = setupComposeShader {
+ paint, _, _, shaderB ->
+
+ val original = PaintTask(paint).call()
+
+ // Change one of the subshaders and verify that the paint now draws differently.
+ shaderB.changeMatrix()
+ val changed = PaintTask(paint).call()
+ assertFalse(changed.sameAs(original))
+ }
+
+ /*
+ * This task will trigger the creation of native objects, if they have not already been
+ * created.
+ */
+ class PaintTask(private val mPaint: Paint) : Callable<Bitmap> {
+ private val size = 255 // matches size of gradients in setupComposeShader
+ override fun call(): Bitmap = Bitmap.createBitmap(size, size,
+ Bitmap.Config.ARGB_8888).apply {
+ val canvas = Canvas(this)
+ canvas.drawPaint(mPaint)
+ }
+ }
+
+ @Test
+ fun testMultiThreadShader() = setupComposeShader {
+ paint, _, _, _ ->
+ // Create an arbitrary number of tasks and try to start them at approximately the same time.
+ // They will race to create the native objects, but this should be safe.
+ val tasks = List(5) { PaintTask(paint) }
+ val results = Executors.newCachedThreadPool().invokeAll(tasks)
+ var expectedBitmap: Bitmap? = null
+ for (result in results) {
+ if (expectedBitmap == null) {
+ expectedBitmap = result.get()
+ } else {
+ assertTrue(expectedBitmap.sameAs(result.get()))
+ }
+ }
+ }
+
+ @Test
+ fun testMultiThreadColorFilter() {
+ val paint = Paint().apply {
+ color = Color.MAGENTA
+ colorFilter = LightingColorFilter(Color.BLUE, Color.GREEN)
+ }
+ // Create an arbitrary number of tasks and try to start them at approximately the same time.
+ // They will race to create the native objects, but this should be safe.
+ val tasks = List(5) { PaintTask(paint) }
+ val results = Executors.newCachedThreadPool().invokeAll(tasks)
+ for (result in results) {
+ assertEquals(Color.CYAN, result.get().getPixel(0, 0))
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
index c6d6d1ff90d5..b50792ca6b38 100644
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ b/core/tests/coretests/src/android/graphics/PathTest.java
@@ -28,7 +28,9 @@ public class PathTest extends TestCase {
final Path.FillType defaultFillType = path.getFillType();
final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
- assertFalse(fillType.equals(defaultFillType)); // Sanity check for the test itself.
+
+ // This test is only meaningful if it changes from the default.
+ assertFalse(fillType.equals(defaultFillType));
path.setFillType(fillType);
path.reset();
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index f20220c4ab9b..e083b0d460a2 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -136,6 +136,23 @@ public class UriTest extends TestCase {
assertEquals(0, b.compareTo(b2));
}
+ /**
+ * Check that {@link Uri#EMPTY} is properly initialized to guard against a
+ * regression based on a problematic initialization order (b/159907422).
+ *
+ * The problematic initialization order happened when {@code Uri$PathPart<clinit>}
+ * ran before {@code Uri.<clinit>}. De facto this test would probably never have
+ * failed on Android because {@code Uri.<clinit>} will almost certainly have run
+ * somewhere in the Zygote, but just in case and in case this test is ever run on
+ * a platform that doesn't perform Zygote initialization, this test attempts to
+ * trigger {@code Uri$PathPart<clinit>} prior to inspecting {@link Uri#EMPTY}.
+ */
+ @SmallTest
+ public void testEmpty_initializerOrder() {
+ new Uri.Builder().scheme("http").path("path").build();
+ assertEquals("", Uri.EMPTY.toString());
+ }
+
@SmallTest
public void testEqualsAndHashCode() {
diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
index d00d052db590..0af8c728aba3 100644
--- a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
+++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
@@ -154,6 +154,29 @@ public class EuiccProfileInfoTest {
}
@Test
+ public void testBuilder_BasedOnAnotherProfileWithEmptyAccessRules() {
+ EuiccProfileInfo p =
+ new EuiccProfileInfo.Builder("21430000000000006587")
+ .setNickname("profile nickname")
+ .setProfileName("profile name")
+ .setServiceProviderName("service provider")
+ .setCarrierIdentifier(
+ new CarrierIdentifier(
+ new byte[] {0x23, 0x45, 0x67},
+ "123",
+ "45"))
+ .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+ .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+ .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+ .setUiccAccessRule(null)
+ .build();
+
+ EuiccProfileInfo copied = new EuiccProfileInfo.Builder(p).build();
+
+ assertEquals(null, copied.getUiccAccessRules());
+ }
+
+ @Test
public void testEqualsHashCode() {
EuiccProfileInfo p =
new EuiccProfileInfo.Builder("21430000000000006587")
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index 5a0a84db5905..a3434e885012 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -16,9 +16,12 @@
package android.text.format;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import android.icu.text.DateFormatSymbols;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -27,6 +30,7 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.Locale;
@Presubmit
@@ -55,4 +59,80 @@ public class DateFormatTest {
assertFalse(DateFormat.is24HourLocale(Locale.US));
assertTrue(DateFormat.is24HourLocale(Locale.GERMANY));
}
+
+ @Test
+ public void testgetIcuDateFormatSymbols() {
+ DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.US);
+ assertEquals("AM", dfs.getAmPmStrings()[0]);
+ assertEquals("PM", dfs.getAmPmStrings()[1]);
+ assertEquals("a", dfs.getAmpmNarrowStrings()[0]);
+ assertEquals("p", dfs.getAmpmNarrowStrings()[1]);
+ }
+
+ @Test
+ public void testGetDateFormatOrder() {
+ // lv and fa use differing orders depending on whether you're using numeric or
+ // textual months.
+ Locale lv = new Locale("lv");
+ assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(lv, "yyyy-M-dd"))));
+ assertEquals("[y, d, M]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(lv, "yyyy-MMM-dd"))));
+ assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(lv, "MMM-dd"))));
+ Locale fa = new Locale("fa");
+ assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(fa, "yyyy-M-dd"))));
+ assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(fa, "yyyy-MMM-dd"))));
+ assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(fa, "MMM-dd"))));
+
+ // English differs on each side of the Atlantic.
+ Locale enUS = Locale.US;
+ assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(enUS, "yyyy-M-dd"))));
+ assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(enUS, "yyyy-MMM-dd"))));
+ assertEquals("[M, d, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(enUS, "MMM-dd"))));
+ Locale enGB = Locale.UK;
+ assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(enGB, "yyyy-M-dd"))));
+ assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(enGB, "yyyy-MMM-dd"))));
+ assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder(
+ best(enGB, "MMM-dd"))));
+
+ assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder(
+ "yyyy - 'why' '' 'ddd' MMM-dd")));
+
+ try {
+ DateFormat.getDateFormatOrder("the quick brown fox jumped over the lazy dog");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ DateFormat.getDateFormatOrder("'");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ DateFormat.getDateFormatOrder("yyyy'");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ DateFormat.getDateFormatOrder("yyyy'MMM");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private static String best(Locale l, String skeleton) {
+ return DateFormat.getBestDateTimePattern(l, skeleton);
+ }
}
diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
new file mode 100644
index 000000000000..0f17d27048f3
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import static android.icu.util.TimeZone.GMT_ZONE;
+import static android.icu.util.ULocale.ENGLISH;
+import static android.text.format.DateIntervalFormat.formatDateRange;
+import static android.text.format.DateUtils.FORMAT_12HOUR;
+import static android.text.format.DateUtils.FORMAT_24HOUR;
+import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
+import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY;
+import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY;
+import static android.text.format.DateUtils.FORMAT_NO_YEAR;
+import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
+import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY;
+import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
+import static android.text.format.DateUtils.FORMAT_UTC;
+
+import static org.junit.Assert.assertEquals;
+
+import android.icu.util.Calendar;
+import android.icu.util.TimeZone;
+import android.icu.util.ULocale;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.BiFunction;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DateIntervalFormatTest {
+ private static final long MINUTE = 60 * 1000;
+ private static final long HOUR = 60 * MINUTE;
+ private static final long DAY = 24 * HOUR;
+ private static final long MONTH = 31 * DAY;
+ private static final long YEAR = 12 * MONTH;
+
+ // These are the old CTS tests for DateIntervalFormat.formatDateRange.
+ @Test
+ public void test_formatDateInterval() throws Exception {
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+
+ Calendar c = Calendar.getInstance(tz, ULocale.US);
+ c.set(Calendar.MONTH, Calendar.JANUARY);
+ c.set(Calendar.DAY_OF_MONTH, 19);
+ c.set(Calendar.HOUR_OF_DAY, 3);
+ c.set(Calendar.MINUTE, 30);
+ c.set(Calendar.SECOND, 15);
+ c.set(Calendar.MILLISECOND, 0);
+ long timeWithCurrentYear = c.getTimeInMillis();
+
+ c.set(Calendar.YEAR, 2009);
+ long fixedTime = c.getTimeInMillis();
+
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ long onTheHour = c.getTimeInMillis();
+
+ long noonDuration = (8 * 60 + 30) * 60 * 1000 - 15 * 1000;
+ long midnightDuration = (3 * 60 + 30) * 60 * 1000 + 15 * 1000;
+
+ ULocale de_DE = new ULocale("de", "DE");
+ ULocale en_US = new ULocale("en", "US");
+ ULocale es_ES = new ULocale("es", "ES");
+ ULocale es_US = new ULocale("es", "US");
+
+ assertEquals("Monday",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("January 19",
+ formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR,
+ FORMAT_SHOW_DATE));
+ assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME));
+ assertEquals("January 19, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR));
+ assertEquals("January 19",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_NO_YEAR));
+ assertEquals("January",
+ formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR,
+ FORMAT_NO_MONTH_DAY));
+ assertEquals("3:30 AM",
+ formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_12HOUR | FORMAT_SHOW_TIME));
+ assertEquals("03:30",
+ formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_24HOUR | FORMAT_SHOW_TIME));
+ assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime,
+ FORMAT_12HOUR /*| FORMAT_CAP_AMPM*/ | FORMAT_SHOW_TIME));
+ assertEquals("12:00 PM",
+ formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration,
+ FORMAT_12HOUR | FORMAT_SHOW_TIME));
+ assertEquals("12:00 PM",
+ formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration,
+ FORMAT_12HOUR | FORMAT_SHOW_TIME /*| FORMAT_CAP_NOON*/));
+ assertEquals("12:00 PM",
+ formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration,
+ FORMAT_12HOUR /*| FORMAT_NO_NOON*/ | FORMAT_SHOW_TIME));
+ assertEquals("12:00 AM", formatDateRange(en_US, tz, fixedTime - midnightDuration,
+ fixedTime - midnightDuration,
+ FORMAT_12HOUR | FORMAT_SHOW_TIME /*| FORMAT_NO_MIDNIGHT*/));
+ assertEquals("3:30 AM",
+ formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME | FORMAT_UTC));
+ assertEquals("3 AM", formatDateRange(en_US, tz, onTheHour, onTheHour,
+ FORMAT_SHOW_TIME | FORMAT_ABBREV_TIME));
+ assertEquals("Mon", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_WEEKDAY));
+ assertEquals("Jan 19",
+ formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH));
+ assertEquals("Jan 19",
+ formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+
+ assertEquals("1/19/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * HOUR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("1/19/2009 – 1/22/2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("1/19/2009 – 4/22/2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("1/19/2009 – 2/9/2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+
+ assertEquals("19.1.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + HOUR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.–22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01. – 22.04.2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01.2009 – 09.02.2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+
+ assertEquals("19/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/1/2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/4/2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–9/2/2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+
+ assertEquals("19/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + HOUR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/1/2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/4/2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–9/2/2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+
+ // These are some random other test cases I came up with.
+
+ assertEquals("January 19 – 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("Jan 19 – 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mon, Jan 19 – Thu, Jan 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Monday, January 19 – Thursday, January 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+
+ assertEquals("January 19 – April 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("Jan 19 – Apr 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mon, Jan 19 – Wed, Apr 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("January – April 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+
+ assertEquals("Jan 19, 2009 – Feb 9, 2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Jan 2009 – Feb 2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("January 19, 2009 – February 9, 2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("Monday, January 19, 2009 – Thursday, February 9, 2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+
+ // The same tests but for de_DE.
+
+ assertEquals("19.–22. Januar 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19.–22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mo., 19. – Do., 22. Jan. 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Montag, 19. – Donnerstag, 22. Januar 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+
+ assertEquals("19. Januar – 22. April 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19. Jan. – 22. Apr. 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mo., 19. Jan. – Mi., 22. Apr. 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Januar–April 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+
+ assertEquals("19. Jan. 2009 – 9. Feb. 2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Jan. 2009 – Feb. 2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19. Januar 2009 – 9. Februar 2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("Montag, 19. Januar 2009 – Donnerstag, 9. Februar 2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+
+ // The same tests but for es_US.
+
+ assertEquals("19–22 de enero de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19–22 de ene. de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+
+ assertEquals("19 de enero–22 de abril de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 de ene. – 22 de abr. 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("enero–abril de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+
+ assertEquals("19 de ene. de 2009 – 9 de feb. de 2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("ene. de 2009 – feb. de 2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 de enero de 2009–9 de febrero de 2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+
+ // The same tests but for es_ES.
+
+ assertEquals("19–22 de enero de 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene. – jue., 22 ene. 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+
+ assertEquals("19 de enero–22 de abril de 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 ene. – 22 abr. 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene. – mié., 22 abr. 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("enero–abril de 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+
+ assertEquals("19 ene. 2009 – 9 feb. 2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("ene. 2009 – feb. 2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 de enero de 2009–9 de febrero de 2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ }
+
+ // http://b/8862241 - we should be able to format dates past 2038.
+ // See also http://code.google.com/p/android/issues/detail?id=13050.
+ @Test
+ public void test8862241() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar c = Calendar.getInstance(tz, l);
+ c.clear();
+ c.set(2042, Calendar.JANUARY, 19, 3, 30);
+ long jan_19_2042 = c.getTimeInMillis();
+ c.set(2046, Calendar.OCTOBER, 4, 3, 30);
+ long oct_4_2046 = c.getTimeInMillis();
+ int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL;
+ assertEquals("Jan 19, 2042 – Oct 4, 2046",
+ formatDateRange(l, tz, jan_19_2042, oct_4_2046, flags));
+ }
+
+ // http://b/10089890 - we should take the given time zone into account.
+ @Test
+ public void test10089890() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
+ int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL | FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
+ // The Unix epoch is UTC, so 0 is 1970-01-01T00:00Z...
+ assertEquals("Jan 1, 1970, 00:00 – Jan 2, 1970, 00:00",
+ formatDateRange(l, utc, 0, DAY + 1, flags));
+ // But MTV is hours behind, so 0 was still the afternoon of the previous day...
+ assertEquals("Dec 31, 1969, 16:00 – Jan 1, 1970, 16:00",
+ formatDateRange(l, pacific, 0, DAY, flags));
+ }
+
+ // http://b/10318326 - we can drop the minutes in a 12-hour time if they're zero,
+ // but not if we're using the 24-hour clock. That is: "4 PM" is reasonable, "16" is not.
+ @Test
+ public void test10318326() throws Exception {
+ long midnight = 0;
+ long teaTime = 16 * HOUR;
+
+ int time12 = FORMAT_12HOUR | FORMAT_SHOW_TIME;
+ int time24 = FORMAT_24HOUR | FORMAT_SHOW_TIME;
+ int abbr12 = time12 | FORMAT_ABBREV_ALL;
+ int abbr24 = time24 | FORMAT_ABBREV_ALL;
+
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ // Full length on-the-hour times.
+ assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, time24));
+ assertEquals("12:00 AM", formatDateRange(l, utc, midnight, midnight, time12));
+ assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, time24));
+ assertEquals("4:00 PM", formatDateRange(l, utc, teaTime, teaTime, time12));
+
+ // Abbreviated on-the-hour times.
+ assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, abbr24));
+ assertEquals("12 AM", formatDateRange(l, utc, midnight, midnight, abbr12));
+ assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, abbr24));
+ assertEquals("4 PM", formatDateRange(l, utc, teaTime, teaTime, abbr12));
+
+ // Abbreviated on-the-hour ranges.
+ assertEquals("00:00 – 16:00", formatDateRange(l, utc, midnight, teaTime, abbr24));
+ assertEquals("12 AM – 4 PM", formatDateRange(l, utc, midnight, teaTime, abbr12));
+
+ // Abbreviated mixed ranges.
+ assertEquals("00:00 – 16:01", formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr24));
+ assertEquals("12:00 AM – 4:01 PM",
+ formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr12));
+ }
+
+ // http://b/10560853 - when the time is not displayed, an end time 0 ms into the next day is
+ // considered to belong to the previous day.
+ @Test
+ public void test10560853_when_time_not_displayed() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ long midnight = 0;
+ long midnightNext = 1 * DAY;
+
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY;
+
+ // An all-day event runs until 0 milliseconds into the next day, but is formatted as if it's
+ // just the first day.
+ assertEquals("Thursday, January 1, 1970",
+ formatDateRange(l, utc, midnight, midnightNext, flags));
+
+ // Run one millisecond over, though, and you're into the next day.
+ long nextMorning = 1 * DAY + 1;
+ assertEquals("Thursday, January 1 – Friday, January 2, 1970",
+ formatDateRange(l, utc, midnight, nextMorning, flags));
+
+ // But the same reasoning applies for that day.
+ long nextMidnight = 2 * DAY;
+ assertEquals("Thursday, January 1 – Friday, January 2, 1970",
+ formatDateRange(l, utc, midnight, nextMidnight, flags));
+ }
+
+ // http://b/10560853 - when the start and end times are otherwise on the same day,
+ // an end time 0 ms into the next day is considered to belong to the previous day.
+ @Test
+ public void test10560853_for_single_day_events() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
+
+ assertEquals("January 1, 1970, 22:00 – 00:00",
+ formatDateRange(l, utc, 22 * HOUR, 24 * HOUR, flags));
+ assertEquals("January 1, 1970, 22:00 – January 2, 1970, 00:30",
+ formatDateRange(l, utc, 22 * HOUR, 24 * HOUR + 30 * MINUTE, flags));
+ }
+
+ // The fix for http://b/10560853 didn't work except for the day around the epoch, which was
+ // all the unit test checked!
+ @Test
+ public void test_single_day_events_later_than_epoch() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
+
+ Calendar c = Calendar.getInstance(utc, l);
+ c.clear();
+ c.set(1980, Calendar.JANUARY, 1, 0, 0);
+ long jan_1_1980 = c.getTimeInMillis();
+ assertEquals("January 1, 1980, 22:00 – 00:00",
+ formatDateRange(l, utc, jan_1_1980 + 22 * HOUR, jan_1_1980 + 24 * HOUR, flags));
+ assertEquals("January 1, 1980, 22:00 – January 2, 1980, 00:30",
+ formatDateRange(l, utc, jan_1_1980 + 22 * HOUR,
+ jan_1_1980 + 24 * HOUR + 30 * MINUTE, flags));
+ }
+
+ // The fix for http://b/10560853 didn't work except for UTC, which was
+ // all the unit test checked!
+ @Test
+ public void test_single_day_events_not_in_UTC() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
+
+ int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
+
+ Calendar c = Calendar.getInstance(pacific, l);
+ c.clear();
+ c.set(1980, Calendar.JANUARY, 1, 0, 0);
+ long jan_1_1980 = c.getTimeInMillis();
+ assertEquals("January 1, 1980, 22:00 – 00:00",
+ formatDateRange(l, pacific, jan_1_1980 + 22 * HOUR, jan_1_1980 + 24 * HOUR, flags));
+
+ c.set(1980, Calendar.JULY, 1, 0, 0);
+ long jul_1_1980 = c.getTimeInMillis();
+ assertEquals("July 1, 1980, 22:00 – 00:00",
+ formatDateRange(l, pacific, jul_1_1980 + 22 * HOUR, jul_1_1980 + 24 * HOUR, flags));
+ }
+
+ // http://b/10209343 - even if the caller didn't explicitly ask us to include the year,
+ // we should do so for years other than the current year.
+ @Test
+ public void test10209343_when_not_this_year() {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
+ assertEquals("Thursday, January 1, 1970, 00:00", formatDateRange(l, utc, 0L, 0L, flags));
+
+ long t1833 = ((long) Integer.MIN_VALUE + Integer.MIN_VALUE) * 1000L;
+ assertEquals("Sunday, November 24, 1833, 17:31",
+ formatDateRange(l, utc, t1833, t1833, flags));
+
+ long t1901 = Integer.MIN_VALUE * 1000L;
+ assertEquals("Friday, December 13, 1901, 20:45",
+ formatDateRange(l, utc, t1901, t1901, flags));
+
+ long t2038 = Integer.MAX_VALUE * 1000L;
+ assertEquals("Tuesday, January 19, 2038, 03:14",
+ formatDateRange(l, utc, t2038, t2038, flags));
+
+ long t2106 = (2L + Integer.MAX_VALUE + Integer.MAX_VALUE) * 1000L;
+ assertEquals("Sunday, February 7, 2106, 06:28",
+ formatDateRange(l, utc, t2106, t2106, flags));
+ }
+
+ // http://b/10209343 - for the current year, we should honor the FORMAT_SHOW_YEAR flags.
+ @Test
+ public void test10209343_when_this_year() {
+ // Construct a date in the current year (whenever the test happens to be run).
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ Calendar c = Calendar.getInstance(utc, l);
+ c.set(Calendar.MONTH, Calendar.FEBRUARY);
+ c.set(Calendar.DAY_OF_MONTH, 10);
+ c.set(Calendar.HOUR_OF_DAY, 0);
+ long thisYear = c.getTimeInMillis();
+
+ // You don't get the year if it's this year...
+ assertEquals("February 10", formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE));
+
+ // ...unless you explicitly ask for it.
+ assertEquals(String.format("February 10, %d", c.get(Calendar.YEAR)),
+ formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR));
+
+ // ...or it's not actually this year...
+ Calendar c2 = (Calendar) c.clone();
+ c2.set(Calendar.YEAR, 1980);
+ long oldYear = c2.getTimeInMillis();
+ assertEquals("February 10, 1980",
+ formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE));
+
+ // (But you can disable that!)
+ assertEquals("February 10",
+ formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR));
+
+ // ...or the start and end years aren't the same...
+ assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)),
+ formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE));
+
+ // (And you can't avoid that --- icu4c steps in and overrides you.)
+ assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)),
+ formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR));
+ }
+
+ // http://b/8467515 - yet another y2k38 bug report.
+ @Test
+ public void test8467515() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH
+ | FORMAT_ABBREV_WEEKDAY;
+ long t;
+
+ Calendar calendar = Calendar.getInstance(utc, l);
+ calendar.clear();
+
+ calendar.set(2038, Calendar.JANUARY, 19, 12, 0, 0);
+ t = calendar.getTimeInMillis();
+ assertEquals("Tue, Jan 19, 2038", formatDateRange(l, utc, t, t, flags));
+
+ calendar.set(1900, Calendar.JANUARY, 1, 0, 0, 0);
+ t = calendar.getTimeInMillis();
+ assertEquals("Mon, Jan 1, 1900", formatDateRange(l, utc, t, t, flags));
+ }
+
+ // http://b/12004664
+ @Test
+ public void test12004664() throws Exception {
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ Calendar c = Calendar.getInstance(utc, ULocale.US);
+ c.clear();
+ c.set(Calendar.YEAR, 1980);
+ c.set(Calendar.MONTH, Calendar.FEBRUARY);
+ c.set(Calendar.DAY_OF_MONTH, 10);
+ c.set(Calendar.HOUR_OF_DAY, 0);
+ long thisYear = c.getTimeInMillis();
+
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR;
+ assertEquals("Sunday, February 10, 1980",
+ formatDateRange(new ULocale("en", "US"), utc, thisYear, thisYear, flags));
+
+ // If we supported non-Gregorian calendars, this is what that we'd expect for these
+ // ULocales.
+ // This is really the correct behavior, but since java.util.Calendar currently only supports
+ // the Gregorian calendar, we want to deliberately force icu4c to agree, otherwise we'd have
+ // a mix of calendars throughout an app's UI depending on whether Java or native code
+ // formatted
+ // the date.
+ // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه‍.ش.", formatDateRange(new ULocale("fa"), utc,
+ // thisYear, thisYear, flags));
+ // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new ULocale("ps"), utc,
+ // thisYear, thisYear, flags));
+ // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new ULocale("th"), utc,
+ // thisYear, thisYear, flags));
+
+ // For now, here are the localized Gregorian strings instead...
+ assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰",
+ formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags));
+ assertEquals("يونۍ د ۱۹۸۰ د فبروري ۱۰",
+ formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags));
+ assertEquals("วันอาทิตย์ที่ 10 กุมภาพันธ์ ค.ศ. 1980",
+ formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags));
+ }
+
+ // http://b/13234532
+ @Test
+ public void test13234532() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ int flags = FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL | FORMAT_12HOUR;
+
+ assertEquals("10 – 11 AM", formatDateRange(l, utc, 10 * HOUR, 11 * HOUR, flags));
+ assertEquals("11 AM – 1 PM", formatDateRange(l, utc, 11 * HOUR, 13 * HOUR, flags));
+ assertEquals("2 – 3 PM", formatDateRange(l, utc, 14 * HOUR, 15 * HOUR, flags));
+ }
+
+ // http://b/20708022
+ @Test
+ public void testEndOfDayOnLastDayOfMonth() throws Exception {
+ final ULocale locale = new ULocale("en");
+ final TimeZone timeZone = TimeZone.getTimeZone("UTC");
+
+ assertEquals("11:00 PM – 12:00 AM", formatDateRange(locale, timeZone,
+ 1430434800000L, 1430438400000L, FORMAT_SHOW_TIME));
+ }
+
+ // http://b/68847519
+ @Test
+ public void testEndAtMidnight_dateAndTime() {
+ BiFunction<Long, Long, String> fmt = (from, to) -> formatDateRange(
+ ENGLISH, GMT_ZONE, from, to, FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_24HOUR);
+ // If we're showing times and the end-point is midnight the following day, we want the
+ // behaviour of suppressing the date for the end...
+ assertEquals("February 27, 2007, 04:00 – 00:00", fmt.apply(1172548800000L, 1172620800000L));
+ // ...unless the start-point is also midnight, in which case we need dates to disambiguate.
+ assertEquals("February 27, 2007, 00:00 – February 28, 2007, 00:00",
+ fmt.apply(1172534400000L, 1172620800000L));
+ // We want to show the date if the end-point is a millisecond after midnight the following
+ // day, or if it is exactly midnight the day after that.
+ assertEquals("February 27, 2007, 04:00 – February 28, 2007, 00:00",
+ fmt.apply(1172548800000L, 1172620800001L));
+ assertEquals("February 27, 2007, 04:00 – March 1, 2007, 00:00",
+ fmt.apply(1172548800000L, 1172707200000L));
+ // We want to show the date if the start-point is anything less than a minute after
+ // midnight,
+ // since that gets displayed as midnight...
+ assertEquals("February 27, 2007, 00:00 – February 28, 2007, 00:00",
+ fmt.apply(1172534459999L, 1172620800000L));
+ // ...but not if it is exactly one minute after midnight.
+ assertEquals("February 27, 2007, 00:01 – 00:00", fmt.apply(1172534460000L, 1172620800000L));
+ }
+
+ // http://b/68847519
+ @Test
+ public void testEndAtMidnight_dateOnly() {
+ BiFunction<Long, Long, String> fmt = (from, to) -> formatDateRange(
+ ENGLISH, GMT_ZONE, from, to, FORMAT_SHOW_DATE);
+ // If we're only showing dates and the end-point is midnight of any day, we want the
+ // behaviour of showing an end date one earlier. So if the end-point is March 2, 2007 00:00,
+ // show March 1, 2007 instead (whether the start-point is midnight or not).
+ assertEquals("February 27 – March 1, 2007", fmt.apply(1172534400000L, 1172793600000L));
+ assertEquals("February 27 – March 1, 2007", fmt.apply(1172548800000L, 1172793600000L));
+ // We want to show the true date if the end-point is a millisecond after midnight.
+ assertEquals("February 27 – March 2, 2007", fmt.apply(1172534400000L, 1172793600001L));
+
+ // 2006-02-27 00:00:00.000 GMT - 2007-03-02 00:00:00.000 GMT
+ assertEquals("February 27, 2006 – March 1, 2007",
+ fmt.apply(1140998400000L, 1172793600000L));
+
+ // Spans a leap year's Feb 29th.
+ assertEquals("February 27 – March 1, 2004", fmt.apply(1077840000000L, 1078185600000L));
+ }
+}
diff --git a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
new file mode 100644
index 000000000000..4b3b5735b4f3
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
+import static android.text.format.DateUtils.FORMAT_NO_YEAR;
+import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
+import static android.text.format.RelativeDateTimeFormatter.DAY_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.HOUR_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.MINUTE_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.SECOND_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.WEEK_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.YEAR_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.getRelativeDateTimeString;
+import static android.text.format.RelativeDateTimeFormatter.getRelativeTimeSpanString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RelativeDateTimeFormatterTest {
+
+ // Tests adopted from CTS tests for DateUtils.getRelativeTimeSpanString.
+ @Test
+ public void test_getRelativeTimeSpanStringCTS() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("GMT");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2015 at 10:50 GMT
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long baseTime = cal.getTimeInMillis();
+
+ assertEquals("0 minutes ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - SECOND_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("In 0 minutes",
+ getRelativeTimeSpanString(en_US, tz, baseTime + SECOND_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+
+ assertEquals("1 minute ago",
+ getRelativeTimeSpanString(en_US, tz, 0, MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 0));
+ assertEquals("In 1 minute",
+ getRelativeTimeSpanString(en_US, tz, MINUTE_IN_MILLIS, 0, MINUTE_IN_MILLIS, 0));
+
+ assertEquals("42 minutes ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - 42 * MINUTE_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("In 42 minutes",
+ getRelativeTimeSpanString(en_US, tz, baseTime + 42 * MINUTE_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+
+ final long TWO_HOURS_IN_MS = 2 * HOUR_IN_MILLIS;
+ assertEquals("2 hours ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - TWO_HOURS_IN_MS, baseTime,
+ MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+ assertEquals("In 2 hours",
+ getRelativeTimeSpanString(en_US, tz, baseTime + TWO_HOURS_IN_MS, baseTime,
+ MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+
+ assertEquals("In 42 min.",
+ getRelativeTimeSpanString(en_US, tz, baseTime + (42 * MINUTE_IN_MILLIS), baseTime,
+ MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+
+ assertEquals("Tomorrow",
+ getRelativeTimeSpanString(en_US, tz, DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+ assertEquals("In 2 days",
+ getRelativeTimeSpanString(en_US, tz, 2 * DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+ assertEquals("Yesterday",
+ getRelativeTimeSpanString(en_US, tz, 0, DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+ assertEquals("2 days ago",
+ getRelativeTimeSpanString(en_US, tz, 0, 2 * DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+
+ final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+ assertEquals("5 days ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+ DAY_IN_MILLIS, 0));
+ }
+
+ private void test_getRelativeTimeSpanString_helper(long delta, long minResolution, int flags,
+ String expectedInPast,
+ String expectedInFuture) throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2015 at 10:50 PST
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long base = cal.getTimeInMillis();
+
+ assertEquals(expectedInPast,
+ getRelativeTimeSpanString(en_US, tz, base - delta, base, minResolution, flags));
+ assertEquals(expectedInFuture,
+ getRelativeTimeSpanString(en_US, tz, base + delta, base, minResolution, flags));
+ }
+
+ private void test_getRelativeTimeSpanString_helper(long delta, long minResolution,
+ String expectedInPast,
+ String expectedInFuture) throws Exception {
+ test_getRelativeTimeSpanString_helper(delta, minResolution, 0, expectedInPast,
+ expectedInFuture);
+ }
+
+ @Test
+ public void test_getRelativeTimeSpanString() throws Exception {
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, "0 seconds ago",
+ "0 seconds ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, "5 days ago", "In 5 days");
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+ "0 seconds ago",
+ "0 seconds ago");
+ test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+ "1 second ago",
+ "In 1 second");
+ test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+ "2 seconds ago",
+ "In 2 seconds");
+ test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+ "25 seconds ago",
+ "In 25 seconds");
+ test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+ "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+ "1 hour ago",
+ "In 1 hour");
+
+ test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+ "0 minutes ago",
+ "0 minutes ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+ "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+ "2 minutes ago",
+ "In 2 minutes");
+ test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+ "25 minutes ago",
+ "In 25 minutes");
+ test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+ "12 hours ago",
+ "In 12 hours");
+
+ test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+ "0 hours ago");
+ test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "2 hours ago",
+ "In 2 hours");
+ test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "5 hours ago",
+ "In 5 hours");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "20 hours ago",
+ "In 20 hours");
+
+ test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+ "In 2 days");
+ test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, "January 11",
+ "March 2");
+
+ test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+ "0 weeks ago");
+ test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+ "In 1 week");
+ test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "2 weeks ago",
+ "In 2 weeks");
+ test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "25 weeks ago",
+ "In 25 weeks");
+
+ // duration >= minResolution
+ test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, "30 seconds ago",
+ "In 30 seconds");
+ test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+ "30 minutes ago", "In 30 minutes");
+ test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, "5 days ago",
+ "In 5 days");
+ test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS,
+ "July 10, 2014",
+ "September 3");
+ test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS,
+ "February 6, 2010", "February 4, 2020");
+
+ test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS,
+ "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS,
+ "1 minute ago", "In 1 minute");
+ test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS,
+ "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+ test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Today");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+ "In 2 days");
+ test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+ "In 2 days");
+ test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+ "In 1 week");
+ test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, "1 week ago",
+ "In 1 week");
+
+ // duration < minResolution
+ test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS,
+ "0 minutes ago",
+ "In 0 minutes");
+ test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+ "In 0 hours");
+ test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, "0 hours ago",
+ "In 0 hours");
+ test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+ "In 0 weeks");
+ test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, "0 weeks ago",
+ "In 0 weeks");
+ }
+
+ @Test
+ public void test_getRelativeTimeSpanStringAbbrev() throws Exception {
+ int flags = FORMAT_ABBREV_RELATIVE;
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, flags, "0 sec. ago",
+ "0 sec. ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, flags, "1 min. ago",
+ "In 1 min.");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, flags, "5 days ago",
+ "In 5 days");
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "0 sec. ago", "0 sec. ago");
+ test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "1 sec. ago", "In 1 sec.");
+ test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "2 sec. ago", "In 2 sec.");
+ test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "25 sec. ago", "In 25 sec.");
+ test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+
+ test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "0 min. ago", "0 min. ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "2 min. ago", "In 2 min.");
+ test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "25 min. ago", "In 25 min.");
+ test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "12 hr. ago", "In 12 hr.");
+
+ test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "0 hr. ago", "0 hr. ago");
+ test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "2 hr. ago", "In 2 hr.");
+ test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "5 hr. ago", "In 5 hr.");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "20 hr. ago", "In 20 hr.");
+
+ test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+ "Today");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "2 days ago", "In 2 days");
+ test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "January 11", "March 2");
+
+ test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "0 wk. ago", "0 wk. ago");
+ test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "1 wk. ago", "In 1 wk.");
+ test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "2 wk. ago", "In 2 wk.");
+ test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "25 wk. ago", "In 25 wk.");
+
+ // duration >= minResolution
+ test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, flags, "30 sec. ago",
+ "In 30 sec.");
+ test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "30 min. ago", "In 30 min.");
+ test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "5 days ago", "In 5 days");
+ test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "July 10, 2014", "September 3");
+ test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "February 6, 2010", "February 4, 2020");
+
+ test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+ "Today");
+ test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Today");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "2 days ago", "In 2 days");
+ test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "2 days ago", "In 2 days");
+ test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "1 wk. ago", "In 1 wk.");
+ test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+ "1 wk. ago", "In 1 wk.");
+
+ // duration < minResolution
+ test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "0 min. ago", "In 0 min.");
+ test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "0 hr. ago", "In 0 hr.");
+ test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+ "0 hr. ago", "In 0 hr.");
+ test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "0 wk. ago", "In 0 wk.");
+ test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+ "0 wk. ago", "In 0 wk.");
+
+ }
+
+ @Test
+ public void test_getRelativeTimeSpanStringGerman() throws Exception {
+ // Bug: 19744876
+ // We need to specify the timezone and the time explicitly. Otherwise it
+ // may not always give a correct answer of "tomorrow" by using
+ // (now + DAY_IN_MILLIS).
+ Locale de_DE = new Locale("de", "DE");
+ TimeZone tz = TimeZone.getTimeZone("Europe/Berlin");
+ Calendar cal = Calendar.getInstance(tz, de_DE);
+ // Feb 5, 2015 at 10:50 CET
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long now = cal.getTimeInMillis();
+
+ // 42 minutes ago
+ assertEquals("Vor 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+ now - 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+ // In 42 minutes
+ assertEquals("In 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+ now + 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+ // Yesterday
+ assertEquals("Gestern", getRelativeTimeSpanString(de_DE, tz,
+ now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day before yesterday
+ assertEquals("Vorgestern", getRelativeTimeSpanString(de_DE, tz,
+ now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // Tomorrow
+ assertEquals("Morgen", getRelativeTimeSpanString(de_DE, tz,
+ now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day after tomorrow
+ assertEquals("Übermorgen", getRelativeTimeSpanString(de_DE, tz,
+ now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ }
+
+ @Test
+ public void test_getRelativeTimeSpanStringFrench() throws Exception {
+ Locale fr_FR = new Locale("fr", "FR");
+ TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
+ Calendar cal = Calendar.getInstance(tz, fr_FR);
+ // Feb 5, 2015 at 10:50 CET
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long now = cal.getTimeInMillis();
+
+ // 42 minutes ago
+ assertEquals("Il y a 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+ now - (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+ // In 42 minutes
+ assertEquals("Dans 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+ now + (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+ // Yesterday
+ assertEquals("Hier", getRelativeTimeSpanString(fr_FR, tz,
+ now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day before yesterday
+ assertEquals("Avant-hier", getRelativeTimeSpanString(fr_FR, tz,
+ now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // Tomorrow
+ assertEquals("Demain", getRelativeTimeSpanString(fr_FR, tz,
+ now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day after tomorrow
+ assertEquals("Après-demain", getRelativeTimeSpanString(fr_FR, tz,
+ now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ }
+
+ // Tests adopted from CTS tests for DateUtils.getRelativeDateTimeString.
+ @Test
+ public void test_getRelativeDateTimeStringCTS() throws Exception {
+ Locale en_US = Locale.getDefault();
+ TimeZone tz = TimeZone.getDefault();
+ final long baseTime = System.currentTimeMillis();
+
+ final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+ assertNotNull(getRelativeDateTimeString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+ MINUTE_IN_MILLIS, DAY_IN_MILLIS,
+ FORMAT_NUMERIC_DATE));
+ }
+
+ @Test
+ public void test_getRelativeDateTimeString() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2015 at 10:50 PST
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long base = cal.getTimeInMillis();
+
+ assertEquals("5 seconds ago, 10:49 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("5 min. ago, 10:45 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+ HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("0 hr. ago, 10:45 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base,
+ HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("5 hours ago, 5:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base,
+ HOUR_IN_MILLIS, DAY_IN_MILLIS, 0));
+ assertEquals("Yesterday, 7:50 PM",
+ getRelativeDateTimeString(en_US, tz, base - 15 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("5 days ago, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("Jan 29, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 7 * DAY_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2014, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2014, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ YEAR_IN_MILLIS, 0));
+
+ // User-supplied flags should be ignored when formatting the date clause.
+ final int FORMAT_SHOW_WEEKDAY = 0x00002;
+ assertEquals("11/27/2014, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS,
+ FORMAT_ABBREV_ALL | FORMAT_SHOW_WEEKDAY));
+ }
+
+ @Test
+ public void test_getRelativeDateTimeStringDST() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+
+ // DST starts on Mar 9, 2014 at 2:00 AM.
+ // So 5 hours before 3:15 AM should be formatted as 'Yesterday, 9:15 PM'.
+ cal.set(2014, Calendar.MARCH, 9, 3, 15, 0);
+ long base = cal.getTimeInMillis();
+ assertEquals("Yesterday, 9:15 PM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+
+ // 1 hour after 2:00 AM should be formatted as 'In 1 hour, 4:00 AM'.
+ cal.set(2014, Calendar.MARCH, 9, 2, 0, 0);
+ base = cal.getTimeInMillis();
+ assertEquals("In 1 hour, 4:00 AM",
+ getRelativeDateTimeString(en_US, tz, base + 1 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+
+ // DST ends on Nov 2, 2014 at 2:00 AM. Clocks are turned backward 1 hour to
+ // 1:00 AM. 8 hours before 5:20 AM should be 'Yesterday, 10:20 PM'.
+ cal.set(2014, Calendar.NOVEMBER, 2, 5, 20, 0);
+ base = cal.getTimeInMillis();
+ assertEquals("Yesterday, 10:20 PM",
+ getRelativeDateTimeString(en_US, tz, base - 8 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+
+ cal.set(2014, Calendar.NOVEMBER, 2, 0, 45, 0);
+ base = cal.getTimeInMillis();
+ // 45 minutes after 0:45 AM should be 'In 45 minutes, 1:30 AM'.
+ assertEquals("In 45 minutes, 1:30 AM",
+ getRelativeDateTimeString(en_US, tz, base + 45 * MINUTE_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ // 45 minutes later, it should be 'In 45 minutes, 1:15 AM'.
+ assertEquals("In 45 minutes, 1:15 AM",
+ getRelativeDateTimeString(en_US, tz, base + 90 * MINUTE_IN_MILLIS,
+ base + 45 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+ // Another 45 minutes later, it should be 'In 45 minutes, 2:00 AM'.
+ assertEquals("In 45 minutes, 2:00 AM",
+ getRelativeDateTimeString(en_US, tz, base + 135 * MINUTE_IN_MILLIS,
+ base + 90 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+ }
+
+ @Test
+ public void test_getRelativeDateTimeStringItalian() throws Exception {
+ Locale it_IT = new Locale("it", "IT");
+ TimeZone tz = TimeZone.getTimeZone("Europe/Rome");
+ Calendar cal = Calendar.getInstance(tz, it_IT);
+ // 05 febbraio 2015 20:15
+ cal.set(2015, Calendar.FEBRUARY, 5, 20, 15, 0);
+ final long base = cal.getTimeInMillis();
+
+ assertEquals("5 secondi fa, 20:14",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("5 min fa, 20:10",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+ HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("0 h fa, 20:10",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base,
+ HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("Ieri, 22:15",
+ getRelativeDateTimeString(it_IT, tz, base - 22 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("5 giorni fa, 20:15",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("27/11/2014, 20:15",
+ getRelativeDateTimeString(it_IT, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ }
+
+ // http://b/5252772: detect the actual date difference
+ @Test
+ public void test5252772() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+
+ // Now is Sep 2, 2011, 10:23 AM PDT.
+ Calendar nowCalendar = Calendar.getInstance(tz, en_US);
+ nowCalendar.set(2011, Calendar.SEPTEMBER, 2, 10, 23, 0);
+ final long now = nowCalendar.getTimeInMillis();
+
+ // Sep 1, 2011, 10:24 AM
+ Calendar yesterdayCalendar1 = Calendar.getInstance(tz, en_US);
+ yesterdayCalendar1.set(2011, Calendar.SEPTEMBER, 1, 10, 24, 0);
+ long yesterday1 = yesterdayCalendar1.getTimeInMillis();
+ assertEquals("Yesterday, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, yesterday1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 1, 2011, 10:22 AM
+ Calendar yesterdayCalendar2 = Calendar.getInstance(tz, en_US);
+ yesterdayCalendar2.set(2011, Calendar.SEPTEMBER, 1, 10, 22, 0);
+ long yesterday2 = yesterdayCalendar2.getTimeInMillis();
+ assertEquals("Yesterday, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, yesterday2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Aug 31, 2011, 10:24 AM
+ Calendar twoDaysAgoCalendar1 = Calendar.getInstance(tz, en_US);
+ twoDaysAgoCalendar1.set(2011, Calendar.AUGUST, 31, 10, 24, 0);
+ long twoDaysAgo1 = twoDaysAgoCalendar1.getTimeInMillis();
+ assertEquals("2 days ago, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysAgo1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Aug 31, 2011, 10:22 AM
+ Calendar twoDaysAgoCalendar2 = Calendar.getInstance(tz, en_US);
+ twoDaysAgoCalendar2.set(2011, Calendar.AUGUST, 31, 10, 22, 0);
+ long twoDaysAgo2 = twoDaysAgoCalendar2.getTimeInMillis();
+ assertEquals("2 days ago, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysAgo2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 3, 2011, 10:22 AM
+ Calendar tomorrowCalendar1 = Calendar.getInstance(tz, en_US);
+ tomorrowCalendar1.set(2011, Calendar.SEPTEMBER, 3, 10, 22, 0);
+ long tomorrow1 = tomorrowCalendar1.getTimeInMillis();
+ assertEquals("Tomorrow, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, tomorrow1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 3, 2011, 10:24 AM
+ Calendar tomorrowCalendar2 = Calendar.getInstance(tz, en_US);
+ tomorrowCalendar2.set(2011, Calendar.SEPTEMBER, 3, 10, 24, 0);
+ long tomorrow2 = tomorrowCalendar2.getTimeInMillis();
+ assertEquals("Tomorrow, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, tomorrow2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 4, 2011, 10:22 AM
+ Calendar twoDaysLaterCalendar1 = Calendar.getInstance(tz, en_US);
+ twoDaysLaterCalendar1.set(2011, Calendar.SEPTEMBER, 4, 10, 22, 0);
+ long twoDaysLater1 = twoDaysLaterCalendar1.getTimeInMillis();
+ assertEquals("In 2 days, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysLater1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 4, 2011, 10:24 AM
+ Calendar twoDaysLaterCalendar2 = Calendar.getInstance(tz, en_US);
+ twoDaysLaterCalendar2.set(2011, Calendar.SEPTEMBER, 4, 10, 24, 0);
+ long twoDaysLater2 = twoDaysLaterCalendar2.getTimeInMillis();
+ assertEquals("In 2 days, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysLater2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+ }
+
+ // b/19822016: show / hide the year based on the dates in the arguments.
+ @Test
+ public void test_bug19822016() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2012 at 10:50 PST
+ cal.set(2012, Calendar.FEBRUARY, 5, 10, 50, 0);
+ long base = cal.getTimeInMillis();
+
+ assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+ assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2011, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+ assertEquals("January 6, 2012", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+
+ // Feb 5, 2018 at 10:50 PST
+ cal.set(2018, Calendar.FEBRUARY, 5, 10, 50, 0);
+ base = cal.getTimeInMillis();
+ assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+ assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2017, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+ assertEquals("January 6, 2018", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+ }
+
+ // Check for missing ICU data. http://b/25821045
+ @Test
+ public void test_bug25821045() {
+ final TimeZone tz = TimeZone.getDefault();
+ final long now = System.currentTimeMillis();
+ final long time = now + 1000;
+ final int minResolution = 1000 * 60;
+ final int transitionResolution = minResolution;
+ final int flags = FORMAT_ABBREV_RELATIVE;
+ // Exercise all available locales, forcing the ICU implementation to pre-cache the data.
+ // This
+ // highlights data issues. It can take a while.
+ for (Locale locale : Locale.getAvailableLocales()) {
+ // In (e.g.) ICU56 an exception is thrown on the first use for a locale if required
+ // data for
+ // the "other" plural is missing. It doesn't matter what is actually formatted.
+ try {
+ RelativeDateTimeFormatter.getRelativeDateTimeString(
+ locale, tz, time, now, minResolution, transitionResolution, flags);
+ } catch (IllegalStateException e) {
+ fail("Failed to format for " + locale);
+ }
+ }
+ }
+
+ // Check for ICU data lookup fallback failure. http://b/25883157
+ @Test
+ public void test_bug25883157() {
+ final Locale locale = new Locale("en", "GB");
+ final TimeZone tz = TimeZone.getTimeZone("GMT");
+
+ final Calendar cal = Calendar.getInstance(tz, locale);
+ cal.set(2015, Calendar.JUNE, 19, 12, 0, 0);
+
+ final long base = cal.getTimeInMillis();
+ final long time = base + 2 * WEEK_IN_MILLIS;
+
+ assertEquals("In 2 wk", getRelativeTimeSpanString(
+ locale, tz, time, base, WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ }
+
+ // http://b/63745717
+ @Test
+ public void test_combineDateAndTime_apostrophe() {
+ final Locale locale = new Locale("fr");
+ android.icu.text.RelativeDateTimeFormatter icuFormatter =
+ android.icu.text.RelativeDateTimeFormatter.getInstance(locale);
+ assertEquals("D à T", icuFormatter.combineDateAndTime("D", "T"));
+ // Ensure single quote ' and curly braces {} are not interpreted in input values.
+ assertEquals("D'x' à T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}"));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index bfcf52af80bf..eb695258142a 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -86,7 +86,7 @@ public class ImeInsetsSourceConsumerTest {
false,
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
- SOFT_INPUT_ADJUST_RESIZE, 0);
+ SOFT_INPUT_ADJUST_RESIZE, 0, 0);
mImeConsumer = (ImeInsetsSourceConsumer) mController.getSourceConsumer(ITYPE_IME);
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 964ae21d6086..801cd4ddb94e 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -165,7 +165,7 @@ public class InsetsControllerTest {
false,
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
- SOFT_INPUT_ADJUST_RESIZE, 0);
+ SOFT_INPUT_ADJUST_RESIZE, 0, 0);
mController.onFrameChanged(new Rect(0, 0, 100, 100));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -691,18 +691,57 @@ public class InsetsControllerTest {
@Test
public void testRequestedState() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+ // The modified state can be controlled when we have control.
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
mController.hide(statusBars());
assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
- mController.onControlsChanged(new InsetsSourceControl[0]);
+
+ // The modified state won't be changed while losing control.
+ mController.onControlsChanged(null /* activeControls */);
assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state won't be changed while state changed while we don't have control.
InsetsState newState = new InsetsState(mController.getState(), true /* copySource */);
mController.onStateChanged(newState);
assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state won't be changed while controlling an insets without having the
+ // control.
mController.show(statusBars());
assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state can be updated while gaining control.
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
assertTrue(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state can still be updated if the local state and the requested state
+ // are the same.
+ mController.onControlsChanged(null /* activeControls */);
+ mController.hide(statusBars());
+ newState = new InsetsState(mController.getState(), true /* copySource */);
+ newState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+ mController.onStateChanged(newState);
+ mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
+ assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state will always be updated while receiving IME control if IME is
+ // requested visible.
+ mController.getSourceConsumer(ITYPE_IME).show(false /* fromIme */);
+ newState = new InsetsState(mController.getState(), true /* copySource */);
+ newState.getSource(ITYPE_IME).setVisible(true);
+ newState.getSource(ITYPE_IME).setFrame(1, 2, 3, 4);
+ mController.onStateChanged(newState);
+ mController.onControlsChanged(createSingletonControl(ITYPE_IME));
+ assertEquals(newState.getSource(ITYPE_IME),
+ mTestHost.getModifiedState().peekSource(ITYPE_IME));
+ newState = new InsetsState(mController.getState(), true /* copySource */);
+ newState.getSource(ITYPE_IME).setVisible(true);
+ newState.getSource(ITYPE_IME).setFrame(5, 6, 7, 8);
+ mController.onStateChanged(newState);
+ mController.onControlsChanged(createSingletonControl(ITYPE_IME));
+ assertEquals(newState.getSource(ITYPE_IME),
+ mTestHost.getModifiedState().peekSource(ITYPE_IME));
});
}
@@ -717,7 +756,7 @@ public class InsetsControllerTest {
// Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will
// attempt to release mLeash directly.
- SurfaceControl copy = new SurfaceControl(mLeash);
+ SurfaceControl copy = new SurfaceControl(mLeash, "InsetsControllerTest.createControl");
return new InsetsSourceControl(type, copy, new Point());
}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 1b3272572db0..7efd616c5607 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -71,6 +71,9 @@ public class InsetsSourceConsumerTest {
private SurfaceControl mLeash;
@Mock Transaction mMockTransaction;
private InsetsSource mSpyInsetsSource;
+ private boolean mRemoveSurfaceCalled = false;
+ private InsetsController mController;
+ private InsetsState mState;
@Before
public void setup() {
@@ -89,13 +92,19 @@ public class InsetsSourceConsumerTest {
} catch (BadTokenException e) {
// activity isn't running, lets ignore BadTokenException.
}
- InsetsState state = new InsetsState();
+ mState = new InsetsState();
mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR));
- state.addSource(mSpyInsetsSource);
-
- mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, state,
- () -> mMockTransaction,
- new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl)));
+ mState.addSource(mSpyInsetsSource);
+
+ mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl));
+ mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState,
+ () -> mMockTransaction, mController) {
+ @Override
+ public void removeSurface() {
+ super.removeSurface();
+ mRemoveSurfaceCalled = true;
+ }
+ };
});
instrumentation.waitForIdleSync();
@@ -171,6 +180,25 @@ public class InsetsSourceConsumerTest {
mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
new int[1], hideTypes);
assertEquals(statusBars(), hideTypes[0]);
+ assertFalse(mRemoveSurfaceCalled);
+ });
+ }
+
+ @Test
+ public void testRestore_noAnimation() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mConsumer.hide();
+ mController.onStateChanged(mState);
+ mConsumer.setControl(null, new int[1], new int[1]);
+ reset(mMockTransaction);
+ verifyZeroInteractions(mMockTransaction);
+ mRemoveSurfaceCalled = false;
+ int[] hideTypes = new int[1];
+ mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
+ new int[1], hideTypes);
+ assertTrue(mRemoveSurfaceCalled);
+ assertEquals(0, hideTypes[0]);
});
+
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 7115acfedcf6..c7d835ca7c7e 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -29,6 +29,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
@@ -77,7 +78,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setVisible(true);
SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, typeSideMap);
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, typeSideMap);
assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR));
@@ -96,7 +97,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300));
mState.getSource(ITYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null);
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, null);
assertEquals(100, insets.getStableInsetBottom());
assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(Type.systemBars()));
assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
@@ -115,7 +116,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, 0, 0, null);
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -131,7 +132,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
mState.getSource(ITYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, null);
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, 0, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
assertEquals(100, insets.getInsets(ime()).bottom);
assertTrue(insets.isVisible(ime()));
@@ -147,11 +148,28 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
mState.getSource(ITYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
SYSTEM_UI_FLAG_LAYOUT_STABLE, null);
assertEquals(100, insets.getSystemWindowInsetTop());
insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
- DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING,
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
+ 0 /* legacySystemUiFlags */, null);
+ assertEquals(0, insets.getSystemWindowInsetTop());
+ }
+ }
+
+ @Test
+ public void testCalculateInsets_systemUiFlagLayoutStable_windowFlagFullscreen() {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN,
+ SYSTEM_UI_FLAG_LAYOUT_STABLE, null);
+ assertEquals(0, insets.getSystemWindowInsetTop());
+ insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
0 /* legacySystemUiFlags */, null);
assertEquals(0, insets.getSystemWindowInsetTop());
}
@@ -195,7 +213,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, 0, 0, null);
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -211,7 +229,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, 0, 0, null);
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -226,7 +244,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setVisible(true);
mState.removeSource(ITYPE_IME);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
- DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null);
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
}
@@ -304,6 +322,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setVisibleFrame(new Rect(0, 0, 50, 10));
mState.getSource(ITYPE_IME).setVisible(true);
mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(0, 0, 100, 100));
mState2.set(mState, true);
assertEquals(mState, mState2);
}
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 02ffc00dcba5..93de03adfa84 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -264,6 +264,25 @@ public class EditorInfoTest {
InputConnection.GET_TEXT_WITH_STYLES)));
}
+ @Test
+ public void surroundingTextRetrieval_writeToParcel_noException() {
+ StringBuilder sb = new StringBuilder("abcdefg");
+ Parcel parcel = Parcel.obtain();
+ EditorInfo editorInfo = new EditorInfo();
+ editorInfo.initialSelStart = 2;
+ editorInfo.initialSelEnd = 5;
+ editorInfo.inputType = EditorInfo.TYPE_CLASS_TEXT;
+
+ editorInfo.setInitialSurroundingText(sb);
+ sb.setLength(0);
+ editorInfo.writeToParcel(parcel, 0);
+
+ try {
+ editorInfo.getInitialTextBeforeCursor(60, 1);
+ fail("Test shouldn't have exception");
+ } catch (AssertionError e) { }
+ }
+
private static void assertExpectedTextLength(EditorInfo editorInfo,
@Nullable Integer expectBeforeCursorLength, @Nullable Integer expectSelectionLength,
@Nullable Integer expectAfterCursorLength) {
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index 89cc6e743752..df2946c97d20 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -37,7 +37,6 @@ import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.app.Instrumentation;
import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
@@ -96,7 +95,6 @@ public class EditorCursorDragTest {
mMotionEvents.clear();
}
- @Presubmit
@Test
public void testCursorDrag_horizontal_whenTextViewContentsFitOnScreen() throws Throwable {
String text = "Hello world!";
@@ -145,7 +143,7 @@ public class EditorCursorDragTest {
// Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
// the handle as the touch moves downwards (and because we have some slop to avoid jumping
// across lines), the cursor position will end up higher than the finger position.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
// Swipe right-down along a very steep diagonal path. This should not drag the cursor.
@@ -181,7 +179,7 @@ public class EditorCursorDragTest {
// Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
// the handle as the touch moves downwards (and because we have some slop to avoid jumping
// across lines), the cursor position will end up higher than the finger position.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
// Swipe right-down along a very steep diagonal path. This should not drag the cursor.
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index ec75e40f1334..35fd4bd7dc14 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -326,9 +326,9 @@ public class EditorTouchStateTest {
mTouchState.update(event1, mConfig);
assertSingleTap(mTouchState, 0f, 0f, 0, 0);
- // Simulate an ACTION_MOVE event that is > 30 deg from vertical.
+ // Simulate an ACTION_MOVE event that is > 45 deg from vertical.
long event2Time = 1002;
- MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 173f);
+ MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f);
mTouchState.update(event2, mConfig);
assertDrag(mTouchState, 0f, 0f, 0, 0, false);
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 49de7c80057f..090645f6f7a8 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -1995,6 +1995,134 @@ public class ChooserActivityTest {
isQueryTargetServicesCalledOnWorkProfile[0]);
}
+ @Test
+ public void testWorkTab_selectingWorkTabWithNotRunningWorkUser_directShareTargetsNotQueried() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ sOverrides.isWorkProfileUserRunning = false;
+ boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
+ isQueryDirectShareCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryTargetServices = chooserListAdapter -> {
+ isQueryTargetServicesCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertFalse("Direct share targets were queried on a locked work profile user",
+ isQueryDirectShareCalledOnWorkProfile[0]);
+ assertFalse("Target services were queried on a locked work profile user",
+ isQueryTargetServicesCalledOnWorkProfile[0]);
+ }
+
+ @Test
+ public void testWorkTab_workUserNotRunning_workTargetsShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+ sOverrides.isWorkProfileUserRunning = false;
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertEquals(3, activity.getWorkListAdapter().getCount());
+ }
+
+ @Test
+ public void testWorkTab_selectingWorkTabWithLockedWorkUser_directShareTargetsNotQueried() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ sOverrides.isWorkProfileUserUnlocked = false;
+ boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
+ isQueryDirectShareCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryTargetServices = chooserListAdapter -> {
+ isQueryTargetServicesCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertFalse("Direct share targets were queried on a locked work profile user",
+ isQueryDirectShareCalledOnWorkProfile[0]);
+ assertFalse("Target services were queried on a locked work profile user",
+ isQueryTargetServicesCalledOnWorkProfile[0]);
+ }
+
+ @Test
+ public void testWorkTab_workUserLocked_workTargetsShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+ sOverrides.isWorkProfileUserUnlocked = false;
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertEquals(3, activity.getWorkListAdapter().getCount());
+ }
+
private Intent createChooserIntent(Intent intent, Intent[] initialIntents) {
Intent chooserIntent = new Intent();
chooserIntent.setAction(Intent.ACTION_CHOOSER);
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 0f6b51f82116..d3d5caf3f7e2 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -227,6 +227,22 @@ public class ChooserWrapperActivity extends ChooserActivity {
return sOverrides.isQuietModeEnabled;
}
+ @Override
+ protected boolean isUserRunning(UserHandle userHandle) {
+ if (userHandle.equals(UserHandle.SYSTEM)) {
+ return super.isUserRunning(userHandle);
+ }
+ return sOverrides.isWorkProfileUserRunning;
+ }
+
+ @Override
+ protected boolean isUserUnlocked(UserHandle userHandle) {
+ if (userHandle.equals(UserHandle.SYSTEM)) {
+ return super.isUserUnlocked(userHandle);
+ }
+ return sOverrides.isWorkProfileUserUnlocked;
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
@@ -252,6 +268,8 @@ public class ChooserWrapperActivity extends ChooserActivity {
public UserHandle workProfileUserHandle;
public boolean hasCrossProfileIntents;
public boolean isQuietModeEnabled;
+ public boolean isWorkProfileUserRunning;
+ public boolean isWorkProfileUserUnlocked;
public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
public PackageManager packageManager;
@@ -274,6 +292,8 @@ public class ChooserWrapperActivity extends ChooserActivity {
workProfileUserHandle = null;
hasCrossProfileIntents = true;
isQuietModeEnabled = false;
+ isWorkProfileUserRunning = true;
+ isWorkProfileUserUnlocked = true;
packageManager = null;
multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
@Override
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index b7d1c9b993d9..a5117a3e7cc3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -807,6 +807,38 @@ public class BinderCallsStatsTest {
}
}
+ @Test
+ public void testNativeTids() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ Binder binder = new Binder();
+
+ bcs.nativeTid = 3;
+
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ bcs.nativeTid = 1;
+
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ bcs.nativeTid = 1;
+
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ bcs.nativeTid = 2;
+
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ int[] tids = bcs.getNativeTids();
+ assertEquals(3, tids.length);
+ assertEquals(1, tids[0]);
+ assertEquals(2, tids[1]);
+ assertEquals(3, tids[2]);
+ }
+
private static class TestHandler extends Handler {
ArrayList<Runnable> mRunnables = new ArrayList<>();
@@ -825,6 +857,7 @@ public class BinderCallsStatsTest {
public int callingUid = CALLING_UID;
public long time = 1234;
public long elapsedTime = 0;
+ public int nativeTid;
TestBinderCallsStats() {
this(mDeviceState);
@@ -874,6 +907,10 @@ public class BinderCallsStatsTest {
protected void setCallingUid(int uid) {
callingUid = uid;
}
- }
+ @Override
+ protected int getNativeTid() {
+ return nativeTid;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java b/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java
new file mode 100644
index 000000000000..e4597b53b1a3
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.Binder;
+
+import com.android.internal.os.BinderCallHeavyHitterWatcher.HeavyHitterContainer;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Tests for {@link BinderCallHeavyHitterWatcher}.
+ */
+public final class BinderHeavyHitterTest extends TestCase {
+
+ private boolean mListenerNotified = false;
+
+ private List<HeavyHitterContainer> mExpectedResult = null;
+
+ /**
+ * Generate random input.
+ */
+ private ArrayList<HeavyHitterContainer> generateRandomInput(final int total,
+ final List<HeavyHitterContainer> heavyHitters,
+ final List<Integer> numOfHeavyHits) {
+ final ArrayList<HeavyHitterContainer> result = new ArrayList<>();
+ List<HeavyHitterContainer> flatternedHeavyHitters = null;
+ int totalHeavyHitters = 0;
+ if (numOfHeavyHits != null) {
+ flatternedHeavyHitters = new ArrayList<>();
+ for (int i = numOfHeavyHits.size() - 1; i >= 0; i--) {
+ final int k = numOfHeavyHits.get(i);
+ totalHeavyHitters += k;
+ final HeavyHitterContainer container = heavyHitters.get(i);
+ for (int j = 0; j < k; j++) {
+ flatternedHeavyHitters.add(container);
+ }
+ }
+ }
+ int totalLightHitters = total - totalHeavyHitters;
+ final Binder[] binders = {new TestBinder1(), new TestBinder2(), new TestBinder3()};
+ final int maxUid = 1000;
+ final int maxCode = 1000;
+ final Random rand = new Random();
+ for (int i = 0; i < total; i++) {
+ HeavyHitterContainer container = null;
+ if (totalLightHitters <= 0) {
+ container = flatternedHeavyHitters.remove(rand.nextInt(totalHeavyHitters));
+ totalHeavyHitters--;
+ } else if (totalHeavyHitters <= 0) {
+ container = newContainer(rand.nextInt(maxUid),
+ binders[rand.nextInt(binders.length)].getClass(),
+ rand.nextInt(maxCode), 0.0f);
+ totalLightHitters--;
+ } else {
+ int val = rand.nextInt(total - i);
+ if (val >= totalLightHitters) {
+ container = flatternedHeavyHitters.remove(rand.nextInt(totalHeavyHitters));
+ totalHeavyHitters--;
+ } else {
+ container = newContainer(rand.nextInt(maxUid),
+ binders[rand.nextInt(binders.length)].getClass(),
+ rand.nextInt(maxCode), 0.0f);
+ totalLightHitters--;
+ }
+ }
+ result.add(container);
+ }
+ return result;
+ }
+
+ private HeavyHitterContainer newContainer(final int uid, final Class clazz, final int code,
+ final float freq) {
+ final HeavyHitterContainer container = new HeavyHitterContainer();
+ container.mUid = uid;
+ container.mClass = clazz;
+ container.mCode = code;
+ container.mFrequency = freq;
+ return container;
+ }
+
+ private void onResult(final List<HeavyHitterContainer> results, final Integer inputSize,
+ final Float threshod, final Long timeSpan) {
+ mListenerNotified = true;
+ if (mExpectedResult == null) {
+ assertTrue(results == null || results.size() == 0);
+ } else {
+ int size = mExpectedResult.size();
+ assertEquals(size, results.size());
+ for (int i = 0; i < size; i++) {
+ final HeavyHitterContainer container = mExpectedResult.get(i);
+ assertNotNull(container);
+ assertTrue(results.remove(container));
+ }
+ assertEquals(0, results.size());
+ }
+ }
+
+ public void testPositive() throws Exception {
+ BinderCallHeavyHitterWatcher watcher = BinderCallHeavyHitterWatcher.getInstance();
+ try {
+ List<HeavyHitterContainer> hitters = new ArrayList<>();
+ List<Integer> counts = new ArrayList<>();
+ hitters.add(newContainer(1001, TestBinder4.class, 1002, 0.4f));
+ counts.add(400);
+ hitters.add(newContainer(2001, TestBinder5.class, 2002, 0.333f));
+ counts.add(333);
+ ArrayList<HeavyHitterContainer> inputs = generateRandomInput(1000, hitters, counts);
+ inputs.addAll((List<HeavyHitterContainer>) inputs.clone());
+
+ watcher.setConfig(true, inputs.size(), 0.333f, this::onResult);
+ mListenerNotified = false;
+ mExpectedResult = hitters;
+
+ for (int i = inputs.size() - 1; i >= 0; i--) {
+ final HeavyHitterContainer container = inputs.get(i);
+ watcher.onTransaction(container.mUid, container.mClass, container.mCode);
+ }
+ assertTrue(mListenerNotified);
+ } finally {
+ watcher.setConfig(false, 0, 0.0f, null);
+ mListenerNotified = false;
+ mExpectedResult = null;
+ }
+ }
+
+ public void testNegative() throws Exception {
+ BinderCallHeavyHitterWatcher watcher = BinderCallHeavyHitterWatcher.getInstance();
+ try {
+ List<HeavyHitterContainer> hitters = new ArrayList<>();
+ List<Integer> counts = new ArrayList<>();
+ hitters.add(newContainer(1001, TestBinder4.class, 1002, 0.332f));
+ counts.add(332);
+ hitters.add(newContainer(2001, TestBinder5.class, 2002, 0.331f));
+ counts.add(331);
+ ArrayList<HeavyHitterContainer> inputs = generateRandomInput(1000, hitters, counts);
+ inputs.addAll((List<HeavyHitterContainer>) inputs.clone());
+
+ watcher.setConfig(true, inputs.size(), 0.333f, this::onResult);
+ mListenerNotified = false;
+ mExpectedResult = null;
+
+ for (int i = inputs.size() - 1; i >= 0; i--) {
+ final HeavyHitterContainer container = inputs.get(i);
+ watcher.onTransaction(container.mUid, container.mClass, container.mCode);
+ }
+ assertFalse(mListenerNotified);
+ } finally {
+ watcher.setConfig(false, 0, 0.0f, null);
+ mListenerNotified = false;
+ mExpectedResult = null;
+ }
+ }
+
+ private class TestBinder1 extends Binder {
+ }
+
+ private class TestBinder2 extends Binder {
+ }
+
+ private class TestBinder3 extends Binder {
+ }
+
+ private class TestBinder4 extends Binder {
+ }
+
+ private class TestBinder5 extends Binder {
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/HeavyHitterSketchTest.java b/core/tests/utiltests/src/com/android/internal/util/HeavyHitterSketchTest.java
new file mode 100644
index 000000000000..f2285a12e30a
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/HeavyHitterSketchTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.util.ArraySet;
+import android.util.Pair;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+/**
+ * Tests for {@link HeavyHitterSketch}.
+ */
+public final class HeavyHitterSketchTest extends TestCase {
+
+ private static final float EPSILON = 0.00001f;
+
+ /**
+ * A naive counter based heavy hitter sketch, tracks every single input. To be used to validate
+ * the correctness of {@link HeavyHitterSketch}.
+ */
+ private class CounterBased<T> {
+ private final HashMap<T, Integer> mData = new HashMap<>();
+ private int mTotalInput = 0;
+
+ public void add(final T newInstance) {
+ int val = mData.getOrDefault(newInstance, 0);
+ mData.put(newInstance, val + 1);
+ mTotalInput++;
+ }
+
+ public List<Pair<T, Float>> getTopHeavyHitters(final int k) {
+ final int lower = mTotalInput / (k + 1);
+ return mData.entrySet().stream()
+ .filter(e -> e.getValue() >= lower)
+ .limit(k)
+ .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
+ .map((v) -> new Pair<T, Float>(v.getKey(), (float) v.getValue() / mTotalInput))
+ .collect(Collectors.toList());
+ }
+ }
+
+ private List<Pair<Integer, Float>> getTopHeavyHitters(final int[] input, final int capacity) {
+ final CounterBased counter = new CounterBased<Integer>();
+ final HeavyHitterSketch<Integer> sketcher = HeavyHitterSketch.<Integer>newDefault();
+ final float ratio = sketcher.getRequiredValidationInputRatio();
+ final int total = (int) (input.length / (1 - ratio));
+ sketcher.setConfig(total, capacity);
+ for (int i = 0; i < input.length; i++) {
+ sketcher.add(input[i]);
+ counter.add(input[i]);
+ }
+ int validationSize = total - input.length;
+ assertTrue(validationSize <= input.length);
+ for (int i = 0; i < validationSize; i++) {
+ sketcher.add(input[i]);
+ }
+ final List<Float> freqs = new ArrayList<>();
+ final List<Integer> tops = sketcher.getTopHeavyHitters(capacity - 1, null, freqs);
+ final List<Pair<Integer, Float>> result = new ArrayList<>();
+ if (tops != null) {
+ assertEquals(freqs.size(), tops.size());
+ final List<Pair<Integer, Float>> cl = counter.getTopHeavyHitters(capacity - 1);
+ for (int i = 0; i < tops.size(); i++) {
+ final Pair<Integer, Float> pair = cl.get(i);
+ assertEquals(pair.first.intValue(), tops.get(i).intValue());
+ assertTrue(Math.abs(pair.second - freqs.get(i)) < EPSILON);
+ result.add(new Pair<>(tops.get(i), freqs.get(i)));
+ }
+ } else {
+ assertTrue(counter.getTopHeavyHitters(capacity - 1).isEmpty());
+ }
+ return result;
+ }
+
+ private List<Integer> getCandidates(final int[] input, final int capacity) {
+ final HeavyHitterSketch<Integer> sketcher = HeavyHitterSketch.<Integer>newDefault();
+ final float ratio = sketcher.getRequiredValidationInputRatio();
+ final int total = (int) (input.length / (1 - ratio));
+ sketcher.setConfig(total, capacity);
+ for (int i = 0; i < input.length; i++) {
+ sketcher.add(input[i]);
+ }
+ return sketcher.getCandidates(null);
+ }
+
+ private void verify(final int[] input, final int capacity, final int[] expected,
+ final float[] freqs) throws Exception {
+ final List<Integer> candidates = getCandidates(input, capacity);
+ final List<Pair<Integer, Float>> result = getTopHeavyHitters(input, capacity);
+ if (expected != null) {
+ assertTrue(candidates != null);
+ for (int i = 0; i < expected.length; i++) {
+ assertTrue(candidates.contains(expected[i]));
+ }
+ assertTrue(result != null);
+ assertEquals(expected.length, result.size());
+ for (int i = 0; i < expected.length; i++) {
+ final Pair<Integer, Float> pair = result.get(i);
+ assertEquals(expected[i], pair.first.intValue());
+ assertTrue(Math.abs(freqs[i] - pair.second) < EPSILON);
+ }
+ } else {
+ assertEquals(null, result);
+ }
+ }
+
+ private void verifyNotExpected(final int[] input, final int capacity, final int[] notExpected)
+ throws Exception {
+ final List<Pair<Integer, Float>> result = getTopHeavyHitters(input, capacity);
+ if (result != null) {
+ final ArraySet<Integer> set = new ArraySet<>();
+ for (Pair<Integer, Float> p : result) {
+ set.add(p.first);
+ }
+ for (int i = 0; i < notExpected.length; i++) {
+ assertFalse(set.contains(notExpected[i]));
+ }
+ }
+ }
+
+ private int[] generateRandomInput(final int size, final int[] hitters) {
+ final Random random = new Random();
+ final Random random2 = new Random();
+ final int[] input = new int[size];
+ // 80% of them would be hitters, 20% will be random numbers
+ final int numOfRandoms = size / 5;
+ final int numOfHitters = size - numOfRandoms;
+ for (int i = 0, j = 0, m = numOfRandoms, n = numOfHitters; i < size; i++) {
+ int r = m > 0 && n > 0 ? random2.nextInt(size) : (m > 0 ? 0 : numOfRandoms);
+ if (r < numOfRandoms) {
+ input[i] = random.nextInt(size);
+ m--;
+ } else {
+ input[i] = hitters[j++];
+ if (j == hitters.length) {
+ j = 0;
+ }
+ n--;
+ }
+ }
+ return input;
+ }
+
+ public void testPositive() throws Exception {
+ // Simple case
+ verify(new int[]{2, 9, 9, 9, 7, 6, 4, 9, 9, 9, 3, 9}, 2, new int[]{9},
+ new float[]{0.583333f});
+
+ // Two heavy hitters
+ verify(new int[]{2, 3, 9, 3, 9, 3, 9, 7, 6, 4, 9, 9, 3, 9, 3, 9}, 3, new int[]{9, 3},
+ new float[]{0.4375f, 0.3125f});
+
+ // Create a random data set and insert some numbers
+ final int[] input = generateRandomInput(100,
+ new int[]{1001, 1002, 1002, 1003, 1003, 1003, 1004, 1004, 1004, 1004});
+ verify(input, 12, new int[]{1004, 1003, 1002, 1001},
+ new float[]{0.32f, 0.24f, 0.16f, 0.08f});
+ }
+
+ public void testNegative() throws Exception {
+ // Simple case
+ verifyNotExpected(new int[]{2, 9, 9, 9, 7, 6, 4, 9, 9, 9, 3, 9}, 2, new int[]{0, 1, 2});
+
+ // Two heavy hitters
+ verifyNotExpected(new int[]{2, 3, 9, 3, 9, 3, 9, 7, 6, 4, 9, 9, 3, 9, 3, 9}, 3,
+ new int[]{0, 1, 2});
+
+ // Create a random data set and insert some numbers
+ final int[] input = generateRandomInput(100,
+ new int[]{1001, 1002, 1002, 1003, 1003, 1003, 1004, 1004, 1004, 1004});
+ verifyNotExpected(input, 12, new int[]{0, 1, 2, 1000, 1005});
+ }
+
+ public void testFalsePositive() throws Exception {
+ // Simple case
+ verifyNotExpected(new int[]{2, 9, 2, 2, 7, 6, 4, 9, 9, 9, 3, 9}, 2, new int[]{9});
+
+ // One heavy hitter
+ verifyNotExpected(new int[]{2, 3, 9, 3, 9, 3, 9, 7, 6, 4, 9, 9, 3, 9, 2, 9}, 3,
+ new int[]{3});
+
+ // Create a random data set and insert some numbers
+ final int[] input = generateRandomInput(100,
+ new int[]{1001, 1002, 1002, 1003, 1003, 1003, 1004, 1004, 1004, 1004});
+ verifyNotExpected(input, 11, new int[]{1001});
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
index 76aa93f7e8be..edf473eac1b1 100644
--- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
@@ -16,27 +16,25 @@
package com.android.internal.util;
-import java.util.Collection;
-import java.util.Iterator;
-
import android.os.Debug;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.test.TestLooper;
-
-import android.test.suitebuilder.annotation.Suppress;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.StateMachine.LogRec;
-
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import com.android.internal.util.StateMachine.LogRec;
+
import junit.framework.TestCase;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collection;
+import java.util.Iterator;
+
/**
* Test for StateMachine.
*/
@@ -2013,4 +2011,12 @@ public class StateMachineTest extends TestCase {
private static void tloge(String s) {
Log.e(TAG, s);
}
+
+ public void testDumpDoesNotThrowNpeAfterQuit() {
+ final Hsm1 sm = Hsm1.makeHsm1();
+ sm.quitNow();
+ final StringWriter stringWriter = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(stringWriter);
+ sm.dump(null, printWriter, new String[0]);
+ }
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 1d3a3997d857..fb8b17c1f159 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -63,6 +63,14 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "privapp_whitelist_com.android.cellbroadcastreceiver",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.cellbroadcastreceiver.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.android.contacts",
product_specific: true,
sub_dir: "permissions",
diff --git a/data/etc/com.android.cellbroadcastreceiver.xml b/data/etc/com.android.cellbroadcastreceiver.xml
new file mode 100644
index 000000000000..dd2df42e442f
--- /dev/null
+++ b/data/etc/com.android.cellbroadcastreceiver.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.cellbroadcastreceiver">
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index a5a2221e5532..ada8b000a26b 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -39,6 +39,7 @@
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.OBSERVE_NETWORK_POLICY"/>
+ <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
<permission name="android.permission.PACKAGE_USAGE_STATS" />
<permission name="android.permission.READ_DREAM_STATE"/>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index e1f6b2aa76ab..dd8f40d586bc 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -263,10 +263,4 @@
be able to connect to the internet when such a proxy is in use, since
all outgoing connections originate from this app. -->
<allow-in-power-save-except-idle package="com.android.proxyhandler" />
-
- <!-- These are the packages that are white-listed to be able to run as system user -->
- <system-user-whitelisted-app package="com.android.settings" />
-
- <!-- These are the packages that shouldn't run as system user -->
- <system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
</permissions>
diff --git a/data/etc/preinstalled-packages-platform-overlays.xml b/data/etc/preinstalled-packages-platform-overlays.xml
index ecd7d40ff21d..83323beb8dd4 100644
--- a/data/etc/preinstalled-packages-platform-overlays.xml
+++ b/data/etc/preinstalled-packages-platform-overlays.xml
@@ -23,9 +23,15 @@
<install-in-user-type package="com.android.internal.display.cutout.emulation.double">
<install-in user-type="FULL" />
</install-in-user-type>
+ <install-in-user-type package="com.android.internal.display.cutout.emulation.hole">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
<install-in-user-type package="com.android.internal.display.cutout.emulation.tall">
<install-in user-type="FULL" />
</install-in-user-type>
+ <install-in-user-type package="com.android.internal.display.cutout.emulation.waterfall">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.navbar.gestural">
<install-in user-type="FULL" />
</install-in-user-type>
@@ -47,4 +53,154 @@
<install-in-user-type package="com.android.internal.systemui.onehanded.gestural">
<install-in user-type="FULL" />
</install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.amethyst">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.aquamarine">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.black">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.carbon">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.cinnamon">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.green">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.ocean">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.orchid">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.palette">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.purple">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.sand">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.space">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.tangerine">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.font.notoserifsource">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.android">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.launcher">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.settings">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.systemui">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.themepicker">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.android">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.launcher">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.settings">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.systemui">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.themepicker">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.android">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.launcher">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.settings">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.systemui">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.themepicker">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.android">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.launcher">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.settings">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.systemui">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.themepicker">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.android">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.launcher">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.settings">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.systemui">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.themepicker">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.android">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.launcher">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.settings">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.systemui">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.themepicker">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.pebble">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.roundedrect">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.squircle">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.taperedrect">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.teardrop">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.vessel">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
</config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8520fffb3444..0286a7148b0b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -143,6 +143,9 @@ applications that come with the platform
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
<permission name="android.permission.PACKAGE_USAGE_STATS" />
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
+
+ <!-- For permission hub 2 debugging only -->
+ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
</privapp-permissions>
<privapp-permissions package="com.android.phone">
@@ -160,6 +163,7 @@ applications that come with the platform
<permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
<permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
<permission name="android.permission.DUMP"/>
+ <permission name="android.permission.HANDLE_CAR_MODE_CHANGES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_USERS"/>
@@ -198,6 +202,8 @@ applications that come with the platform
<permission name="android.permission.MANAGE_USERS" />
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.USE_RESERVED_DISK"/>
+ <permission name="android.permission.LOG_COMPAT_CHANGE" />
+ <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
</privapp-permissions>
<privapp-permissions package="com.android.providers.contacts">
@@ -424,6 +430,12 @@ applications that come with the platform
<permission name="android.permission.LOCATION_HARDWARE" />
<!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
<permission name="android.permission.CAPTURE_AUDIO_OUTPUT" />
+ <!-- Permissions required for CTS test - AdbManagerTest -->
+ <permission name="android.permission.MANAGE_DEBUGGING" />
+ <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
+ <permission name="android.car.permission.CAR_DRIVING_STATE" />
+ <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
+ <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index e85da88477e0..73296987adde 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -757,6 +757,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-547111355": {
+ "message": "hideIme Control target: %s ",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"-545190927": {
"message": "<<< CLOSE TRANSACTION animate",
"level": "INFO",
@@ -1093,6 +1099,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "95216706": {
+ "message": "hideIme target: %s ",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"95281111": {
"message": "Attempted to get IME flag of a display that does not exist: %d",
"level": "WARN",
@@ -1675,12 +1687,6 @@
"group": "WM_SHOW_SURFACE_ALLOC",
"at": "com\/android\/server\/wm\/ScreenRotationAnimation.java"
},
- "1108406230": {
- "message": "stopFreezingDisplayLocked: Returning mWaitingForConfig=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d",
- "level": "DEBUG",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"1112047265": {
"message": "finishDrawingWindow: %s mDrawState=%s",
"level": "DEBUG",
@@ -1741,6 +1747,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1246035185": {
+ "message": "stopFreezingDisplayLocked: Returning waitingForConfig=%b, waitingForRemoteRotation=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"1254403969": {
"message": "Surface returned was null: %s",
"level": "VERBOSE",
@@ -1957,12 +1969,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/AppTransition.java"
},
- "1591969812": {
- "message": "updateImeControlTarget %s",
- "level": "INFO",
- "group": "WM_DEBUG_IME",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"1628345525": {
"message": "Now opening app %s",
"level": "VERBOSE",
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 51500439d3cf..4cdfbb8ce27f 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -317,7 +317,7 @@ key 366 DVR
# key 367 "KEY_MHP"
# key 368 "KEY_LANGUAGE"
# key 369 "KEY_TITLE"
-# key 370 "KEY_SUBTITLE"
+key 370 CAPTIONS
# key 371 "KEY_ANGLE"
# key 372 "KEY_ZOOM"
# key 373 "KEY_MODE"
@@ -352,7 +352,7 @@ key 397 CALENDAR
key 402 CHANNEL_UP
key 403 CHANNEL_DOWN
# key 404 "KEY_FIRST"
-# key 405 "KEY_LAST"
+key 405 LAST_CHANNEL
# key 406 "KEY_AB"
# key 407 "KEY_NEXT"
# key 408 "KEY_RESTART"
@@ -412,6 +412,7 @@ key 582 VOICE_ASSIST
key 583 ASSIST
# Keys defined by HID usages
+key usage 0x0c0067 WINDOW
key usage 0x0c006F BRIGHTNESS_UP
key usage 0x0c0070 BRIGHTNESS_DOWN
diff --git a/data/keyboards/OWNERS b/data/keyboards/OWNERS
index 031a6c1c7a89..c4f6df824a39 100644
--- a/data/keyboards/OWNERS
+++ b/data/keyboards/OWNERS
@@ -2,3 +2,4 @@ set noparent
michaelwr@google.com
svv@google.com
+lzye@google.com
diff --git a/data/keyboards/Vendor_046d_Product_c216.kl b/data/keyboards/Vendor_046d_Product_c216.kl
index 6743323d7db8..8bc142f0cab0 100644
--- a/data/keyboards/Vendor_046d_Product_c216.kl
+++ b/data/keyboards/Vendor_046d_Product_c216.kl
@@ -16,15 +16,15 @@
# Logitech Dual Action Controller
#
-key 0x120 BUTTON_A
-key 0x123 BUTTON_B
-key 0x121 BUTTON_X
-key 0x122 BUTTON_Y
+key 0x121 BUTTON_A
+key 0x122 BUTTON_B
+key 0x120 BUTTON_X
+key 0x123 BUTTON_Y
key 0x124 BUTTON_L1
key 0x125 BUTTON_R1
key 0x126 BUTTON_L2
key 0x127 BUTTON_R2
-key 0x128 BUTTON_SELECT
+key 0x128 BACK
key 0x129 BUTTON_START
key 0x12a BUTTON_THUMBL
key 0x12b BUTTON_THUMBR
diff --git a/data/keyboards/Vendor_056e_Product_2010.kl b/data/keyboards/Vendor_056e_Product_2010.kl
new file mode 100644
index 000000000000..09e15eaa62b8
--- /dev/null
+++ b/data/keyboards/Vendor_056e_Product_2010.kl
@@ -0,0 +1,48 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Elecom JC-U4113S in DirectInput Mode (D mode).
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 306 BUTTON_A
+key 307 BUTTON_B
+key 304 BUTTON_X
+key 305 BUTTON_Y
+
+key 308 BUTTON_L1
+key 309 BUTTON_R1
+key 310 BUTTON_L2
+key 311 BUTTON_R2
+
+key 312 BUTTON_THUMBL
+key 313 BUTTON_THUMBR
+
+key 314 BACK
+key 315 BUTTON_START
+
+# Left and right stick.
+axis 0x00 X
+axis 0x01 Y
+axis 0x05 Z
+axis 0x02 RZ
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# "Guide" button (Xbox key).
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_056e_Product_2013.kl b/data/keyboards/Vendor_056e_Product_2013.kl
new file mode 100644
index 000000000000..c2a74a9fd442
--- /dev/null
+++ b/data/keyboards/Vendor_056e_Product_2013.kl
@@ -0,0 +1,44 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Elecom JC-U4113S in XInput Mode (X mode).
+#
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BACK
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Left and right stick.
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+
+axis 0x02 BRAKE
+axis 0x05 GAS
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# "Guide" button (Xbox key).
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1532_Product_0705.kl b/data/keyboards/Vendor_1532_Product_0705.kl
new file mode 100644
index 000000000000..611aaec1c26a
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_0705.kl
@@ -0,0 +1,64 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Mobile Controller with wired USB interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x133 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x134 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select key
+key 0x13a BUTTON_SELECT
+# Start key
+key 0x13b BUTTON_START
+# Home key
+key 0xac BUTTON_MODE
+# Back key
+key 0x9e BACK
diff --git a/data/keyboards/Vendor_1532_Product_0707.kl b/data/keyboards/Vendor_1532_Product_0707.kl
new file mode 100644
index 000000000000..48c171468bce
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_0707.kl
@@ -0,0 +1,64 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Mobile Controller with wireless Bluetooth interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x133 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x134 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select key
+key 0x13a BUTTON_SELECT
+# Start key
+key 0x13b BUTTON_START
+# Home key
+key 0xac BUTTON_MODE
+# Back key
+key 0x9e BACK
diff --git a/data/keyboards/Vendor_1532_Product_0709.kl b/data/keyboards/Vendor_1532_Product_0709.kl
new file mode 100644
index 000000000000..20ea2ab7504e
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_0709.kl
@@ -0,0 +1,51 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Junglecat Controller with wireless Bluetooth interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 0x133 BUTTON_X
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select key
+key 0x13a BUTTON_SELECT
+# Start key
+key 0x13b BUTTON_START
diff --git a/data/keyboards/Vendor_1532_Product_1004.kl b/data/keyboards/Vendor_1532_Product_1004.kl
new file mode 100644
index 000000000000..bfbfed5e9cab
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1004.kl
@@ -0,0 +1,65 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Ultimate Edition Controller with wired USB interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_1532_Product_1007.kl b/data/keyboards/Vendor_1532_Product_1007.kl
new file mode 100644
index 000000000000..6f6c286b34a5
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1007.kl
@@ -0,0 +1,65 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Tournament Edition Controller with wired USB interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_1532_Product_1009.kl b/data/keyboards/Vendor_1532_Product_1009.kl
new file mode 100644
index 000000000000..c380d5c33567
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1009.kl
@@ -0,0 +1,65 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Ultimate Edition Controller with wireless Bluetooth interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_1532_Product_100a.kl b/data/keyboards/Vendor_1532_Product_100a.kl
new file mode 100644
index 000000000000..b0e966d519b3
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_100a.kl
@@ -0,0 +1,65 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Tournament Edition Controller with wireless Bluetooth interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_27f8_Product_0bbf.kl b/data/keyboards/Vendor_27f8_Product_0bbf.kl
new file mode 100644
index 000000000000..a59f5663842c
--- /dev/null
+++ b/data/keyboards/Vendor_27f8_Product_0bbf.kl
@@ -0,0 +1,54 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Kishi Mobile Controller
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 0x133 BUTTON_X
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+axis 0x00 X
+axis 0x01 Y
+
+axis 0x02 Z
+axis 0x05 RZ
+
+axis 0x09 RTRIGGER
+axis 0x0a LTRIGGER
+
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Left Triangle Button
+key 0x13a BUTTON_SELECT
+# Right Triangle Button
+key 0x13b BUTTON_START
+# Home Button
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_2e95_Product_7725.kl b/data/keyboards/Vendor_2e95_Product_7725.kl
new file mode 100644
index 000000000000..7672e22f8adc
--- /dev/null
+++ b/data/keyboards/Vendor_2e95_Product_7725.kl
@@ -0,0 +1,64 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Scuf Vantage Controller
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# L2 Trigger axis
+axis 0x03 LTRIGGER
+# R2 Trigger axis
+axis 0x04 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index c763cc094662..9c55a2fb3bf1 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -537,7 +537,7 @@ key BUTTON_SELECT {
}
key BUTTON_MODE {
- base: fallback MENU
+ base: fallback HOME
}
key BUTTON_1 {
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index 098f4bfa74ac..c60191c5b980 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -14,10 +14,35 @@ java_library_host {
static_libs: [
"//external/error_prone:error_prone_core",
- "//external/dagger2:dagger2-auto-service",
+ ],
+
+ libs: [
+ "//external/auto:auto_service_annotations",
],
plugins: [
- "//external/dagger2:dagger2-auto-service",
+ "//external/auto:auto_service_plugin",
],
}
+
+java_test_host {
+ name: "error_prone_android_framework_test",
+ test_suites: ["general-tests"],
+ srcs: ["tests/java/**/*.java"],
+ java_resource_dirs: ["tests/res"],
+ java_resources: [":error_prone_android_framework_testdata"],
+ static_libs: [
+ "error_prone_android_framework_lib",
+ "error_prone_test_helpers",
+ "hamcrest-library",
+ "hamcrest",
+ "platform-test-annotations",
+ "junit",
+ ],
+}
+
+filegroup {
+ name: "error_prone_android_framework_testdata",
+ path: "tests/res",
+ srcs: ["tests/res/**/*.java"],
+}
diff --git a/errorprone/TEST_MAPPING b/errorprone/TEST_MAPPING
new file mode 100644
index 000000000000..ee4552fb3b33
--- /dev/null
+++ b/errorprone/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "error_prone_android_framework_test"
+ }
+ ]
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java
new file mode 100644
index 000000000000..4f1af3e9bea2
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.bugpatterns.android.UidChecker.getFlavor;
+import static com.google.errorprone.matchers.Matchers.enclosingClass;
+import static com.google.errorprone.matchers.Matchers.hasAnnotation;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.bugpatterns.android.UidChecker.Flavor;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+
+import java.util.List;
+
+/**
+ * To avoid an explosion of {@code startActivityForUser} style methods, we've
+ * converged on recommending the use of {@code Context.createContextAsUser()},
+ * and then ensuring that all system services pass {@link Context.getUserId()}
+ * for any {@code int userId} arguments across Binder interfaces.
+ * <p>
+ * This design allows developers to easily redirect all services obtained from a
+ * specific {@code Context} to a different user with no additional API surface.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkContextUserId",
+ summary = "Verifies that system_server calls use Context.getUserId()",
+ severity = WARNING)
+public final class ContextUserIdChecker extends BugChecker implements MethodInvocationTreeMatcher {
+ private static final Matcher<Tree> INSIDE_MANAGER =
+ enclosingClass(hasAnnotation("android.annotation.SystemService"));
+
+ private static final Matcher<ExpressionTree> BINDER_CALL = methodInvocation(
+ instanceMethod().onDescendantOf("android.os.IInterface").withAnyName());
+ private static final Matcher<ExpressionTree> GET_USER_ID_CALL = methodInvocation(
+ instanceMethod().onDescendantOf("android.content.Context").named("getUserId"));
+
+ private static final Matcher<ExpressionTree> USER_ID_FIELD = new Matcher<ExpressionTree>() {
+ @Override
+ public boolean matches(ExpressionTree tree, VisitorState state) {
+ if (tree instanceof IdentifierTree) {
+ return "mUserId".equals((((IdentifierTree) tree).getName().toString()));
+ }
+ return false;
+ }
+ };
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (INSIDE_MANAGER.matches(tree, state)
+ && BINDER_CALL.matches(tree, state)) {
+ final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params();
+ for (int i = 0; i < vars.size(); i++) {
+ final Flavor varFlavor = getFlavor(vars.get(i));
+ final ExpressionTree arg = tree.getArguments().get(i);
+ if (varFlavor == Flavor.USER_ID &&
+ !GET_USER_ID_CALL.matches(arg, state) &&
+ !USER_ID_FIELD.matches(arg, state)) {
+ return buildDescription(tree)
+ .setMessage("Must pass Context.getUserId() as user ID"
+ + " to enable createContextAsUser()")
+ .build();
+ }
+ }
+ }
+ return Description.NO_MATCH;
+ }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
new file mode 100644
index 000000000000..3ffb8ea40789
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Many system internals pass around PID, UID and user ID arguments as a single
+ * weakly-typed {@code int} value, which developers can accidentally cross in
+ * method argument lists, resulting in obscure bugs.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkUid",
+ summary = "Verifies that PID, UID and user ID arguments aren't crossed",
+ severity = WARNING)
+public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher {
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params();
+ final List<? extends ExpressionTree> args = tree.getArguments();
+ for (int i = 0; i < Math.min(vars.size(), args.size()); i++) {
+ final Flavor varFlavor = getFlavor(vars.get(i));
+ final Flavor argFlavor = getFlavor(args.get(i));
+ if (varFlavor == Flavor.UNKNOWN || argFlavor == Flavor.UNKNOWN) {
+ continue;
+ }
+ if (varFlavor != argFlavor) {
+ return buildDescription(tree).setMessage("Argument #" + (i + 1) + " expected "
+ + varFlavor + " but passed " + argFlavor).build();
+ }
+ }
+ return Description.NO_MATCH;
+ }
+
+ public static enum Flavor {
+ UNKNOWN(null),
+ PID(Pattern.compile("(^pid$|Pid$)")),
+ UID(Pattern.compile("(^uid$|Uid$)")),
+ USER_ID(Pattern.compile("(^userId$|UserId$|^userHandle$|UserHandle$)"));
+
+ private Pattern pattern;
+ private Flavor(Pattern pattern) {
+ this.pattern = pattern;
+ }
+ public boolean matches(CharSequence input) {
+ return (pattern != null) && pattern.matcher(input).find();
+ }
+ }
+
+ public static Flavor getFlavor(String name) {
+ for (Flavor f : Flavor.values()) {
+ if (f.matches(name)) {
+ return f;
+ }
+ }
+ return Flavor.UNKNOWN;
+ }
+
+ public static Flavor getFlavor(VarSymbol symbol) {
+ final String type = symbol.type.toString();
+ if ("int".equals(type)) {
+ return getFlavor(symbol.name.toString());
+ }
+ return Flavor.UNKNOWN;
+ }
+
+ public static Flavor getFlavor(ExpressionTree tree) {
+ if (tree instanceof IdentifierTree) {
+ return getFlavor(((IdentifierTree) tree).getName().toString());
+ } else if (tree instanceof MemberSelectTree) {
+ return getFlavor(((MemberSelectTree) tree).getIdentifier().toString());
+ } else if (tree instanceof MethodInvocationTree) {
+ return getFlavor(((MethodInvocationTree) tree).getMethodSelect());
+ }
+ return Flavor.UNKNOWN;
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java
new file mode 100644
index 000000000000..c0b8cd745afc
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ContextUserIdCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ ContextUserIdChecker.class, getClass());
+ }
+
+ @Test
+ public void testValid() {
+ compilationHelper
+ .addSourceFile("/android/annotation/SystemService.java")
+ .addSourceFile("/android/content/Context.java")
+ .addSourceFile("/android/foo/IFooService.java")
+ .addSourceFile("/android/os/IInterface.java")
+ .addSourceFile("/android/os/RemoteException.java")
+ .addSourceLines("FooManager.java",
+ "import android.annotation.SystemService;",
+ "import android.content.Context;",
+ "import android.foo.IFooService;",
+ "import android.os.RemoteException;",
+ "@SystemService(\"foo\") public class FooManager {",
+ " Context mContext;",
+ " IFooService mService;",
+ " final int mUserId;",
+ " FooManager(Context context) {",
+ " mUserId = mContext.getUserId();",
+ " }",
+ " void bar() throws RemoteException {",
+ " mService.baz(null, mContext.getUserId());",
+ " }",
+ " void baz() throws RemoteException {",
+ " mService.baz(null, mUserId);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testInvalid() {
+ compilationHelper
+ .addSourceFile("/android/annotation/SystemService.java")
+ .addSourceFile("/android/content/Context.java")
+ .addSourceFile("/android/foo/IFooService.java")
+ .addSourceFile("/android/os/IInterface.java")
+ .addSourceFile("/android/os/UserHandle.java")
+ .addSourceFile("/android/os/RemoteException.java")
+ .addSourceLines("FooManager.java",
+ "import android.annotation.SystemService;",
+ "import android.content.Context;",
+ "import android.foo.IFooService;",
+ "import android.os.UserHandle;",
+ "import android.os.RemoteException;",
+ "@SystemService(\"foo\") public class FooManager {",
+ " Context mContext;",
+ " IFooService mService;",
+ " void bar() throws RemoteException {",
+ " // BUG: Diagnostic contains:",
+ " mService.baz(null, 0);",
+ " }",
+ " void baz() throws RemoteException {",
+ " // BUG: Diagnostic contains:",
+ " mService.baz(null, UserHandle.myUserId());",
+ " }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java
new file mode 100644
index 000000000000..32efbf206a45
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class RethrowFromSystemCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ RethrowFromSystemChecker.class, getClass());
+ }
+
+ @Test
+ public void testValid() {
+ compilationHelper
+ .addSourceFile("/android/annotation/SystemService.java")
+ .addSourceFile("/android/foo/IFooService.java")
+ .addSourceFile("/android/os/IInterface.java")
+ .addSourceFile("/android/os/RemoteException.java")
+ .addSourceLines("FooManager.java",
+ "import android.annotation.SystemService;",
+ "import android.foo.IFooService;",
+ "import android.os.RemoteException;",
+ "@SystemService(\"foo\") public class FooManager {",
+ " IFooService mService;",
+ " void bar() {",
+ " try {",
+ " mService.bar();",
+ " } catch (RemoteException e) {",
+ " throw e.rethrowFromSystemServer();",
+ " }",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testInvalid() {
+ compilationHelper
+ .addSourceFile("/android/annotation/SystemService.java")
+ .addSourceFile("/android/foo/IFooService.java")
+ .addSourceFile("/android/os/IInterface.java")
+ .addSourceFile("/android/os/RemoteException.java")
+ .addSourceLines("FooManager.java",
+ "import android.annotation.SystemService;",
+ "import android.foo.IFooService;",
+ "import android.os.RemoteException;",
+ "@SystemService(\"foo\") public class FooManager {",
+ " IFooService mService;",
+ " void bar() {",
+ " try {",
+ " mService.bar();",
+ " // BUG: Diagnostic contains:",
+ " } catch (RemoteException e) {",
+ " e.printStackTrace();",
+ " }",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testIgnored() {
+ compilationHelper
+ .addSourceFile("/android/annotation/SystemService.java")
+ .addSourceFile("/android/foo/IFooService.java")
+ .addSourceFile("/android/os/IInterface.java")
+ .addSourceFile("/android/os/RemoteException.java")
+ .addSourceLines("FooManager.java",
+ "import android.annotation.SystemService;",
+ "import android.foo.IFooService;",
+ "import android.os.RemoteException;",
+ "@SystemService(\"foo\") public class FooManager {",
+ " IFooService mService;",
+ " void bar() {",
+ " try {",
+ " mService.bar();",
+ " // BUG: Diagnostic contains:",
+ " } catch (RemoteException ignored) {",
+ " }",
+ " }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/TargetSdkCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/TargetSdkCheckerTest.java
new file mode 100644
index 000000000000..99a21c973fe8
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/TargetSdkCheckerTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TargetSdkCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ TargetSdkChecker.class, getClass());
+ }
+
+ @Test
+ public void testValid() {
+ compilationHelper
+ .addSourceFile("/android/os/Build.java")
+ .addSourceLines("Example.java",
+ "import android.os.Build;",
+ "public class Example {",
+ " void test(int targetSdkVersion) {",
+ " boolean res = targetSdkVersion >= Build.VERSION_CODES.DONUT;",
+ " if (targetSdkVersion < Build.VERSION_CODES.DONUT) { }",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testInvalid() {
+ compilationHelper
+ .addSourceFile("/android/os/Build.java")
+ .addSourceLines("Example.java",
+ "import android.os.Build;",
+ "public class Example {",
+ " void test(int targetSdkVersion) {",
+ " // BUG: Diagnostic contains:",
+ " boolean res = targetSdkVersion > Build.VERSION_CODES.DONUT;",
+ " // BUG: Diagnostic contains:",
+ " if (targetSdkVersion <= Build.VERSION_CODES.DONUT) { }",
+ " }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
new file mode 100644
index 000000000000..74da94731092
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UidCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ UidChecker.class, getClass());
+ }
+
+ @Test
+ public void testTypical() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "public abstract class Example {",
+ " abstract void bar(int pid, int uid, int userId);",
+ " abstract int getUserId();",
+ " void foo(int pid, int uid, int userId, int unrelated) {",
+ " bar(0, 0, 0);",
+ " bar(pid, uid, userId);",
+ " bar(pid, uid, getUserId());",
+ " bar(unrelated, unrelated, unrelated);",
+ " // BUG: Diagnostic contains:",
+ " bar(uid, pid, userId);",
+ " // BUG: Diagnostic contains:",
+ " bar(pid, userId, uid);",
+ " // BUG: Diagnostic contains:",
+ " bar(getUserId(), 0, 0);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testCallingUid() {
+ compilationHelper
+ .addSourceFile("/android/os/Binder.java")
+ .addSourceFile("/android/os/UserHandle.java")
+ .addSourceLines("Example.java",
+ "import android.os.Binder;",
+ "import android.os.UserHandle;",
+ "public abstract class Example {",
+ " int callingUserId;",
+ " int callingUid;",
+ " abstract void setCallingUserId(int callingUserId);",
+ " abstract void setCallingUid(int callingUid);",
+ " void doUserId(int callingUserId) {",
+ " setCallingUserId(UserHandle.getUserId(Binder.getCallingUid()));",
+ " setCallingUserId(this.callingUserId);",
+ " setCallingUserId(callingUserId);",
+ " // BUG: Diagnostic contains:",
+ " setCallingUserId(Binder.getCallingUid());",
+ " // BUG: Diagnostic contains:",
+ " setCallingUserId(this.callingUid);",
+ " // BUG: Diagnostic contains:",
+ " setCallingUserId(callingUid);",
+ " }",
+ " void doUid(int callingUserId) {",
+ " setCallingUid(Binder.getCallingUid());",
+ " setCallingUid(this.callingUid);",
+ " setCallingUid(callingUid);",
+ " // BUG: Diagnostic contains:",
+ " setCallingUid(UserHandle.getUserId(Binder.getCallingUid()));",
+ " // BUG: Diagnostic contains:",
+ " setCallingUid(this.callingUserId);",
+ " // BUG: Diagnostic contains:",
+ " setCallingUid(callingUserId);",
+ " }",
+ " void doInner() {",
+ " // BUG: Diagnostic contains:",
+ " setCallingUserId(UserHandle.getUserId(callingUserId));",
+ " }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/errorprone/tests/res/android/annotation/SystemService.java b/errorprone/tests/res/android/annotation/SystemService.java
new file mode 100644
index 000000000000..b84edcbd2543
--- /dev/null
+++ b/errorprone/tests/res/android/annotation/SystemService.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.annotation;
+
+public @interface SystemService {
+ String value();
+}
diff --git a/errorprone/tests/res/android/content/Context.java b/errorprone/tests/res/android/content/Context.java
new file mode 100644
index 000000000000..7ba3fbb56245
--- /dev/null
+++ b/errorprone/tests/res/android/content/Context.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+public class Context {
+ public int getUserId() {
+ return 0;
+ }
+}
diff --git a/errorprone/tests/res/android/foo/IFooService.java b/errorprone/tests/res/android/foo/IFooService.java
new file mode 100644
index 000000000000..1ae7253d889b
--- /dev/null
+++ b/errorprone/tests/res/android/foo/IFooService.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.foo;
+
+import android.os.RemoteException;
+
+public interface IFooService extends android.os.IInterface {
+ public void bar() throws RemoteException;
+ public void baz(String baz, int userId) throws RemoteException;
+}
diff --git a/errorprone/tests/res/android/os/Binder.java b/errorprone/tests/res/android/os/Binder.java
new file mode 100644
index 000000000000..d388587c2f58
--- /dev/null
+++ b/errorprone/tests/res/android/os/Binder.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+public class Binder {
+ public static int getCallingUid() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/errorprone/tests/res/android/os/Build.java b/errorprone/tests/res/android/os/Build.java
new file mode 100644
index 000000000000..bbf7ef2172b5
--- /dev/null
+++ b/errorprone/tests/res/android/os/Build.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+public class Build {
+ public static class VERSION_CODES {
+ public static final int DONUT = 4;
+ }
+}
diff --git a/errorprone/tests/res/android/os/IInterface.java b/errorprone/tests/res/android/os/IInterface.java
new file mode 100644
index 000000000000..1a8a37cdb50c
--- /dev/null
+++ b/errorprone/tests/res/android/os/IInterface.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+public interface IInterface {
+}
diff --git a/errorprone/tests/res/android/os/RemoteException.java b/errorprone/tests/res/android/os/RemoteException.java
new file mode 100644
index 000000000000..afe19881aae3
--- /dev/null
+++ b/errorprone/tests/res/android/os/RemoteException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+public class RemoteException extends Exception {
+ public RuntimeException rethrowFromSystemServer() {
+ throw new RuntimeException(this);
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java b/errorprone/tests/res/android/os/UserHandle.java
index 42977f549162..c58d661fad08 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java
+++ b/errorprone/tests/res/android/os/UserHandle.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,22 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.helpers;
+package android.os;
-import android.app.Instrumentation;
-
-import com.android.server.wm.flicker.StandardAppHelper;
+public class UserHandle {
+ public static int getUserId(int uid) {
+ throw new UnsupportedOperationException();
+ }
-public abstract class FlickerAppHelper extends StandardAppHelper {
+ public static int getAppId(int uid) {
+ throw new UnsupportedOperationException();
+ }
- static int sFindTimeout = 10000;
- static String sFlickerPackage = "com.android.server.wm.flicker.testapp";
+ public static int getUid(int userId, int appId) {
+ throw new UnsupportedOperationException();
+ }
- public FlickerAppHelper(Instrumentation instr, String launcherName) {
- super(instr, sFlickerPackage, launcherName);
+ public static int myUserId() {
+ throw new UnsupportedOperationException();
}
}
diff --git a/graphics/java/android/graphics/ColorFilter.java b/graphics/java/android/graphics/ColorFilter.java
index 4c2ef84404e2..8fd6f7f609c6 100644
--- a/graphics/java/android/graphics/ColorFilter.java
+++ b/graphics/java/android/graphics/ColorFilter.java
@@ -48,7 +48,7 @@ public class ColorFilter {
return 0;
}
- void discardNativeInstance() {
+ synchronized final void discardNativeInstance() {
if (mNativeInstance != 0) {
mCleaner.run();
mCleaner = null;
@@ -57,7 +57,7 @@ public class ColorFilter {
}
/** @hide */
- public long getNativeInstance() {
+ public synchronized final long getNativeInstance() {
if (mNativeInstance == 0) {
mNativeInstance = createNativeInstance();
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 279e2937a80a..b840f3f7654d 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -95,13 +95,9 @@ public class ComposeShader extends Shader {
/** @hide */
@Override
- protected void verifyNativeInstance() {
- if (mShaderA.getNativeInstance() != mNativeInstanceShaderA
- || mShaderB.getNativeInstance() != mNativeInstanceShaderB) {
- // Child shader native instance has been updated,
- // so our cached native instance is no longer valid - discard it
- discardNativeInstance();
- }
+ protected boolean shouldDiscardNativeInstance() {
+ return mShaderA.getNativeInstance() != mNativeInstanceShaderA
+ || mShaderB.getNativeInstance() != mNativeInstanceShaderB;
}
private static native long nativeCreate(long nativeMatrix,
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 6dd1f9939989..1da434496297 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -872,11 +872,6 @@ public class HardwareRenderer {
}
}
- /** @hide */
- public static void invokeFunctor(long functor, boolean waitForCompletion) {
- nInvokeFunctor(functor, waitForCompletion);
- }
-
/**
* b/68769804: For low FPS experiments.
*
@@ -1246,8 +1241,6 @@ public class HardwareRenderer {
private static native void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator);
- private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
-
private static native long nCreateTextureLayer(long nativeProxy);
private static native void nBuildLayer(long nativeProxy, long node);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3b586242e5b1..28d7911c771f 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -674,10 +674,15 @@ public class Paint {
* Return the pointer to the native object while ensuring that any
* mutable objects that are attached to the paint are also up-to-date.
*
+ * Note: Although this method is |synchronized|, this is simply so it
+ * is not thread-hostile to multiple threads calling this method. It
+ * is still unsafe to attempt to change the Shader/ColorFilter while
+ * another thread attempts to access the native object.
+ *
* @hide
*/
@UnsupportedAppUsage
- public long getNativeInstance() {
+ public synchronized long getNativeInstance() {
long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
if (newNativeShader != mNativeShader) {
mNativeShader = newNativeShader;
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 22aacdecaf5d..9f46ceb61332 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -17,11 +17,9 @@
package android.graphics;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.util.Pools.SynchronizedPool;
import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
/**
* A Canvas implementation that records view system drawing operations for deferred rendering.
@@ -146,44 +144,10 @@ public final class RecordingCanvas extends BaseRecordingCanvas {
}
///////////////////////////////////////////////////////////////////////////
- // Functor
+ // WebView
///////////////////////////////////////////////////////////////////////////
/**
- * Records the functor specified with the drawGLFunction function pointer. This is
- * functionality used by webview for calling into their renderer from our display lists.
- *
- * @param drawGLFunction A native function pointer
- *
- * @hide
- * @deprecated Use {@link #drawWebViewFunctor(int)}
- */
- @Deprecated
- public void callDrawGLFunction2(long drawGLFunction) {
- nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction, null);
- }
-
- /**
- * Records the functor specified with the drawGLFunction function pointer. This is
- * functionality used by webview for calling into their renderer from our display lists.
- *
- * @param drawGLFunctor A native function pointer
- * @param releasedCallback Called when the display list is destroyed, and thus
- * the functor is no longer referenced by this canvas's display list.
- *
- * NOTE: The callback does *not* necessarily mean that there are no longer
- * any references to the functor, just that the reference from this specific
- * canvas's display list has been released.
- *
- * @hide
- * @deprecated Use {@link #drawWebViewFunctor(int)}
- */
- @Deprecated
- public void drawGLFunctor2(long drawGLFunctor, @Nullable Runnable releasedCallback) {
- nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
- }
-
- /**
* Calls the provided functor that was created via WebViewFunctor_create()
* @hide
*/
@@ -273,13 +237,6 @@ public final class RecordingCanvas extends BaseRecordingCanvas {
}
- // ------------------ Fast JNI ------------------------
-
- @FastNative
- private static native void nCallDrawGLFunction(long renderer,
- long drawGLFunction, Runnable releasedCallback);
-
-
// ------------------ Critical JNI ------------------------
@CriticalNative
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index d8d96413a93d..43373ffbd3f4 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -409,10 +409,10 @@ public class Region implements Parcelable {
mNativeRegion = ni;
}
- /* add dummy parameter so constructor can be called from jni without
+ /* Add an unused parameter so constructor can be called from jni without
triggering 'not cloneable' exception */
@UnsupportedAppUsage
- private Region(long ni, int dummy) {
+ private Region(long ni, int unused) {
this(ni);
}
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index fb15d0794dd7..8154ebf1e508 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -150,7 +150,12 @@ public class Shader {
/**
* @hide Only to be used by subclasses in the graphics package.
*/
- protected final void discardNativeInstance() {
+ protected synchronized final void discardNativeInstance() {
+ discardNativeInstanceLocked();
+ }
+
+ // For calling inside a synchronized method.
+ private void discardNativeInstanceLocked() {
if (mNativeInstance != 0) {
mCleaner.run();
mCleaner = null;
@@ -159,11 +164,12 @@ public class Shader {
}
/**
- * Callback for subclasses to call {@link #discardNativeInstance()} if the most recently
- * constructed native instance is no longer valid.
+ * Callback for subclasses to specify whether the most recently
+ * constructed native instance is still valid.
* @hide Only to be used by subclasses in the graphics package.
*/
- protected void verifyNativeInstance() {
+ protected boolean shouldDiscardNativeInstance() {
+ return false;
}
@@ -171,9 +177,10 @@ public class Shader {
* @hide so it can be called by android.graphics.drawable but must not be called from outside
* the module.
*/
- public final long getNativeInstance() {
- // verify mNativeInstance is valid
- verifyNativeInstance();
+ public synchronized final long getNativeInstance() {
+ if (shouldDiscardNativeInstance()) {
+ discardNativeInstanceLocked();
+ }
if (mNativeInstance == 0) {
mNativeInstance = createNativeInstance(mLocalMatrix == null
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index d4cf53e874f1..6ad8d2c0aca3 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -31,7 +31,6 @@ import com.android.org.bouncycastle.asn1.ASN1InputStream;
import com.android.org.bouncycastle.asn1.ASN1Integer;
import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
import com.android.org.bouncycastle.asn1.DERBitString;
-import com.android.org.bouncycastle.asn1.DERInteger;
import com.android.org.bouncycastle.asn1.DERNull;
import com.android.org.bouncycastle.asn1.DERSequence;
import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -702,8 +701,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
sigAlgOid = X9ObjectIdentifiers.ecdsa_with_SHA256;
sigAlgId = new AlgorithmIdentifier(sigAlgOid);
ASN1EncodableVector v = new ASN1EncodableVector();
- v.add(new DERInteger(0));
- v.add(new DERInteger(0));
+ v.add(new ASN1Integer(BigInteger.valueOf(0)));
+ v.add(new ASN1Integer(BigInteger.valueOf(0)));
signature = new DERSequence().getEncoded();
break;
case KeymasterDefs.KM_ALGORITHM_RSA:
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index b8934dc8c583..843b17703676 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -22,8 +22,4 @@ android_library {
"res",
],
manifest: "AndroidManifest.xml",
-
- platform_apis: true,
- sdk_version: "current",
- min_sdk_version: "system_current",
}
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_focus_gain_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml
index 257bf35c8e76..29d9b257cc59 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_focus_gain_animation.xml
+++ b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="alpha"
android:valueTo="1"
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_focus_loss_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml
index e032008b3750..70f553b89657 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_focus_loss_animation.xml
+++ b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="alpha"
android:valueTo="0"
diff --git a/packages/SystemUI/res/anim/tv_pip_menu_fade_in_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml
index 257bf35c8e76..29d9b257cc59 100644
--- a/packages/SystemUI/res/anim/tv_pip_menu_fade_in_animation.xml
+++ b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="alpha"
android:valueTo="1"
diff --git a/packages/SystemUI/res/anim/tv_pip_menu_fade_out_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml
index e032008b3750..70f553b89657 100644
--- a/packages/SystemUI/res/anim/tv_pip_menu_fade_out_animation.xml
+++ b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="alpha"
android:valueTo="0"
diff --git a/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient.xml b/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient.xml
new file mode 100644
index 000000000000..8b3057d5841e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="270"
+ android:startColor="#00000000"
+ android:endColor="#77000000"
+ android:type="linear" />
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient_transition.xml b/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient_transition.xml
new file mode 100644
index 000000000000..772d0a5ea89b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient_transition.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@android:color/transparent" />
+ <item android:drawable="@drawable/floating_dismiss_gradient" />
+</transition> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml b/libs/WindowManager/Shell/res/drawable/pip_expand.xml
index 6a0695e817c7..c99d81934aab 100644
--- a/packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_expand.xml
@@ -13,7 +13,16 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@color/transparent" />
- <item android:drawable="@drawable/floating_dismiss_gradient" />
-</transition> \ No newline at end of file
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36"
+ android:viewportHeight="36">
+
+ <path
+ android:pathData="M0 0h36v36H0z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M10 21H7v8h8v-3h-5v-5zm-3-6h3v-5h5V7H7v8zm19 11h-5v3h8v-8h-3v5zM21
+7v3h5v5h3V7h-8z" />
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
new file mode 100644
index 000000000000..bcc850a854de
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_fullscreen_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_fullscreen_white.xml
new file mode 100644
index 000000000000..56699dc04e10
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_fullscreen_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_pause_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml
index 5b65f100490c..ef9b2d9c1c63 100644
--- a/packages/SystemUI/res/drawable/ic_pause_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml
@@ -1,7 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2016 The Android Open Source Project
+ Copyright (C) 2020 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/packages/SystemUI/res/drawable/ic_play_arrow_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml
index ddc9e8dd17a0..f12d2cbebc87 100644
--- a/packages/SystemUI/res/drawable/ic_play_arrow_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml
@@ -1,7 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2016 The Android Open Source Project
+ Copyright (C) 2020 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml
new file mode 100644
index 000000000000..b61e98ce2f9f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/pip_icon.xml b/libs/WindowManager/Shell/res/drawable/pip_icon.xml
index bd92ccd2e6e3..b19d907d1ff3 100644
--- a/packages/SystemUI/res/drawable/pip_icon.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_icon.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2017 The Android Open Source Project
+ Copyright (C) 2020 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/packages/SystemUI/res/drawable/pip_resize_handle.xml b/libs/WindowManager/Shell/res/drawable/pip_resize_handle.xml
index 0a8cbc429dd8..4d1e080cf466 100644
--- a/packages/SystemUI/res/drawable/pip_resize_handle.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_resize_handle.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2020 The Android Open Source Project
+ Copyright (C) 2020 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml
new file mode 100644
index 000000000000..cce13035dba7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#9AFFFFFF" android:radius="17dp" />
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml
new file mode 100644
index 000000000000..7a026ca63f50
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ImageButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/pip_action_size"
+ android:layout_height="@dimen/pip_action_size"
+ android:padding="@dimen/pip_action_padding"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:forceHasOverlappingRendering="false" />
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml
index 2b33e17a5fbd..2e0a5e09e34f 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml
@@ -1,17 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+ Copyright (C) 2020 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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"
@@ -61,25 +62,30 @@
</FrameLayout>
</FrameLayout>
- <ImageButton
- android:id="@+id/settings"
- android:layout_width="@dimen/pip_action_size"
- android:layout_height="@dimen/pip_action_size"
- android:layout_gravity="top|start"
- android:padding="@dimen/pip_action_padding"
- android:contentDescription="@string/pip_phone_settings"
- android:src="@drawable/ic_settings"
- android:background="?android:selectableItemBackgroundBorderless" />
-
- <ImageButton
- android:id="@+id/dismiss"
- android:layout_width="@dimen/pip_action_size"
- android:layout_height="@dimen/pip_action_size"
+ <LinearLayout
+ android:id="@+id/top_end_container"
android:layout_gravity="top|end"
- android:padding="@dimen/pip_action_padding"
- android:contentDescription="@string/pip_phone_close"
- android:src="@drawable/ic_close_white"
- android:background="?android:selectableItemBackgroundBorderless" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <ImageButton
+ android:id="@+id/settings"
+ android:layout_width="@dimen/pip_action_size"
+ android:layout_height="@dimen/pip_action_size"
+ android:padding="@dimen/pip_action_padding"
+ android:contentDescription="@string/pip_phone_settings"
+ android:src="@drawable/pip_ic_settings"
+ android:background="?android:selectableItemBackgroundBorderless" />
+
+ <ImageButton
+ android:id="@+id/dismiss"
+ android:layout_width="@dimen/pip_action_size"
+ android:layout_height="@dimen/pip_action_size"
+ android:padding="@dimen/pip_action_padding"
+ android:contentDescription="@string/pip_phone_close"
+ android:src="@drawable/pip_ic_close_white"
+ android:background="?android:selectableItemBackgroundBorderless" />
+ </LinearLayout>
<!--TODO (b/156917828): Add content description for a11y purposes?-->
<ImageButton
@@ -88,6 +94,7 @@
android:layout_height="@dimen/pip_resize_handle_size"
android:layout_gravity="top|start"
android:layout_margin="@dimen/pip_resize_handle_margin"
+ android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_control_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml
index b9b0154e70b7..72287c144bed 100644
--- a/packages/SystemUI/res/layout/tv_pip_control_button.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml
@@ -1,22 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-**
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
<!-- Layout for {@link com.android.systemui.pip.tv.PipControlButtonView}. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml
index 0b7bce13d761..22e0452d620d 100644
--- a/packages/SystemUI/res/layout/tv_pip_controls.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml
@@ -1,22 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-**
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
<!-- Layout for {@link com.android.systemui.pip.tv.PipControlsView}. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
@@ -24,7 +21,7 @@
android:id="@+id/full_button"
android:layout_width="@dimen/picture_in_picture_button_width"
android:layout_height="wrap_content"
- android:src="@drawable/ic_fullscreen_white_24dp"
+ android:src="@drawable/pip_ic_fullscreen_white"
android:text="@string/pip_fullscreen" />
<com.android.systemui.pip.tv.PipControlButtonView
@@ -32,7 +29,7 @@
android:layout_width="@dimen/picture_in_picture_button_width"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
- android:src="@drawable/ic_close_white"
+ android:src="@drawable/pip_ic_close_white"
android:text="@string/pip_close" />
<com.android.systemui.pip.tv.PipControlButtonView
@@ -40,7 +37,7 @@
android:layout_width="@dimen/picture_in_picture_button_width"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
- android:src="@drawable/ic_pause_white"
+ android:src="@drawable/pip_ic_pause_white"
android:text="@string/pip_pause"
android:visibility="gone" />
</merge>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml b/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml
new file mode 100644
index 000000000000..e6cd1122ca77
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.systemui.pip.tv.PipControlButtonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/picture_in_picture_button_width"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" />
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
new file mode 100644
index 000000000000..a049787b40b9
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:paddingTop="350dp"
+ android:background="#CC000000"
+ android:gravity="top|center_horizontal"
+ android:clipChildren="false">
+
+ <com.android.systemui.pip.tv.PipControlsView
+ android:id="@+id/pip_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:alpha="0" />
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
new file mode 100644
index 000000000000..7920fd237a08
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- The dimensions to user for picture-in-picture action buttons. -->
+ <dimen name="picture_in_picture_button_width">100dp</dimen>
+ <dimen name="picture_in_picture_button_start_margin">-50dp</dimen>
+</resources>
+
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index c894eb0133b5..245c0725c2a8 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -1,21 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
-** Copyright 2019, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
<resources>
-</resources> \ No newline at end of file
+ <!-- Animation duration for resizing of PIP when entering/exiting. -->
+ <integer name="config_pipResizeAnimationDuration">425</integer>
+
+ <!-- Allow dragging the PIP to a location to close it -->
+ <bool name="config_pipEnableDismissDragToEdge">true</bool>
+
+ <!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu -->
+ <bool name="config_pipEnableResizeForMenu">true</bool>
+
+ <!-- Allow PIP to enable round corner, see also R.dimen.pip_corner_radius -->
+ <bool name="config_pipEnableRoundCorner">false</bool>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
new file mode 100644
index 000000000000..1c1217681b9f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="dismiss_circle_size">52dp</dimen>
+
+ <!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
+ <dimen name="floating_dismiss_gradient_height">250dp</dimen>
+
+ <!-- The padding around a PiP actions. -->
+ <dimen name="pip_action_padding">12dp</dimen>
+
+ <!-- The height of the PiP actions container in which the actions are vertically centered. -->
+ <dimen name="pip_action_size">48dp</dimen>
+
+ <!-- The padding between actions in the PiP in landscape Note that the PiP does not reflect
+ the configuration of the device, so we can't use -land resources. -->
+ <dimen name="pip_between_action_padding_land">8dp</dimen>
+
+ <!-- The buffer to use when calculating whether the pip is in an adjust zone. -->
+ <dimen name="pip_bottom_offset_buffer">1dp</dimen>
+
+ <!-- The corner radius for PiP window. -->
+ <dimen name="pip_corner_radius">8dp</dimen>
+
+ <!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. -->
+ <dimen name="pip_dismiss_text_bottom_margin">24dp</dimen>
+
+ <!-- The bottom margin of the expand container when there are actions.
+ Equal to pip_action_size - pip_action_padding. -->
+ <dimen name="pip_expand_container_edge_margin">30dp</dimen>
+
+ <!-- The shortest-edge size of the expanded PiP. -->
+ <dimen name="pip_expanded_shortest_edge_size">160dp</dimen>
+
+ <!-- The additional offset to apply to the IME animation to account for the input field. -->
+ <dimen name="pip_ime_offset">48dp</dimen>
+
+ <!-- The touchable/draggable edge size for PIP resize. -->
+ <dimen name="pip_resize_edge_size">48dp</dimen>
+
+ <!-- PIP Resize handle size, margin and padding. -->
+ <dimen name="pip_resize_handle_size">12dp</dimen>
+ <dimen name="pip_resize_handle_margin">4dp</dimen>
+ <dimen name="pip_resize_handle_padding">0dp</dimen>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml
new file mode 100644
index 000000000000..ed20398f309d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/ids.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <item type="id" name="action_pip_resize" />
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
new file mode 100644
index 000000000000..6752b56fcdf3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for PIP close button [CHAR LIMIT=NONE]-->
+ <string name="pip_phone_close">Close</string>
+
+ <!-- Making the PIP fullscreen [CHAR LIMIT=25] -->
+ <string name="pip_phone_expand">Expand</string>
+
+ <!-- Label for PIP settings button [CHAR LIMIT=NONE]-->
+ <string name="pip_phone_settings">Settings</string>
+
+ <!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
+ <string name="pip_menu_title">Menu</string>
+
+ <!-- PiP BTW notification title. [CHAR LIMIT=50] -->
+ <string name="pip_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is in picture-in-picture</string>
+
+ <!-- PiP BTW notification description. [CHAR LIMIT=NONE] -->
+ <string name="pip_notification_message">If you don\'t want <xliff:g id="name" example="Google Maps">%s</xliff:g> to use this feature, tap to open settings and turn it off.</string>
+
+ <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+ <string name="pip_play">Play</string>
+
+ <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+ <string name="pip_pause">Pause</string>
+
+ <!-- Button to skip to the next media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+ <string name="pip_skip_to_next">Skip to next</string>
+
+ <!-- Button to skip to the prev media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+ <string name="pip_skip_to_prev">Skip to previous</string>
+
+ <!-- Accessibility action for resizing PIP [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_pip_resize">Resize</string>
+
+ <!-- TODO Deprecated. Label for PIP action to Minimize the PIP. DO NOT TRANSLATE [CHAR LIMIT=25] -->
+ <string name="pip_phone_minimize">Minimize</string>
+
+ <!-- TODO Deprecated. Label for PIP the drag to dismiss hint. DO NOT TRANSLATE [CHAR LIMIT=NONE]-->
+ <string name="pip_phone_dismiss_hint">Drag down to dismiss</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/strings_tv.xml b/libs/WindowManager/Shell/res/values/strings_tv.xml
new file mode 100644
index 000000000000..2dfdcabaa931
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/strings_tv.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Picture-in-Picture (PIP) notification -->
+ <!-- Title for the notification channel for TV PIP controls. [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_tv_pip">Picture-in-Picture</string>
+
+ <!-- Title of the picture-in-picture (PIP) notification title
+ when the media doesn't have title [CHAR LIMIT=NONE] -->
+ <string name="pip_notification_unknown_title">(No title program)</string>
+
+ <!-- Picture-in-Picture (PIP) menu -->
+ <eat-comment />
+ <!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=30] -->
+ <string name="pip_close">Close PIP</string>
+
+ <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
+ <string name="pip_fullscreen">Full screen</string>
+</resources>
+
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index 6eba9acbab4e..3263f79888d6 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import android.os.Handler;
import android.os.RemoteException;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 083c2439aa87..418973204add 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import android.annotation.Nullable;
import android.content.Context;
@@ -28,21 +28,16 @@ import android.view.Display;
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.wm.DisplayChangeController.OnDisplayChangingListener;
+import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener;
import java.util.ArrayList;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* This module deals with display rotations coming from WM. When WM starts a rotation: after it has
* frozen the screen, it will call into this class. This will then call all registered local
* controllers and give them a chance to queue up task changes to be applied synchronously with that
* rotation.
*/
-@Singleton
public class DisplayController {
private static final String TAG = "DisplayController";
@@ -55,7 +50,7 @@ public class DisplayController {
private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
/**
- * Get's a display by id from DisplayManager.
+ * Gets a display by id from DisplayManager.
*/
public Display getDisplay(int displayId) {
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
@@ -169,10 +164,9 @@ public class DisplayController {
}
};
- @Inject
- public DisplayController(Context context, @Main Handler mainHandler,
+ public DisplayController(Context context, Handler handler,
IWindowManager wmService) {
- mHandler = mainHandler;
+ mHandler = handler;
mContext = context;
mWmService = wmService;
mChangeController = new DisplayChangeController(mHandler, mWmService);
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 8ba5b9951c54..338ece5afbc2 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
@@ -29,6 +30,7 @@ import android.os.ServiceManager;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -39,18 +41,12 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import com.android.internal.view.IInputMethodManager;
-import com.android.systemui.TransactionPool;
-import com.android.systemui.dagger.qualifiers.Main;
import java.util.ArrayList;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
*/
-@Singleton
public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayImeController";
@@ -65,21 +61,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
private static final int DIRECTION_HIDE = 2;
private static final int FLOATING_IME_BOTTOM_INSET = -80;
- SystemWindows mSystemWindows;
- final Handler mHandler;
- final TransactionPool mTransactionPool;
-
- final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
+ protected final IWindowManager mWmService;
+ protected final Handler mHandler;
+ private final TransactionPool mTransactionPool;
+ private final DisplayController mDisplayController;
+ private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
+ private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
- final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
-
- @Inject
- public DisplayImeController(SystemWindows syswin, DisplayController displayController,
- @Main Handler mainHandler, TransactionPool transactionPool) {
+ public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+ Handler mainHandler, TransactionPool transactionPool) {
mHandler = mainHandler;
- mSystemWindows = syswin;
+ mWmService = wmService;
mTransactionPool = transactionPool;
- displayController.addDisplayWindowListener(this);
+ mDisplayController = displayController;
+ mDisplayController.addDisplayWindowListener(this);
}
@Override
@@ -87,9 +82,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// Add's a system-ui window-manager specifically for ime. This type is special because
// WM will defer IME inset handling to it in multi-window scenarious.
PerDisplay pd = new PerDisplay(displayId,
- mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
+ mDisplayController.getDisplayLayout(displayId).rotation());
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ mWmService.setDisplayWindowInsetsController(displayId, pd);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
}
@@ -102,7 +97,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if (pd == null) {
return;
}
- if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
+ if (mDisplayController.getDisplayLayout(displayId).rotation()
!= pd.mRotation && isImeShowing(displayId)) {
pd.startAnimation(true, false /* forceRestart */);
}
@@ -111,7 +106,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
@Override
public void onDisplayRemoved(int displayId) {
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
}
@@ -136,12 +131,16 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
- private void dispatchStartPositioning(int displayId, int hiddenTop, int shownTop,
- boolean show, SurfaceControl.Transaction t) {
+ @ImePositionProcessor.ImeAnimationFlags
+ private int dispatchStartPositioning(int displayId, int hiddenTop, int shownTop,
+ boolean show, boolean isFloating, SurfaceControl.Transaction t) {
synchronized (mPositionProcessors) {
+ int flags = 0;
for (ImePositionProcessor pp : mPositionProcessors) {
- pp.onImeStartPositioning(displayId, hiddenTop, shownTop, show, t);
+ flags |= pp.onImeStartPositioning(
+ displayId, hiddenTop, shownTop, show, isFloating, t);
}
+ return flags;
}
}
@@ -184,6 +183,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
int mRotation = Surface.ROTATION_0;
boolean mImeShowing = false;
final Rect mImeFrame = new Rect();
+ boolean mAnimateAlpha = true;
PerDisplay(int displayId, int initialRotation) {
mDisplayId = displayId;
@@ -226,6 +226,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if (!activeControl.getSurfacePosition().equals(lastSurfacePosition)
&& mAnimation != null) {
startAnimation(mImeShowing, true /* forceRestart */);
+ } else if (!mImeShowing) {
+ removeImeSurface();
}
});
}
@@ -251,13 +253,18 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */));
}
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ // no-op
+ }
+
/**
* Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
*/
private void setVisibleDirectly(boolean visible) {
mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
try {
- mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
} catch (RemoteException e) {
}
}
@@ -266,23 +273,37 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
return mImeFrame.top + (int) surfaceOffset;
}
+ private boolean calcIsFloating(InsetsSource imeSource) {
+ final Rect frame = imeSource.getFrame();
+ if (frame.height() == 0) {
+ return true;
+ }
+ // Some Floating Input Methods will still report a frame, but the frame is actually
+ // a nav-bar inset created by WM and not part of the IME (despite being reported as
+ // an IME inset). For now, we assume that no non-floating IME will be <= this nav bar
+ // frame height so any reported frame that is <= nav-bar frame height is assumed to
+ // be floating.
+ return frame.height() <= mDisplayController.getDisplayLayout(mDisplayId)
+ .navBarFrameHeight();
+ }
+
private void startAnimation(final boolean show, final boolean forceRestart) {
final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
if (imeSource == null || mImeSourceControl == null) {
return;
}
- // Set frame, but only if the new frame isn't empty -- this maintains continuity
final Rect newFrame = imeSource.getFrame();
- mImeFrame.set(newFrame);
- final boolean isFloating = newFrame.height() == 0;
+ final boolean isFloating = calcIsFloating(imeSource) && show;
if (isFloating) {
- // This is likely a "floating" or "expanded" IME, so to get animations, just
+ // This is a "floating" or "expanded" IME, so to get animations, just
// pretend the ime has some size just below the screen.
mImeFrame.set(newFrame);
- final int floatingInset = (int) (
- mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId).density()
- * FLOATING_IME_BOTTOM_INSET);
+ final int floatingInset = (int) (mDisplayController.getDisplayLayout(mDisplayId)
+ .density() * FLOATING_IME_BOTTOM_INSET);
mImeFrame.bottom -= floatingInset;
+ } else if (newFrame.height() != 0) {
+ // Don't set a new frame if it's empty and hiding -- this maintains continuity
+ mImeFrame.set(newFrame);
}
if (DEBUG) {
Slog.d(TAG, "Run startAnim show:" + show + " was:"
@@ -326,7 +347,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
SurfaceControl.Transaction t = mTransactionPool.acquire();
float value = (float) animation.getAnimatedValue();
t.setPosition(mImeSourceControl.getLeash(), x, value);
- final float alpha = isFloating ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
+ final float alpha = (mAnimateAlpha || isFloating)
+ ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
t.setAlpha(mImeSourceControl.getLeash(), alpha);
dispatchPositionChanged(mDisplayId, imeTop(value), t);
t.apply();
@@ -335,30 +357,35 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mAnimation.setInterpolator(INTERPOLATOR);
mAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled = false;
+
@Override
public void onAnimationStart(Animator animation) {
SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setPosition(mImeSourceControl.getLeash(), x, startY);
- final float alpha = isFloating ? (startY - hiddenY) / (shownY - hiddenY) : 1.f;
- t.setAlpha(mImeSourceControl.getLeash(), alpha);
if (DEBUG) {
Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
+ imeTop(hiddenY) + "->" + imeTop(shownY)
+ " showing:" + (mAnimationDirection == DIRECTION_SHOW));
}
- dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
- imeTop(shownY), mAnimationDirection == DIRECTION_SHOW,
- t);
+ int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
+ imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t);
+ mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0;
+ final float alpha = (mAnimateAlpha || isFloating)
+ ? (startY - hiddenY) / (shownY - hiddenY)
+ : 1.f;
+ t.setAlpha(mImeSourceControl.getLeash(), alpha);
if (mAnimationDirection == DIRECTION_SHOW) {
t.show(mImeSourceControl.getLeash());
}
t.apply();
mTransactionPool.release(t);
}
+
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
+
@Override
public void onAnimationEnd(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
@@ -370,16 +397,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
t.hide(mImeSourceControl.getLeash());
- final IInputMethodManager imms = getImms();
- if (imms != null) {
- try {
- // Remove the IME surface to make the insets invisible for
- // non-client controlled insets.
- imms.removeImeSurface();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to remove IME surface.", e);
- }
- }
+ removeImeSurface();
}
t.apply();
mTransactionPool.release(t);
@@ -402,20 +420,52 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
+ void removeImeSurface() {
+ final IInputMethodManager imms = getImms();
+ if (imms != null) {
+ try {
+ // Remove the IME surface to make the insets invisible for
+ // non-client controlled insets.
+ imms.removeImeSurface();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to remove IME surface.", e);
+ }
+ }
+ }
+
/**
* Allows other things to synchronize with the ime position
*/
public interface ImePositionProcessor {
/**
+ * Indicates that ime shouldn't animate alpha. It will always be opaque. Used when stuff
+ * behind the IME shouldn't be visible (for example during split-screen adjustment where
+ * there is nothing behind the ime).
+ */
+ int IME_ANIMATION_NO_ALPHA = 1;
+
+ /** @hide */
+ @IntDef(prefix = {"IME_ANIMATION_"}, value = {
+ IME_ANIMATION_NO_ALPHA,
+ })
+ @interface ImeAnimationFlags {
+ }
+
+ /**
* Called when the IME position is starting to animate.
*
- * @param hiddenTop The y position of the top of the IME surface when it is hidden.
- * @param shownTop The y position of the top of the IME surface when it is shown.
- * @param showing {@code true} when we are animating from hidden to shown, {@code false}
- * when animating from shown to hidden.
+ * @param hiddenTop The y position of the top of the IME surface when it is hidden.
+ * @param shownTop The y position of the top of the IME surface when it is shown.
+ * @param showing {@code true} when we are animating from hidden to shown, {@code false}
+ * when animating from shown to hidden.
+ * @param isFloating {@code true} when the ime is a floating ime (doesn't inset).
+ * @return flags that may alter how ime itself is animated (eg. no-alpha).
*/
- default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
- boolean showing, SurfaceControl.Transaction t) {}
+ @ImeAnimationFlags
+ default int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
+ boolean showing, boolean isFloating, SurfaceControl.Transaction t) {
+ return 0;
+ }
/**
* Called when the ime position changed. This is expected to be a synchronous call on the
@@ -423,8 +473,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
*
* @param imeTop The current y position of the top of the IME surface.
*/
- default void onImePositionChanged(int displayId, int imeTop,
- SurfaceControl.Transaction t) {}
+ default void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
+ }
/**
* Called when the IME position is done animating.
@@ -432,7 +482,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
* @param cancel {@code true} if this was cancelled. This implies another start is coming.
*/
default void onImeEndPositioning(int displayId, boolean cancel,
- SurfaceControl.Transaction t) {}
+ SurfaceControl.Transaction t) {
+ }
}
public IInputMethodManager getImms() {
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index cfec1c07ff1d..3181dbf74ace 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -52,7 +52,7 @@ import java.lang.annotation.RetentionPolicy;
/**
* Contains information about the layout-properties of a display. This refers to internal layout
- * like insets/cutout/rotation. In general, this can be thought of as the System-UI analog to
+ * like insets/cutout/rotation. In general, this can be thought of as the shell analog to
* DisplayPolicy.
*/
public class DisplayLayout {
@@ -79,6 +79,7 @@ public class DisplayLayout {
private final Rect mStableInsets = new Rect();
private boolean mHasNavigationBar = false;
private boolean mHasStatusBar = false;
+ private int mNavBarFrameHeight = 0;
/**
* Create empty layout.
@@ -146,6 +147,7 @@ public class DisplayLayout {
if (mHasStatusBar) {
convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar);
}
+ mNavBarFrameHeight = getNavigationBarFrameHeight(res, mWidth > mHeight);
}
/**
@@ -214,6 +216,11 @@ public class DisplayLayout {
return mWidth > mHeight;
}
+ /** Get the navbar frame height (used by ime). */
+ public int navBarFrameHeight() {
+ return mNavBarFrameHeight;
+ }
+
/** Gets the orientation of this layout */
public int getOrientation() {
return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
@@ -338,9 +345,9 @@ public class DisplayLayout {
/** Retrieve the statusbar height from resources. */
static int getStatusBarHeight(boolean landscape, Resources res) {
return landscape ? res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height_landscape)
- : res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height_portrait);
+ com.android.internal.R.dimen.status_bar_height_landscape)
+ : res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
}
/** Calculate the DisplayCutout for a particular display size/rotation. */
@@ -483,6 +490,7 @@ public class DisplayLayout {
} else {
return res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode);
}
+
} else {
if (navBarSide == NAV_BAR_BOTTOM) {
return res.getDimensionPixelSize(landscape
@@ -493,4 +501,11 @@ public class DisplayLayout {
}
}
}
+
+ /** @see com.android.server.wm.DisplayPolicy#getNavigationBarFrameHeight */
+ public static int getNavigationBarFrameHeight(Resources res, boolean landscape) {
+ return res.getDimensionPixelSize(landscape
+ ? R.dimen.navigation_bar_frame_height_landscape
+ : R.dimen.navigation_bar_frame_height);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 21f67aef5604..8abe9eeb6a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
@@ -52,23 +52,18 @@ import com.android.internal.os.IResultReceiver;
import java.util.HashMap;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
- * Represents the "windowing" layer of the System-UI. This layer allows system-ui components to
- * place and manipulate windows without talking to WindowManager.
+ * Represents the "windowing" layer of the WM Shell. This layer allows shell components to place and
+ * manipulate windows without talking to WindowManager.
*/
-@Singleton
public class SystemWindows {
private static final String TAG = "SystemWindows";
private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>();
- final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
- Context mContext;
- IWindowSession mSession;
- DisplayController mDisplayController;
- IWindowManager mWmService;
+ private final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
+ private final DisplayController mDisplayController;
+ private final IWindowManager mWmService;
+ private IWindowSession mSession;
private final DisplayController.OnDisplaysChangedListener mDisplayListener =
new DisplayController.OnDisplaysChangedListener() {
@@ -88,10 +83,7 @@ public class SystemWindows {
public void onDisplayRemoved(int displayId) { }
};
- @Inject
- public SystemWindows(Context context, DisplayController displayController,
- IWindowManager wmService) {
- mContext = context;
+ public SystemWindows(DisplayController displayController, IWindowManager wmService) {
mWmService = wmService;
mDisplayController = displayController;
mDisplayController.addDisplayWindowListener(mDisplayListener);
@@ -172,7 +164,7 @@ public class SystemWindows {
/**
* Get the IWindow token for a specific root.
*
- * @param windowType A window type from {@link android.view.WindowManager}.
+ * @param windowType A window type from {@link WindowManager}.
*/
IWindow getWindow(int displayId, int windowType) {
PerDisplay pd = mPerDisplay.get(displayId);
@@ -215,8 +207,8 @@ public class SystemWindows {
}
final Display display = mDisplayController.getDisplay(mDisplayId);
SurfaceControlViewHost viewRoot =
- new SurfaceControlViewHost(mContext, display, wwm,
- true /* useSfChoreographer */);
+ new SurfaceControlViewHost(
+ view.getContext(), display, wwm, true /* useSfChoreographer */);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
@@ -318,7 +310,7 @@ public class SystemWindows {
}
}
- class ContainerWindow extends IWindow.Stub {
+ static class ContainerWindow extends IWindow.Stub {
ContainerWindow() {}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/TransactionPool.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java
index 801cf8a7523b..4c34566b0d98 100644
--- a/packages/SystemUI/src/com/android/systemui/TransactionPool.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java
@@ -14,24 +14,19 @@
* limitations under the License.
*/
-package com.android.systemui;
+package com.android.wm.shell.common;
import android.util.Pools;
import android.view.SurfaceControl;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* Provides a synchronized pool of {@link SurfaceControl.Transaction}s to minimize allocations.
*/
-@Singleton
public class TransactionPool {
private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool =
new Pools.SynchronizedPool<>(4);
- @Inject
- TransactionPool() {
+ public TransactionPool() {
}
/** Gets a transaction from the pool. */
diff --git a/libs/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp
index 78fa45ebdf94..9868879cebb9 100644
--- a/libs/WindowManager/Shell/tests/Android.bp
+++ b/libs/WindowManager/Shell/tests/Android.bp
@@ -36,9 +36,6 @@ android_test {
"libstaticjvmtiagent",
],
- sdk_version: "current",
- platform_apis: true,
-
optimize: {
enabled: false,
},
diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
index 376875b143a1..f1ead3c8a441 100644
--- a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
+++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package com.android.wm.shell.tests;
+package com.android.wm.shell;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.WindowManagerShell;
-
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java
index 9596a73eaf3e..2b5b77e49e3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import static android.content.res.Configuration.UI_MODE_TYPE_NORMAL;
import static android.view.Surface.ROTATION_0;
@@ -35,12 +35,11 @@ import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
-import com.android.systemui.SysuiTestCase;
import org.junit.Test;
@SmallTest
-public class DisplayLayoutTest extends SysuiTestCase {
+public class DisplayLayoutTest {
@Test
public void testInsets() {
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
new file mode 100644
index 000000000000..77ef8dfb9725
--- /dev/null
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_fuzz {
+ name: "resourcefile_fuzzer",
+ srcs: [
+ "resourcefile_fuzzer.cpp",
+ ],
+ host_supported: true,
+ corpus: ["corpus/*"],
+ static_libs: ["libgmock"],
+ target: {
+ android: {
+ shared_libs:[
+ "libandroidfw",
+ "libbase",
+ "libcutils",
+ "libutils",
+ "libziparchive",
+ "libui",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libandroidfw",
+ "libbase",
+ "libcutils",
+ "libutils",
+ "libziparchive",
+ "liblog",
+ "libz",
+ ],
+ },
+ },
+}
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc b/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc
new file mode 100644
index 000000000000..3cf2ea733d28
--- /dev/null
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc
Binary files differ
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
new file mode 100644
index 000000000000..96d44ab8e45c
--- /dev/null
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include <memory>
+
+#include <androidfw/ApkAssets.h>
+#include <androidfw/LoadedArsc.h>
+#include <androidfw/StringPiece.h>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using android::ApkAssets;
+using android::LoadedArsc;
+using android::StringPiece;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(StringPiece(reinterpret_cast<const char*>(data), size));
+
+ return 0;
+} \ No newline at end of file
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 5a50245a3765..67d8c07e61de 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -149,6 +149,9 @@ void DeferredLayerUpdater::apply() {
sk_sp<SkImage> layerImage = mImageSlots[slot].createIfNeeded(
hardwareBuffer, dataspace, newContent,
mRenderState.getRenderThread().getGrContext());
+ // unref to match the ref added by ASurfaceTexture_dequeueBuffer. eglCreateImageKHR
+ // (invoked by createIfNeeded) will add a ref to the AHardwareBuffer.
+ AHardwareBuffer_release(hardwareBuffer);
if (layerImage.get()) {
SkMatrix textureTransform;
mat4(transformMatrix).copyTo(textureTransform);
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index d25fc4b0b03e..b2c39c90071a 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -139,6 +139,9 @@ void JankTracker::finishFrame(const FrameInfo& frame) {
(*mGlobalData)->reportJank();
}
+ if (mSwapDeadline < 0) {
+ mSwapDeadline = frame[FrameInfoIndex::IntendedVsync] + mFrameInterval;
+ }
bool isTripleBuffered = (mSwapDeadline - frame[FrameInfoIndex::IntendedVsync]) > (mFrameInterval * 0.1);
mSwapDeadline = std::max(mSwapDeadline + mFrameInterval,
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 4460266276f9..b3fbbfe98669 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -75,7 +75,7 @@ private:
std::array<int64_t, NUM_BUCKETS> mThresholds;
int64_t mFrameInterval;
- nsecs_t mSwapDeadline;
+ nsecs_t mSwapDeadline = -1;
// The amount of time we will erase from the total duration to account
// for SF vsync offsets with HWC2 blocking dequeueBuffers.
// (Vsync + mDequeueBlockTolerance) is the point at which we expect
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 49e606b705d2..242b8b0d139e 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -843,9 +843,4 @@ void SkiaCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw RenderNodes");
}
-void SkiaCanvas::callDrawGLFunction(Functor* functor,
- uirenderer::GlFunctorLifecycleListener* listener) {
- LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw GL Content");
-}
-
} // namespace android
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 260a7e407433..1df2b2671659 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -152,8 +152,6 @@ public:
virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
- virtual void callDrawGLFunction(Functor* functor,
- uirenderer::GlFunctorLifecycleListener* listener) override;
virtual void drawPicture(const SkPicture& picture) override;
protected:
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index c0a24438987a..1a89cfd5d0ad 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -209,11 +209,8 @@ static SkImageInfo validateAlpha(const SkImageInfo& info) {
void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) {
mInfo = validateAlpha(newInfo);
- // Dirty hack is dirty
- // TODO: Figure something out here, Skia's current design makes this
- // really hard to work with. Skia really, really wants immutable objects,
- // but with the nested-ref-count hackery going on that's just not
- // feasible without going insane trying to figure it out
+ // TODO: Skia intends for SkPixelRef to be immutable, but this method
+ // modifies it. Find another way to support reusing the same pixel memory.
this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes);
}
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 48cf9a96e3ca..333567b0cf91 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -20,7 +20,6 @@
#include <utils/Functor.h>
#include <androidfw/ResourceTypes.h>
-#include "GlFunctorLifecycleListener.h"
#include "Properties.h"
#include "utils/Macros.h"
@@ -162,8 +161,7 @@ public:
virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0;
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0;
- virtual void callDrawGLFunction(Functor* functor,
- uirenderer::GlFunctorLifecycleListener* listener) = 0;
+
virtual void drawWebViewFunctor(int /*functor*/) {
LOG_ALWAYS_FATAL("Not supported");
}
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 6a12a203b9f8..a6137b073d5a 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -125,22 +125,22 @@ const std::vector<minikin::FontVariation>& MinikinFontSkia::GetAxes() const {
std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation(
const std::vector<minikin::FontVariation>& variations) const {
- SkFontArguments params;
+ SkFontArguments args;
int ttcIndex;
std::unique_ptr<SkStreamAsset> stream(mTypeface->openStream(&ttcIndex));
LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed");
- params.setCollectionIndex(ttcIndex);
- std::vector<SkFontArguments::Axis> skAxes;
- skAxes.resize(variations.size());
+ args.setCollectionIndex(ttcIndex);
+ std::vector<SkFontArguments::VariationPosition::Coordinate> skVariation;
+ skVariation.resize(variations.size());
for (size_t i = 0; i < variations.size(); i++) {
- skAxes[i].fTag = variations[i].axisTag;
- skAxes[i].fStyleValue = SkFloatToScalar(variations[i].value);
+ skVariation[i].axis = variations[i].axisTag;
+ skVariation[i].value = SkFloatToScalar(variations[i].value);
}
- params.setAxes(skAxes.data(), skAxes.size());
+ args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
- sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), params));
+ sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), args));
return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, mFilePath,
ttcIndex, variations);
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index a2fef1e19328..68eaa0a3ca54 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -104,21 +104,21 @@ static jlong FontFamily_getFamilyReleaseFunc(CRITICAL_JNI_PARAMS) {
static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
jint weight, jint italic) {
- FatVector<SkFontArguments::Axis, 2> skiaAxes;
+ FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
for (const auto& axis : builder->axes) {
- skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
+ skVariation.push_back({axis.axisTag, axis.value});
}
const size_t fontSize = data->size();
const void* fontPtr = data->data();
std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
- SkFontArguments params;
- params.setCollectionIndex(ttcIndex);
- params.setAxes(skiaAxes.data(), skiaAxes.size());
+ SkFontArguments args;
+ args.setCollectionIndex(ttcIndex);
+ args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
- sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
+ sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
if (face == NULL) {
ALOGE("addFont failed to create font, invalid request");
builder->axes.clear();
diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
index e064cc8ff548..7c1422de0984 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -67,39 +67,7 @@ private:
JavaVM* mVm;
jobject mRunnable;
};
-
-class GlFunctorReleasedCallbackBridge : public GlFunctorLifecycleListener {
-public:
- GlFunctorReleasedCallbackBridge(JNIEnv* env, jobject javaCallback) {
- mLooper = Looper::getForThread();
- mMessage = new InvokeRunnableMessage(env, javaCallback);
- }
-
- virtual void onGlFunctorReleased(Functor* functor) override {
- mLooper->sendMessage(mMessage, 0);
- }
-
-private:
- sp<Looper> mLooper;
- sp<InvokeRunnableMessage> mMessage;
-};
-#endif
-
-// ---------------- @FastNative -----------------------------
-
-static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
- jlong canvasPtr, jlong functorPtr, jobject releasedCallback) {
-#ifdef __ANDROID__ // Layoutlib does not support GL
- Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
- Functor* functor = reinterpret_cast<Functor*>(functorPtr);
- sp<GlFunctorReleasedCallbackBridge> bridge;
- if (releasedCallback) {
- bridge = new GlFunctorReleasedCallbackBridge(env, releasedCallback);
- }
- canvas->callDrawGLFunction(functor, bridge.get());
#endif
-}
-
// ---------------- @CriticalNative -------------------------
@@ -183,12 +151,6 @@ static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAM
const char* const kClassPathName = "android/graphics/RecordingCanvas";
static JNINativeMethod gMethods[] = {
-
- // ------------ @FastNative ------------------
-
- { "nCallDrawGLFunction", "(JJLjava/lang/Runnable;)V",
- (void*) android_view_DisplayListCanvas_callDrawGLFunction },
-
// ------------ @CriticalNative --------------
{ "nCreateDisplayListCanvas", "(JII)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
{ "nResetDisplayListCanvas", "(JJII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 69c80beba39d..42743db3061c 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -256,12 +256,6 @@ static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv*
rootRenderNode->addVectorDrawableAnimator(animator);
}
-static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
- jlong functorPtr, jboolean waitForCompletion) {
- Functor* functor = reinterpret_cast<Functor*>(functorPtr);
- RenderProxy::invokeFunctor(functor, waitForCompletion);
-}
-
static jlong android_view_ThreadedRenderer_createTextureLayer(JNIEnv* env, jobject clazz,
jlong proxyPtr) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -684,7 +678,6 @@ static const JNINativeMethod gMethods[] = {
(void*)android_view_ThreadedRenderer_registerAnimatingRenderNode},
{"nRegisterVectorDrawableAnimator", "(JJ)V",
(void*)android_view_ThreadedRenderer_registerVectorDrawableAnimator},
- {"nInvokeFunctor", "(JZ)V", (void*)android_view_ThreadedRenderer_invokeFunctor},
{"nCreateTextureLayer", "(J)J", (void*)android_view_ThreadedRenderer_createTextureLayer},
{"nBuildLayer", "(JJ)V", (void*)android_view_ThreadedRenderer_buildLayer},
{"nCopyLayerInto", "(JJJ)Z", (void*)android_view_ThreadedRenderer_copyLayerInto},
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 5714cd1d0390..996cdceed8a7 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -93,19 +93,19 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo
sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
release_global_ref, reinterpret_cast<void*>(fontRef)));
- FatVector<SkFontArguments::Axis, 2> skiaAxes;
+ FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
for (const auto& axis : builder->axes) {
- skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
+ skVariation.push_back({axis.axisTag, axis.value});
}
std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
- SkFontArguments params;
- params.setCollectionIndex(ttcIndex);
- params.setAxes(skiaAxes.data(), skiaAxes.size());
+ SkFontArguments args;
+ args.setCollectionIndex(ttcIndex);
+ args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
- sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
+ sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
if (face == nullptr) {
jniThrowException(env, "java/lang/IllegalArgumentException",
"Failed to create internal object. maybe invalid font data.");
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index cf2f93b95e71..988a896b6267 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -16,8 +16,6 @@
#pragma once
-#include "GlFunctorLifecycleListener.h"
-
#include <SkCanvas.h>
#include <SkDrawable.h>
@@ -36,44 +34,21 @@ namespace skiapipeline {
*/
class FunctorDrawable : public SkDrawable {
public:
- FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
- : mBounds(canvas->getLocalClipBounds())
- , mAnyFunctor(std::in_place_type<LegacyFunctor>, functor, listener) {}
-
FunctorDrawable(int functor, SkCanvas* canvas)
: mBounds(canvas->getLocalClipBounds())
- , mAnyFunctor(std::in_place_type<NewFunctor>, functor) {}
+ , mWebViewHandle(WebViewFunctorManager::instance().handleFor(functor)) {}
virtual ~FunctorDrawable() {}
virtual void syncFunctor(const WebViewSyncData& data) const {
- if (mAnyFunctor.index() == 0) {
- std::get<0>(mAnyFunctor).handle->sync(data);
- } else {
- (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeSync, nullptr);
- }
+ mWebViewHandle->sync(data);
}
protected:
virtual SkRect onGetBounds() override { return mBounds; }
const SkRect mBounds;
-
- struct LegacyFunctor {
- explicit LegacyFunctor(Functor* functor, GlFunctorLifecycleListener* listener)
- : functor(functor), listener(listener) {}
- Functor* functor;
- sp<GlFunctorLifecycleListener> listener;
- };
-
- struct NewFunctor {
- explicit NewFunctor(int functor) {
- handle = WebViewFunctorManager::instance().handleFor(functor);
- }
- sp<WebViewFunctor::Handle> handle;
- };
-
- std::variant<NewFunctor, LegacyFunctor> mAnyFunctor;
+ sp<WebViewFunctor::Handle> mWebViewHandle;
};
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index e7ebfb06fd4a..14a297f785fc 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -18,7 +18,6 @@
#include <GrContext.h>
#include <private/hwui/DrawGlInfo.h>
#include "FunctorDrawable.h"
-#include "GlFunctorLifecycleListener.h"
#include "GrBackendSurface.h"
#include "GrRenderTarget.h"
#include "GrRenderTargetContext.h"
@@ -32,14 +31,6 @@ namespace android {
namespace uirenderer {
namespace skiapipeline {
-GLFunctorDrawable::~GLFunctorDrawable() {
- if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
- if (lp->listener) {
- lp->listener->onGlFunctorReleased(lp->functor);
- }
- }
-}
-
static void setScissor(int viewportHeight, const SkIRect& clip) {
SkASSERT(!clip.isEmpty());
// transform to Y-flipped GL space, and prevent negatives
@@ -48,29 +39,20 @@ static void setScissor(int viewportHeight, const SkIRect& clip) {
glScissor(clip.fLeft, y, clip.width(), height);
}
-static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
+static void GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
GrRenderTargetContext* renderTargetContext =
canvas->internal_private_accessTopLayerRenderTargetContext();
- if (!renderTargetContext) {
- ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
- return false;
- }
+ LOG_ALWAYS_FATAL_IF(!renderTargetContext, "Failed to retrieve GrRenderTargetContext");
GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget();
- if (!renderTarget) {
- ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
- return false;
- }
+ LOG_ALWAYS_FATAL_IF(!renderTarget, "accessRenderTarget failed");
GrGLFramebufferInfo fboInfo;
- if (!renderTarget->getBackendRenderTarget().getGLFramebufferInfo(&fboInfo)) {
- ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
- return false;
- }
+ LOG_ALWAYS_FATAL_IF(!renderTarget->getBackendRenderTarget().getGLFramebufferInfo(&fboInfo),
+ "getGLFrameBufferInfo failed");
*outFboID = fboInfo.fFBOID;
*outFboSize = SkISize::Make(renderTargetContext->width(), renderTargetContext->height());
- return true;
}
void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
@@ -85,11 +67,12 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
return;
}
+ // flush will create a GrRenderTarget if not already present.
+ canvas->flush();
+
GLuint fboID = 0;
SkISize fboSize;
- if (!GetFboDetails(canvas, &fboID, &fboSize)) {
- return;
- }
+ GetFboDetails(canvas, &fboID, &fboSize);
SkIRect surfaceBounds = canvas->internal_private_getTopLayerBounds();
SkIRect clipBounds = canvas->getDeviceClipBounds();
@@ -143,7 +126,6 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
// ensure that the framebuffer that the webview will render into is bound before we clear
// the stencil and/or draw the functor.
- canvas->flush();
glViewport(0, 0, info.width, info.height);
glBindFramebuffer(GL_FRAMEBUFFER, fboID);
@@ -195,11 +177,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
setScissor(info.height, clipRegion.getBounds());
}
- if (mAnyFunctor.index() == 0) {
- std::get<0>(mAnyFunctor).handle->drawGl(info);
- } else {
- (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
- }
+ mWebViewHandle->drawGl(info);
if (clearStencilAfterFunctor) {
// clear stencil buffer as it may be used by Skia
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index 2ea4f67428bc..4092e8dfa3a5 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -33,7 +33,7 @@ class GLFunctorDrawable : public FunctorDrawable {
public:
using FunctorDrawable::FunctorDrawable;
- virtual ~GLFunctorDrawable();
+ virtual ~GLFunctorDrawable() {}
protected:
void onDraw(SkCanvas* canvas) override;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 88c1d0e63c09..e292cbdd101f 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -132,23 +132,6 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
}
}
-
-void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
- uirenderer::GlFunctorLifecycleListener* listener) {
-#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
- FunctorDrawable* functorDrawable;
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
- functor, listener, asSkCanvas());
- } else {
- functorDrawable =
- mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, asSkCanvas());
- }
- mDisplayList->mChildFunctors.push_back(functorDrawable);
- drawDrawable(functorDrawable);
-#endif
-}
-
void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
FunctorDrawable* functorDrawable;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 1a976a6c8792..83e934974afd 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -72,8 +72,7 @@ public:
virtual void enableZ(bool enableZ) override;
virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
- virtual void callDrawGLFunction(Functor* functor,
- uirenderer::GlFunctorLifecycleListener* listener) override;
+
void drawWebViewFunctor(int functor) override;
private:
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 715a7cb42a8a..50b45e6eb7ec 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -121,12 +121,7 @@ std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDra
return nullptr;
}
std::unique_ptr<VkFunctorDrawHandler> draw;
- if (mAnyFunctor.index() == 0) {
- return std::make_unique<VkFunctorDrawHandler>(std::get<0>(mAnyFunctor).handle, matrix, clip,
- image_info);
- } else {
- LOG_ALWAYS_FATAL("Not implemented");
- }
+ return std::make_unique<VkFunctorDrawHandler>(mWebViewHandle, matrix, clip, image_info);
}
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 27c957d258f4..403d9075dbd1 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -155,11 +155,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
- if (mAnyFunctor.index() == 0) {
- std::get<0>(mAnyFunctor).handle->drawGl(info);
- } else {
- (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
- }
+ mWebViewHandle->drawGl(info);
EGLSyncKHR glDrawFinishedFence =
eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
@@ -190,15 +186,6 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
canvas->restore();
}
-VkInteropFunctorDrawable::~VkInteropFunctorDrawable() {
- if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
- if (lp->listener) {
- ScopedDrawRequest _drawRequest{};
- lp->listener->onGlFunctorReleased(lp->functor);
- }
- }
-}
-
void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const {
ScopedDrawRequest _drawRequest{};
FunctorDrawable::syncFunctor(data);
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
index b12774f32920..e6ea175929c0 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -35,7 +35,7 @@ class VkInteropFunctorDrawable : public FunctorDrawable {
public:
using FunctorDrawable::FunctorDrawable;
- virtual ~VkInteropFunctorDrawable();
+ virtual ~VkInteropFunctorDrawable() {}
static void vkInvokeFunctor(Functor* functor);
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index dc1612c429e3..b57dee4897ac 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -122,9 +122,7 @@ void CacheManager::trimMemory(TrimMemoryMode mode) {
// We must sync the cpu to make sure deletions of resources still queued up on the GPU actually
// happen.
- GrFlushInfo info;
- info.fFlags = kSyncCpu_GrFlushFlag;
- mGrContext->flush(info);
+ mGrContext->flush({});
mGrContext->submit(true);
}
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 32c4173833fb..c7013531c07f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -208,8 +208,12 @@ EGLConfig EglManager::loadFP16Config(EGLDisplay display, SwapBehavior swapBehavi
return config;
}
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
void EglManager::initExtensions() {
auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+ auto extensionsAndroid =
+ StringUtils::split(eglQueryStringImplementationANDROID(mEglDisplay, EGL_EXTENSIONS));
// For our purposes we don't care if EGL_BUFFER_AGE is a result of
// EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered
@@ -228,9 +232,12 @@ void EglManager::initExtensions() {
EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough");
EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
- EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync");
EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync");
EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync");
+
+ // EGL_ANDROID_native_fence_sync is not exposed to applications, so access
+ // this through the private Android-specific query instead.
+ EglExtensions.nativeFenceSync = extensionsAndroid.has("EGL_ANDROID_native_fence_sync");
}
bool EglManager::hasEglContext() {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b66a13d1efda..b764f74bf116 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -128,20 +128,6 @@ void RenderProxy::destroy() {
mRenderThread.queue().runSync([=]() { mContext->destroy(); });
}
-void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
- ATRACE_CALL();
- RenderThread& thread = RenderThread::getInstance();
- auto invoke = [&thread, functor]() { CanvasContext::invokeFunctor(thread, functor); };
- if (waitForCompletion) {
- // waitForCompletion = true is expected to be fairly rare and only
- // happen in destruction. Thus it should be fine to temporarily
- // create a Mutex
- thread.queue().runSync(std::move(invoke));
- } else {
- thread.queue().post(std::move(invoke));
- }
-}
-
void RenderProxy::destroyFunctor(int functor) {
ATRACE_CALL();
RenderThread& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 1b3dc157dc22..16eabadc064c 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -82,7 +82,6 @@ public:
int syncAndDrawFrame();
void destroy();
- static void invokeFunctor(Functor* functor, bool waitForCompletion);
static void destroyFunctor(int functor);
DeferredLayerUpdater* createTextureLayer();
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 91a808df3657..36c5a8c1b3de 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -287,18 +287,6 @@ public:
static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str);
- class MockFunctor : public Functor {
- public:
- virtual status_t operator()(int what, void* data) {
- mLastMode = what;
- return DrawGlInfo::kStatusDone;
- }
- int getLastMode() const { return mLastMode; }
-
- private:
- int mLastMode = -1;
- };
-
static SkColor getColor(const sk_sp<SkSurface>& surface, int x, int y);
static SkRect getClipBounds(const SkCanvas* canvas);
@@ -311,30 +299,32 @@ public:
int glesDraw = 0;
};
- static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); }
+ static void expectOnRenderThread(const std::string_view& function = "unknown") {
+ EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()) << "Called on wrong thread: " << function;
+ }
static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) {
auto callbacks = WebViewFunctorCallbacks{
.onSync =
[](int functor, void* client_data, const WebViewSyncData& data) {
- expectOnRenderThread();
+ expectOnRenderThread("onSync");
sMockFunctorCounts[functor].sync++;
},
.onContextDestroyed =
[](int functor, void* client_data) {
- expectOnRenderThread();
+ expectOnRenderThread("onContextDestroyed");
sMockFunctorCounts[functor].contextDestroyed++;
},
.onDestroyed =
[](int functor, void* client_data) {
- expectOnRenderThread();
+ expectOnRenderThread("onDestroyed");
sMockFunctorCounts[functor].destroyed++;
},
};
switch (mode) {
case RenderMode::OpenGL_ES:
callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params) {
- expectOnRenderThread();
+ expectOnRenderThread("draw");
sMockFunctorCounts[functor].glesDraw++;
};
break;
diff --git a/libs/hwui/tests/scripts/prep_generic.sh b/libs/hwui/tests/scripts/prep_generic.sh
index 223bf373c65a..89826ff69463 100755
--- a/libs/hwui/tests/scripts/prep_generic.sh
+++ b/libs/hwui/tests/scripts/prep_generic.sh
@@ -28,11 +28,17 @@
# performance between different device models.
# Fun notes for maintaining this file:
-# `expr` can deal with ints > INT32_MAX, but if compares cannot. This is why we use MHz.
-# `expr` can sometimes evaluate right-to-left. This is why we use parens.
+# $((arithmetic expressions)) can deal with ints > INT32_MAX, but if compares cannot. This is
+# why we use MHz.
+# $((arithmetic expressions)) can sometimes evaluate right-to-left. This is why we use parens.
# Everything below the initial host-check isn't bash - Android uses mksh
# mksh allows `\n` in an echo, bash doesn't
# can't use `awk`
+# can't use `sed`
+# can't use `cut` on < L
+# can't use `expr` on < L
+
+ARG_CORES=${1:-big}
CPU_TARGET_FREQ_PERCENT=50
GPU_TARGET_FREQ_PERCENT=50
@@ -43,7 +49,7 @@ if [ "`command -v getprop`" == "" ]; then
echo "Pushing $0 and running it on device..."
dest=/data/local/tmp/`basename $0`
adb push $0 ${dest}
- adb shell ${dest}
+ adb shell ${dest} $@
adb shell rm ${dest}
exit
else
@@ -56,7 +62,7 @@ if [ "`command -v getprop`" == "" ]; then
fi
# require root
-if [ "`id -u`" -ne "0" ]; then
+if [[ `id` != "uid=0"* ]]; then
echo "Not running as root, cannot lock clocks, aborting"
exit -1
fi
@@ -64,74 +70,175 @@ fi
DEVICE=`getprop ro.product.device`
MODEL=`getprop ro.product.model`
-# Find CPU max frequency, and lock big cores to an available frequency
-# that's >= $CPU_TARGET_FREQ_PERCENT% of max. Disable other cores.
+if [ "$ARG_CORES" == "big" ]; then
+ CPU_IDEAL_START_FREQ_KHZ=0
+elif [ "$ARG_CORES" == "little" ]; then
+ CPU_IDEAL_START_FREQ_KHZ=100000000 ## finding min of max freqs, so start at 100M KHz (100 GHz)
+else
+ echo "Invalid argument \$1 for ARG_CORES, should be 'big' or 'little', but was $ARG_CORES"
+ exit -1
+fi
+
+function_core_check() {
+ if [ "$ARG_CORES" == "big" ]; then
+ [ $1 -gt $2 ]
+ elif [ "$ARG_CORES" == "little" ]; then
+ [ $1 -lt $2 ]
+ else
+ echo "Invalid argument \$1 for ARG_CORES, should be 'big' or 'little', but was $ARG_CORES"
+ exit -1
+ fi
+}
+
+function_setup_go() {
+ if [ -f /d/fpsgo/common/force_onoff ]; then
+ # Disable fpsgo
+ echo 0 > /d/fpsgo/common/force_onoff
+ fpsgoState=`cat /d/fpsgo/common/force_onoff`
+ if [ "$fpsgoState" != "0" ] && [ "$fpsgoState" != "force off" ]; then
+ echo "Failed to disable fpsgo"
+ exit -1
+ fi
+ fi
+}
+
+# Find the min or max (little vs big) of CPU max frequency, and lock cores of the selected type to
+# an available frequency that's >= $CPU_TARGET_FREQ_PERCENT% of max. Disable other cores.
function_lock_cpu() {
CPU_BASE=/sys/devices/system/cpu
GOV=cpufreq/scaling_governor
+ # Options to make clock locking on go devices more sticky.
+ function_setup_go
+
# Find max CPU freq, and associated list of available freqs
- cpuMaxFreq=0
+ cpuIdealFreq=$CPU_IDEAL_START_FREQ_KHZ
cpuAvailFreqCmpr=0
cpuAvailFreq=0
enableIndices=''
disableIndices=''
cpu=0
- while [ -f ${CPU_BASE}/cpu${cpu}/online ]; do
- # enable core, so we can find its frequencies
- echo 1 > ${CPU_BASE}/cpu${cpu}/online
+ while [ -d ${CPU_BASE}/cpu${cpu}/cpufreq ]; do
+ # Try to enable core, so we can find its frequencies.
+ # Note: In cases where the online file is inaccessible, it represents a
+ # core which cannot be turned off, so we simply assume it is enabled if
+ # this command fails.
+ if [ -f "$CPU_BASE/cpu$cpu/online" ]; then
+ echo 1 > ${CPU_BASE}/cpu${cpu}/online || true
+ fi
+
+ # set userspace governor on all CPUs to ensure freq scaling is disabled
+ echo userspace > ${CPU_BASE}/cpu${cpu}/${GOV}
maxFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/cpuinfo_max_freq`
availFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/scaling_available_frequencies`
availFreqCmpr=${availFreq// /-}
- if [ ${maxFreq} -gt ${cpuMaxFreq} ]; then
- # new highest max freq, look for cpus with same max freq and same avail freq list
- cpuMaxFreq=${maxFreq}
+ if (function_core_check $maxFreq $cpuIdealFreq); then
+ # new min/max of max freq, look for cpus with same max freq and same avail freq list
+ cpuIdealFreq=${maxFreq}
cpuAvailFreq=${availFreq}
cpuAvailFreqCmpr=${availFreqCmpr}
- if [ -z ${disableIndices} ]; then
+ if [ -z "$disableIndices" ]; then
disableIndices="$enableIndices"
else
disableIndices="$disableIndices $enableIndices"
fi
enableIndices=${cpu}
- elif [ ${maxFreq} == ${cpuMaxFreq} ] && [ ${availFreqCmpr} == ${cpuAvailFreqCmpr} ]; then
+ elif [ ${maxFreq} == ${cpuIdealFreq} ] && [ ${availFreqCmpr} == ${cpuAvailFreqCmpr} ]; then
enableIndices="$enableIndices $cpu"
else
- disableIndices="$disableIndices $cpu"
+ if [ -z "$disableIndices" ]; then
+ disableIndices="$cpu"
+ else
+ disableIndices="$disableIndices $cpu"
+ fi
fi
+
cpu=$(($cpu + 1))
done
+ # check that some CPUs will be enabled
+ if [ -z "$enableIndices" ]; then
+ echo "Failed to find any $ARG_CORES cores to enable, aborting."
+ exit -1
+ fi
+
# Chose a frequency to lock to that's >= $CPU_TARGET_FREQ_PERCENT% of max
# (below, 100M = 1K for KHz->MHz * 100 for %)
- TARGET_FREQ_MHZ=`expr \( ${cpuMaxFreq} \* ${CPU_TARGET_FREQ_PERCENT} \) \/ 100000`
+ TARGET_FREQ_MHZ=$(( ($cpuIdealFreq * $CPU_TARGET_FREQ_PERCENT) / 100000 ))
chosenFreq=0
+ chosenFreqDiff=100000000
for freq in ${cpuAvailFreq}; do
- freqMhz=`expr ${freq} \/ 1000`
+ freqMhz=$(( ${freq} / 1000 ))
if [ ${freqMhz} -ge ${TARGET_FREQ_MHZ} ]; then
- chosenFreq=${freq}
- break
+ newChosenFreqDiff=$(( $freq - $TARGET_FREQ_MHZ ))
+ if [ $newChosenFreqDiff -lt $chosenFreqDiff ]; then
+ chosenFreq=${freq}
+ chosenFreqDiff=$(( $chosenFreq - $TARGET_FREQ_MHZ ))
+ fi
fi
done
+ # Lock wembley clocks using high-priority op code method.
+ # This block depends on the shell utility awk, which is only available on API 27+
+ if [ "$DEVICE" == "wembley" ]; then
+ # Get list of available frequencies to lock to by parsing the op-code list.
+ AVAIL_OP_FREQS=`cat /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_oppidx \
+ | awk '{print $2}' \
+ | tail -n +3 \
+ | while read line; do
+ echo "${line:1:${#line}-2}"
+ done`
+
+ # Compute the closest available frequency to the desired frequency, $chosenFreq.
+ # This assumes the op codes listen in /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_oppidx are listed
+ # in order and 0-indexed.
+ opCode=-1
+ opFreq=0
+ currOpCode=-1
+ for currOpFreq in $AVAIL_OP_FREQS; do
+ currOpCode=$((currOpCode + 1))
+
+ prevDiff=$((chosenFreq-opFreq))
+ prevDiff=`function_abs $prevDiff`
+ currDiff=$((chosenFreq-currOpFreq))
+ currDiff=`function_abs $currDiff`
+ if [ $currDiff -lt $prevDiff ]; then
+ opCode="$currOpCode"
+ opFreq="$currOpFreq"
+ fi
+ done
+
+ echo "$opCode" > /proc/ppm/policy/ut_fix_freq_idx
+ fi
+
# enable 'big' CPUs
for cpu in ${enableIndices}; do
freq=${CPU_BASE}/cpu$cpu/cpufreq
- echo 1 > ${CPU_BASE}/cpu${cpu}/online
- echo userspace > ${CPU_BASE}/cpu${cpu}/${GOV}
+ # Try to enable core, so we can find its frequencies.
+ # Note: In cases where the online file is inaccessible, it represents a
+ # core which cannot be turned off, so we simply assume it is enabled if
+ # this command fails.
+ if [ -f "$CPU_BASE/cpu$cpu/online" ]; then
+ echo 1 > ${CPU_BASE}/cpu${cpu}/online || true
+ fi
+
+ # scaling_max_freq must be set before scaling_min_freq
echo ${chosenFreq} > ${freq}/scaling_max_freq
echo ${chosenFreq} > ${freq}/scaling_min_freq
echo ${chosenFreq} > ${freq}/scaling_setspeed
+ # Give system a bit of time to propagate the change to scaling_setspeed.
+ sleep 0.1
+
# validate setting the freq worked
obsCur=`cat ${freq}/scaling_cur_freq`
obsMin=`cat ${freq}/scaling_min_freq`
obsMax=`cat ${freq}/scaling_max_freq`
- if [ obsCur -ne ${chosenFreq} ] || [ obsMin -ne ${chosenFreq} ] || [ obsMax -ne ${chosenFreq} ]; then
+ if [ "$obsCur" -ne "$chosenFreq" ] || [ "$obsMin" -ne "$chosenFreq" ] || [ "$obsMax" -ne "$chosenFreq" ]; then
echo "Failed to set CPU$cpu to $chosenFreq Hz! Aborting..."
echo "scaling_cur_freq = $obsCur"
echo "scaling_min_freq = $obsMin"
@@ -145,8 +252,20 @@ function_lock_cpu() {
echo 0 > ${CPU_BASE}/cpu${cpu}/online
done
- echo "\nLocked CPUs ${enableIndices// /,} to $chosenFreq / $maxFreq KHz"
+ echo "=================================="
+ echo "Locked CPUs ${enableIndices// /,} to $chosenFreq / $cpuIdealFreq KHz"
echo "Disabled CPUs ${disableIndices// /,}"
+ echo "=================================="
+}
+
+# Returns the absolute value of the first arg passed to this helper.
+function_abs() {
+ n=$1
+ if [ $n -lt 0 ]; then
+ echo "$((n * -1 ))"
+ else
+ echo "$n"
+ fi
}
# If we have a Qualcomm GPU, find its max frequency, and lock to
@@ -154,12 +273,12 @@ function_lock_cpu() {
function_lock_gpu_kgsl() {
if [ ! -d /sys/class/kgsl/kgsl-3d0/ ]; then
# not kgsl, abort
- echo "\nCurrently don't support locking GPU clocks of $MODEL ($DEVICE)"
+ echo "Currently don't support locking GPU clocks of $MODEL ($DEVICE)"
return -1
fi
if [ ${DEVICE} == "walleye" ] || [ ${DEVICE} == "taimen" ]; then
# Workaround crash
- echo "\nUnable to lock GPU clocks of $MODEL ($DEVICE)"
+ echo "Unable to lock GPU clocks of $MODEL ($DEVICE)"
return -1
fi
@@ -174,13 +293,13 @@ function_lock_gpu_kgsl() {
done
# (below, 100M = 1M for MHz * 100 for %)
- TARGET_FREQ_MHZ=`expr \( ${gpuMaxFreq} \* ${GPU_TARGET_FREQ_PERCENT} \) \/ 100000000`
+ TARGET_FREQ_MHZ=$(( (${gpuMaxFreq} * ${GPU_TARGET_FREQ_PERCENT}) / 100000000 ))
chosenFreq=${gpuMaxFreq}
index=0
chosenIndex=0
for freq in ${gpuAvailFreq}; do
- freqMhz=`expr ${freq} \/ 1000000`
+ freqMhz=$(( ${freq} / 1000000 ))
if [ ${freqMhz} -ge ${TARGET_FREQ_MHZ} ] && [ ${chosenFreq} -ge ${freq} ]; then
# note avail freq are generally in reverse order, so we don't break out of this loop
chosenFreq=${freq}
@@ -190,7 +309,7 @@ function_lock_gpu_kgsl() {
done
lastIndex=$(($index - 1))
- firstFreq=`echo $gpuAvailFreq | cut -d" " -f1`
+ firstFreq=`function_cut_first_from_space_seperated_list $gpuAvailFreq`
if [ ${gpuMaxFreq} != ${firstFreq} ]; then
# pwrlevel is index of desired freq among available frequencies, from highest to lowest.
@@ -226,24 +345,40 @@ function_lock_gpu_kgsl() {
echo "index = $chosenIndex"
exit -1
fi
- echo "\nLocked GPU to $chosenFreq / $gpuMaxFreq Hz"
+ echo "Locked GPU to $chosenFreq / $gpuMaxFreq Hz"
+}
+
+# cut is not available on some devices (Nexus 5 running LRX22C).
+function_cut_first_from_space_seperated_list() {
+ list=$1
+
+ for freq in $list; do
+ echo $freq
+ break
+ done
}
# kill processes that manage thermals / scaling
-stop thermal-engine
-stop perfd
-stop vendor.thermal-engine
-stop vendor.perfd
+stop thermal-engine || true
+stop perfd || true
+stop vendor.thermal-engine || true
+stop vendor.perfd || true
+setprop vendor.powerhal.init 0 || true
+setprop ctl.interface_restart android.hardware.power@1.0::IPower/default || true
function_lock_cpu
-function_lock_gpu_kgsl
+if [ "$DEVICE" -ne "wembley" ]; then
+ function_lock_gpu_kgsl
+else
+ echo "Unable to lock gpu clocks of $MODEL ($DEVICE)."
+fi
# Memory bus - hardcoded per-device for now
if [ ${DEVICE} == "marlin" ] || [ ${DEVICE} == "sailfish" ]; then
echo 13763 > /sys/class/devfreq/soc:qcom,gpubw/max_freq
else
- echo "\nUnable to lock memory bus of $MODEL ($DEVICE)."
+ echo "Unable to lock memory bus of $MODEL ($DEVICE)."
fi
-echo "\n$DEVICE clocks have been locked - to reset, reboot the device\n" \ No newline at end of file
+echo "$DEVICE clocks have been locked - to reset, reboot the device"
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index 28cff5b9b154..1771c3590e10 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -42,14 +42,3 @@ RENDERTHREAD_TEST(CanvasContext, create) {
canvasContext->destroy();
}
-
-RENDERTHREAD_TEST(CanvasContext, invokeFunctor) {
- TestUtils::MockFunctor functor;
- CanvasContext::invokeFunctor(renderThread, &functor);
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- // we currently don't support OpenGL WebViews on the Vulkan backend
- ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcessNoContext);
- } else {
- ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess);
- }
-}
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 1cd9bd8ee9d9..c19e1ed6ce75 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -231,39 +231,41 @@ TEST(RenderNode, multiTreeValidity) {
}
TEST(RenderNode, releasedCallback) {
- class DecRefOnReleased : public GlFunctorLifecycleListener {
- public:
- explicit DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {}
- void onGlFunctorReleased(Functor* functor) override { *mRefCnt -= 1; }
-
- private:
- int* mRefCnt;
- };
-
- int refcnt = 0;
- sp<DecRefOnReleased> listener(new DecRefOnReleased(&refcnt));
- Functor noopFunctor;
+ int functor = WebViewFunctor_create(
+ nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
auto node = TestUtils::createNode(0, 0, 200, 400, [&](RenderProperties& props, Canvas& canvas) {
- refcnt++;
- canvas.callDrawGLFunction(&noopFunctor, listener.get());
+ canvas.drawWebViewFunctor(functor);
+ });
+ TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
});
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- EXPECT_EQ(1, refcnt);
+ auto& counts = TestUtils::countsForFunctor(functor);
+ EXPECT_EQ(1, counts.sync);
+ EXPECT_EQ(0, counts.destroyed);
TestUtils::recordNode(*node, [&](Canvas& canvas) {
- refcnt++;
- canvas.callDrawGLFunction(&noopFunctor, listener.get());
+ canvas.drawWebViewFunctor(functor);
});
- EXPECT_EQ(2, refcnt);
+ EXPECT_EQ(1, counts.sync);
+ EXPECT_EQ(0, counts.destroyed);
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- EXPECT_EQ(1, refcnt);
+ TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ });
+ EXPECT_EQ(2, counts.sync);
+ EXPECT_EQ(0, counts.destroyed);
+
+ WebViewFunctor_release(functor);
+ EXPECT_EQ(2, counts.sync);
+ EXPECT_EQ(0, counts.destroyed);
TestUtils::recordNode(*node, [](Canvas& canvas) {});
- EXPECT_EQ(1, refcnt);
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- EXPECT_EQ(0, refcnt);
+ TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ });
+ EXPECT_EQ(2, counts.sync);
+ EXPECT_EQ(1, counts.destroyed);
}
RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) {
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index d08aea668b2a..74a565439f85 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -48,7 +48,10 @@ TEST(SkiaDisplayList, reset) {
SkCanvas dummyCanvas;
RenderNodeDrawable drawable(nullptr, &dummyCanvas);
skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
- GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
+ int functor1 = WebViewFunctor_create(
+ nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+ GLFunctorDrawable functorDrawable{functor1, &dummyCanvas};
+ WebViewFunctor_release(functor1);
skiaDL->mChildFunctors.push_back(&functorDrawable);
skiaDL->mMutableImages.push_back(nullptr);
skiaDL->appendVD(nullptr);
@@ -97,16 +100,13 @@ TEST(SkiaDisplayList, syncContexts) {
SkiaDisplayList skiaDL;
SkCanvas dummyCanvas;
- TestUtils::MockFunctor functor;
- GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
- skiaDL.mChildFunctors.push_back(&functorDrawable);
- int functor2 = WebViewFunctor_create(
+ int functor1 = WebViewFunctor_create(
nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
- auto& counts = TestUtils::countsForFunctor(functor2);
+ auto& counts = TestUtils::countsForFunctor(functor1);
skiaDL.mChildFunctors.push_back(
- skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas));
- WebViewFunctor_release(functor2);
+ skiaDL.allocateDrawable<GLFunctorDrawable>(functor1, &dummyCanvas));
+ WebViewFunctor_release(functor1);
SkRect bounds = SkRect::MakeWH(200, 200);
VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
@@ -120,7 +120,6 @@ TEST(SkiaDisplayList, syncContexts) {
});
});
- EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
EXPECT_EQ(counts.sync, 1);
EXPECT_EQ(counts.destroyed, 0);
EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index cc8d83f10d43..62bf39ca8a7a 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -31,7 +31,9 @@ public:
* Check for floats that are close enough to zero.
*/
inline static bool isZero(float value) {
- return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON);
+ // Using fabsf is more performant as ARM computes
+ // fabsf in a single instruction.
+ return fabsf(value) <= NON_ZERO_EPSILON;
}
inline static bool isOne(float value) {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 3b494e9129db..5e480a66c355 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -24,30 +24,9 @@
#include <log/log.h>
-namespace android {
-
-// --- WeakLooperCallback ---
-
-class WeakLooperCallback: public LooperCallback {
-protected:
- virtual ~WeakLooperCallback() { }
-
-public:
- explicit WeakLooperCallback(const wp<LooperCallback>& callback) :
- mCallback(callback) {
- }
+#include <memory>
- virtual int handleEvent(int fd, int events, void* data) {
- sp<LooperCallback> callback = mCallback.promote();
- if (callback != NULL) {
- return callback->handleEvent(fd, events, data);
- }
- return 0; // the client is gone, remove the callback
- }
-
-private:
- wp<LooperCallback> mCallback;
-};
+namespace android {
// --- PointerController ---
@@ -64,29 +43,50 @@ static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
// The number of events to be read at once for DisplayEventReceiver.
static const int EVENT_BUFFER_SIZE = 100;
-// --- PointerController ---
-
-PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
- mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
- mHandler = new WeakMessageHandler(this);
- mCallback = new WeakLooperCallback(this);
-
- if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
- mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
- Looper::EVENT_INPUT, mCallback, nullptr);
+std::shared_ptr<PointerController> PointerController::create(
+ const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController) {
+ std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
+ new PointerController(policy, looper, spriteController));
+
+ /*
+ * Now we need to hook up the constructed PointerController object to its callbacks.
+ *
+ * This must be executed after the constructor but before any other methods on PointerController
+ * in order to ensure that the fully constructed object is visible on the Looper thread, since
+ * that may be a different thread than where the PointerController is initially constructed.
+ *
+ * Unfortunately, this cannot be done as part of the constructor since we need to hand out
+ * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr.
+ */
+
+ controller->mHandler->pointerController = controller;
+ controller->mCallback->pointerController = controller;
+ if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) {
+ controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
+ Looper::EVENT_INPUT, controller->mCallback, nullptr);
} else {
ALOGE("Failed to initialize DisplayEventReceiver.");
}
+ return controller;
+}
+PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController)
+ : mPolicy(policy),
+ mLooper(looper),
+ mSpriteController(spriteController),
+ mHandler(new MessageHandler()),
+ mCallback(new LooperCallback()) {
AutoMutex _l(mLock);
mLocked.animationPending = false;
- mLocked.presentation = PRESENTATION_POINTER;
+ mLocked.presentation = Presentation::POINTER;
mLocked.presentationChanged = false;
- mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
+ mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
mLocked.pointerFadeDirection = 0;
mLocked.pointerX = 0;
@@ -221,7 +221,7 @@ void PointerController::fade(Transition transition) {
removeInactivityTimeoutLocked();
// Start fading.
- if (transition == TRANSITION_IMMEDIATE) {
+ if (transition == Transition::IMMEDIATE) {
mLocked.pointerFadeDirection = 0;
mLocked.pointerAlpha = 0.0f;
updatePointerLocked();
@@ -238,7 +238,7 @@ void PointerController::unfade(Transition transition) {
resetInactivityTimeoutLocked();
// Start unfading.
- if (transition == TRANSITION_IMMEDIATE) {
+ if (transition == Transition::IMMEDIATE) {
mLocked.pointerFadeDirection = 0;
mLocked.pointerAlpha = 1.0f;
updatePointerLocked();
@@ -262,7 +262,7 @@ void PointerController::setPresentation(Presentation presentation) {
return;
}
- if (presentation == PRESENTATION_POINTER) {
+ if (presentation == Presentation::POINTER) {
if (mLocked.additionalMouseResources.empty()) {
mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
&mLocked.animationResources,
@@ -480,24 +480,35 @@ void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
updatePointerLocked();
}
-void PointerController::handleMessage(const Message& message) {
+void PointerController::MessageHandler::handleMessage(const Message& message) {
+ std::shared_ptr<PointerController> controller = pointerController.lock();
+
+ if (controller == nullptr) {
+ ALOGE("PointerController instance was released before processing message: what=%d",
+ message.what);
+ return;
+ }
switch (message.what) {
case MSG_INACTIVITY_TIMEOUT:
- doInactivityTimeout();
+ controller->doInactivityTimeout();
break;
}
}
-int PointerController::handleEvent(int /* fd */, int events, void* /* data */) {
+int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) {
+ std::shared_ptr<PointerController> controller = pointerController.lock();
+ if (controller == nullptr) {
+ ALOGW("PointerController instance was released with pending callbacks. events=0x%x",
+ events);
+ return 0; // Remove the callback, the PointerController is gone anyways
+ }
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
- ALOGE("Display event receiver pipe was closed or an error occurred. "
- "events=0x%x", events);
+ ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events);
return 0; // remove the callback
}
if (!(events & Looper::EVENT_INPUT)) {
- ALOGW("Received spurious callback for unhandled poll event. "
- "events=0x%x", events);
+ ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events);
return 1; // keep the callback
}
@@ -505,7 +516,7 @@ int PointerController::handleEvent(int /* fd */, int events, void* /* data */) {
ssize_t n;
nsecs_t timestamp;
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
- while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
timestamp = buf[i].header.timestamp;
@@ -514,7 +525,7 @@ int PointerController::handleEvent(int /* fd */, int events, void* /* data */) {
}
}
if (gotVsync) {
- doAnimate(timestamp);
+ controller->doAnimate(timestamp);
}
return 1; // keep the callback
}
@@ -613,7 +624,7 @@ bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) {
}
void PointerController::doInactivityTimeout() {
- fade(TRANSITION_GRADUAL);
+ fade(Transition::GRADUAL);
}
void PointerController::startAnimationLocked() {
@@ -627,8 +638,9 @@ void PointerController::startAnimationLocked() {
void PointerController::resetInactivityTimeoutLocked() {
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
- nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
- ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
+ nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT
+ ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT
+ : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
}
@@ -655,7 +667,7 @@ void PointerController::updatePointerLocked() REQUIRES(mLock) {
}
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
- if (mLocked.presentation == PRESENTATION_POINTER) {
+ if (mLocked.presentation == Presentation::POINTER) {
if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) {
mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
} else {
@@ -731,7 +743,7 @@ PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vec
return spot;
}
}
- return NULL;
+ return nullptr;
}
void PointerController::releaseSpotLocked(Spot* spot) {
@@ -772,7 +784,7 @@ void PointerController::loadResourcesLocked() REQUIRES(mLock) {
mLocked.additionalMouseResources.clear();
mLocked.animationResources.clear();
- if (mLocked.presentation == PRESENTATION_POINTER) {
+ if (mLocked.presentation == Presentation::POINTER) {
mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
&mLocked.animationResources, mLocked.viewport.displayId);
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index ebc622bae302..14c0679654c6 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -17,19 +17,20 @@
#ifndef _UI_POINTER_CONTROLLER_H
#define _UI_POINTER_CONTROLLER_H
-#include "SpriteController.h"
-
-#include <map>
-#include <vector>
-
-#include <ui/DisplayInfo.h>
+#include <PointerControllerInterface.h>
+#include <gui/DisplayEventReceiver.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
-#include <PointerControllerInterface.h>
+#include <ui/DisplayInfo.h>
#include <utils/BitSet.h>
-#include <utils/RefBase.h>
#include <utils/Looper.h>
-#include <gui/DisplayEventReceiver.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "SpriteController.h"
namespace android {
@@ -70,25 +71,22 @@ public:
virtual int32_t getCustomPointerIconId() = 0;
};
-
/*
* Tracks pointer movements and draws the pointer sprite to a surface.
*
* Handles pointer acceleration and animation.
*/
-class PointerController : public PointerControllerInterface, public MessageHandler,
- public LooperCallback {
-protected:
- virtual ~PointerController();
-
+class PointerController : public PointerControllerInterface {
public:
- enum InactivityTimeout {
- INACTIVITY_TIMEOUT_NORMAL = 0,
- INACTIVITY_TIMEOUT_SHORT = 1,
+ static std::shared_ptr<PointerController> create(
+ const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController);
+ enum class InactivityTimeout {
+ NORMAL = 0,
+ SHORT = 1,
};
- PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, const sp<SpriteController>& spriteController);
+ virtual ~PointerController();
virtual bool getBounds(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const;
@@ -113,8 +111,8 @@ public:
void reloadPointerResources();
private:
- static const size_t MAX_RECYCLED_SPRITES = 12;
- static const size_t MAX_SPOTS = 12;
+ static constexpr size_t MAX_RECYCLED_SPRITES = 12;
+ static constexpr size_t MAX_SPOTS = 12;
enum {
MSG_INACTIVITY_TIMEOUT,
@@ -130,8 +128,13 @@ private:
float x, y;
inline Spot(uint32_t id, const sp<Sprite>& sprite)
- : id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
- x(0.0f), y(0.0f), lastIcon(NULL) { }
+ : id(id),
+ sprite(sprite),
+ alpha(1.0f),
+ scale(1.0f),
+ x(0.0f),
+ y(0.0f),
+ lastIcon(nullptr) {}
void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
@@ -139,12 +142,24 @@ private:
const SpriteIcon* lastIcon;
};
+ class MessageHandler : public virtual android::MessageHandler {
+ public:
+ void handleMessage(const Message& message) override;
+ std::weak_ptr<PointerController> pointerController;
+ };
+
+ class LooperCallback : public virtual android::LooperCallback {
+ public:
+ int handleEvent(int fd, int events, void* data) override;
+ std::weak_ptr<PointerController> pointerController;
+ };
+
mutable Mutex mLock;
sp<PointerControllerPolicyInterface> mPolicy;
sp<Looper> mLooper;
sp<SpriteController> mSpriteController;
- sp<WeakMessageHandler> mHandler;
+ sp<MessageHandler> mHandler;
sp<LooperCallback> mCallback;
DisplayEventReceiver mDisplayEventReceiver;
@@ -181,14 +196,15 @@ private:
int32_t buttonState;
std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
- std::vector<sp<Sprite> > recycledSprites;
+ std::vector<sp<Sprite>> recycledSprites;
} mLocked GUARDED_BY(mLock);
+ PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController);
+
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
- void handleMessage(const Message& message);
- int handleEvent(int fd, int events, void* data);
void doAnimate(nsecs_t timestamp);
bool doFadingAnimationLocked(nsecs_t timestamp);
bool doBitmapAnimationLocked(nsecs_t timestamp);
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index a15742671dc7..6e129a064385 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -136,7 +136,7 @@ protected:
sp<MockSprite> mPointerSprite;
sp<MockPointerControllerPolicyInterface> mPolicy;
sp<MockSpriteController> mSpriteController;
- sp<PointerController> mPointerController;
+ std::shared_ptr<PointerController> mPointerController;
private:
void loopThread();
@@ -160,7 +160,7 @@ PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<Moc
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
- mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+ mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
}
PointerControllerTest::~PointerControllerTest() {
@@ -193,7 +193,7 @@ void PointerControllerTest::loopThread() {
TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
ensureDisplayViewportIsSet();
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
EXPECT_CALL(*mPointerSprite, setVisible(true));
@@ -208,7 +208,7 @@ TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
TEST_F(PointerControllerTest, updatePointerIcon) {
ensureDisplayViewportIsSet();
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
int32_t type = CURSOR_TYPE_ADDITIONAL;
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
@@ -224,7 +224,7 @@ TEST_F(PointerControllerTest, updatePointerIcon) {
TEST_F(PointerControllerTest, setCustomPointerIcon) {
ensureDisplayViewportIsSet();
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
int32_t style = CURSOR_TYPE_CUSTOM;
float hotSpotX = 15;
@@ -246,13 +246,13 @@ TEST_F(PointerControllerTest, setCustomPointerIcon) {
}
TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
- mPointerController->setPresentation(PointerController::PRESENTATION_POINTER);
+ mPointerController->setPresentation(PointerController::Presentation::POINTER);
mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
mPointerController->clearSpots();
mPointerController->setPosition(1.0f, 1.0f);
mPointerController->move(1.0f, 1.0f);
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
- mPointerController->fade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
+ mPointerController->fade(PointerController::Transition::IMMEDIATE);
EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 496885cd1f37..997339eb2a80 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -151,6 +151,16 @@ public final class GpsStatus {
return status;
}
+ /**
+ * Builds an empty GpsStatus. Should only be used for legacy reasons.
+ *
+ * @hide
+ */
+ @NonNull
+ static GpsStatus createEmpty() {
+ return new GpsStatus();
+ }
+
private GpsStatus() {
}
diff --git a/location/java/android/location/ILocationCallback.aidl b/location/java/android/location/ILocationCallback.aidl
new file mode 100644
index 000000000000..deb390f72c99
--- /dev/null
+++ b/location/java/android/location/ILocationCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.location.Location;
+
+/**
+ * Listener for single locations.
+ * {@hide}
+ */
+oneway interface ILocationCallback
+{
+ void onLocation(in @nullable Location location);
+}
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 8479caf367b0..29b483af8721 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -1,36 +1,29 @@
-/* //device/java/android/android/location/ILocationListener.aidl
-**
-** Copyright 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.
-*/
+/*
+ * 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 android.location;
import android.location.Location;
-import android.os.Bundle;
+import android.os.IRemoteCallback;
/**
* {@hide}
*/
oneway interface ILocationListener
{
- @UnsupportedAppUsage
- void onLocationChanged(in Location location);
- @UnsupportedAppUsage
- void onProviderEnabled(String provider);
- @UnsupportedAppUsage
- void onProviderDisabled(String provider);
- // called when the listener is removed from the server side; no further callbacks are expected
- void onRemoved();
+ void onLocationChanged(in Location location, in @nullable IRemoteCallback onCompleteCallback);
+ void onProviderEnabledChanged(String provider, boolean enabled);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1f7b69f8840e..0e7eaa21888e 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -29,6 +29,7 @@ import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssStatusListener;
import android.location.IGnssNavigationMessageListener;
+import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.Location;
import android.location.LocationRequest;
@@ -46,9 +47,7 @@ import com.android.internal.location.ProviderProperties;
interface ILocationManager
{
Location getLastLocation(in LocationRequest request, String packageName, String attributionTag);
- boolean getCurrentLocation(in LocationRequest request,
- in ICancellationSignal cancellationSignal, in ILocationListener listener,
- String packageName, String attributionTag, String listenerId);
+ void getCurrentLocation(in LocationRequest request, in ICancellationSignal cancellationSignal, in ILocationCallback callback, String packageName, String attributionTag, String listenerId);
void registerLocationListener(in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId);
void unregisterLocationListener(in ILocationListener listener);
@@ -114,12 +113,7 @@ interface ILocationManager
List<LocationRequest> getTestProviderCurrentRequests(String provider);
LocationTime getGnssTimeMillis();
- boolean sendExtraCommand(String provider, String command, inout Bundle extras);
-
- // --- internal ---
-
- // for reporting callback completion
- void locationCallbackFinished(ILocationListener listener);
+ void sendExtraCommand(String provider, String command, inout Bundle extras);
// used by gts tests to verify whitelists
String[] getBackgroundThrottlingWhitelist();
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 8df08345c79b..2738ff4ff38c 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -36,7 +36,9 @@ import android.os.Bundle;
public interface LocationListener {
/**
- * Called when the location has changed.
+ * Called when the location has changed. A wakelock is held on behalf on the listener for some
+ * brief amount of time as this callback executes. If this callback performs long running
+ * operations, it is the client's responsibility to obtain their own wakelock.
*
* @param location the updated location
*/
@@ -52,18 +54,17 @@ public interface LocationListener {
default void onStatusChanged(String provider, int status, Bundle extras) {}
/**
- * Called when the provider is enabled by the user.
+ * Called when a provider this listener is registered with becomes enabled.
*
- * @param provider the name of the location provider that has become enabled
+ * @param provider the name of the location provider
*/
default void onProviderEnabled(@NonNull String provider) {}
/**
- * Called when the provider is disabled by the user. If requestLocationUpdates
- * is called on an already disabled provider, this method is called
- * immediately.
+ * Called when the provider this listener is registered with becomes disabled. If a provider is
+ * disabled when this listener is registered, this callback will be invoked immediately.
*
- * @param provider the name of the location provider that has become disabled
+ * @param provider the name of the location provider
*/
default void onProviderDisabled(@NonNull String provider) {}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 401a5a137327..1803027743f6 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -20,10 +20,8 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.LOCATION_HARDWARE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.app.AlarmManager.ELAPSED_REALTIME;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
import android.Manifest;
import android.annotation.CallbackExecutor;
@@ -34,7 +32,6 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.PropertyInvalidatedCache;
@@ -44,31 +41,30 @@ import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.location.util.LocationListenerTransportManager;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor;
import com.android.internal.listeners.ListenerTransportMultiplexer;
import com.android.internal.location.ProviderProperties;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledRunnable;
+import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
+import java.util.WeakHashMap;
import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
import java.util.function.Consumer;
/**
@@ -264,7 +260,9 @@ public class LocationManager {
* {@code OP_MONITOR_HIGH_POWER_LOCATION}.
*
* @hide
+ * @deprecated This action is unnecessary from Android S forward.
*/
+ @Deprecated
public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
"android.location.HIGH_POWER_REQUEST_CHANGE";
@@ -305,14 +303,16 @@ public class LocationManager {
public static final String METADATA_SETTINGS_FOOTER_STRING =
"com.android.settings.location.FOOTER_STRING";
- private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
+ private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
- @GuardedBy("mProviderLocationListeners")
- private static final ArrayMap<String, LocationListenerTransportManager>
- sProviderLocationListeners = new ArrayMap<>();
+ @GuardedBy("sLocationListeners")
+ private static final WeakHashMap<LocationListener, WeakReference<LocationListenerTransport>>
+ sLocationListeners = new WeakHashMap<>();
final Context mContext;
- @UnsupportedAppUsage
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+ + "LocationManager}")
final ILocationManager mService;
private final Object mLock = new Object();
@@ -422,8 +422,7 @@ public class LocationManager {
try {
return mService.getExtraLocationControllerPackage();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -438,7 +437,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackage(packageName);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -453,7 +452,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -467,8 +466,7 @@ public class LocationManager {
try {
return mService.isExtraLocationControllerPackageEnabled();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return false;
+ throw e.rethrowFromSystemServer();
}
}
@@ -486,7 +484,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackage(packageName);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -504,7 +502,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -643,12 +641,7 @@ public class LocationManager {
@Nullable
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public Location getLastLocation() {
- try {
- return mService.getLastLocation(null, mContext.getPackageName(),
- mContext.getAttributionTag());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getLastKnownLocation(FUSED_PROVIDER);
}
/**
@@ -745,33 +738,19 @@ public class LocationManager {
public void getCurrentLocation(@NonNull LocationRequest locationRequest,
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
- LocationRequest currentLocationRequest = new LocationRequest(locationRequest)
- .setNumUpdates(1);
- if (currentLocationRequest.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
- currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
- }
-
- GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor,
- consumer);
+ ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport();
+ GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor, consumer,
+ remoteCancellationSignal);
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
+ cancellationSignal.setOnCancelListener(transport::cancel);
}
- ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport();
-
try {
- if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
+ mService.getCurrentLocation(locationRequest, remoteCancellationSignal,
transport, mContext.getPackageName(), mContext.getAttributionTag(),
- transport.getListenerId())) {
- transport.register(mContext.getSystemService(AlarmManager.class),
- remoteCancellationSignal);
- if (cancellationSignal != null) {
- cancellationSignal.setOnCancelListener(transport::cancel);
- }
- } else {
- transport.fail();
- }
+ AppOpsManager.toReceiverId(consumer));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -803,7 +782,7 @@ public class LocationManager {
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
- request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
requestLocationUpdates(request, listener, looper);
}
@@ -835,7 +814,7 @@ public class LocationManager {
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
- request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
requestLocationUpdates(request, listener, looper);
}
@@ -862,7 +841,7 @@ public class LocationManager {
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
- request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
requestLocationUpdates(request, pendingIntent);
}
@@ -890,7 +869,7 @@ public class LocationManager {
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
- request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
requestLocationUpdates(request, pendingIntent);
}
@@ -1200,18 +1179,28 @@ public class LocationManager {
locationRequest = new LocationRequest();
}
- String provider = locationRequest.getProvider();
+ synchronized (sLocationListeners) {
+ WeakReference<LocationListenerTransport> reference = sLocationListeners.get(listener);
+ LocationListenerTransport transport = reference != null ? reference.get() : null;
+ if (transport == null) {
+ transport = new LocationListenerTransport(listener, executor);
+ sLocationListeners.put(listener, new WeakReference<>(transport));
+ } else {
+ transport.setExecutor(executor);
+ }
- LocationListenerTransportManager manager;
- synchronized (sProviderLocationListeners) {
- manager = sProviderLocationListeners.get(provider);
- if (manager == null) {
- manager = new LocationListenerTransportManager(mService);
- sProviderLocationListeners.put(provider, manager);
+ try {
+ // making the service call while under lock is less than ideal since LMS must
+ // make sure that callbacks are not made on the same thread - however it is the
+ // easiest way to guarantee that clients will not receive callbacks after
+ // unregistration is complete.
+ mService.registerLocationListener(locationRequest, transport,
+ mContext.getPackageName(), mContext.getAttributionTag(),
+ AppOpsManager.toReceiverId(listener));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
-
- manager.addListener(mContext, locationRequest, executor, listener);
}
/**
@@ -1236,13 +1225,16 @@ public class LocationManager {
public void requestLocationUpdates(
@Nullable LocationRequest locationRequest,
@NonNull PendingIntent pendingIntent) {
- Preconditions.checkArgument(locationRequest != null, "invalid null location request");
Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
"pending intent must be targeted to a package");
}
+ if (locationRequest == null) {
+ locationRequest = new LocationRequest();
+ }
+
try {
mService.registerLocationPendingIntent(locationRequest, pendingIntent,
mContext.getPackageName(), mContext.getAttributionTag());
@@ -1294,25 +1286,24 @@ public class LocationManager {
public void removeUpdates(@NonNull LocationListener listener) {
Preconditions.checkArgument(listener != null, "invalid null listener");
- RuntimeException exception = null;
- synchronized (sProviderLocationListeners) {
- for (int i = 0; i < sProviderLocationListeners.size(); i++) {
- LocationListenerTransportManager manager = sProviderLocationListeners.valueAt(i);
+ synchronized (sLocationListeners) {
+ WeakReference<LocationListenerTransport> reference = sLocationListeners.remove(
+ listener);
+ LocationListenerTransport transport = reference != null ? reference.get() : null;
+ if (transport != null) {
+ transport.unregister();
+
try {
- manager.removeListener(listener);
- } catch (RuntimeException e) {
- if (exception == null) {
- exception = e;
- } else {
- exception.addSuppressed(e);
- }
+ // making the service call while under lock is less than ideal since LMS must
+ // make sure that callbacks are not made on the same thread - however it is the
+ // easiest way to guarantee that clients will not receive callbacks after
+ // unregistration is complete.
+ mService.unregisterLocationListener(transport);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
-
- if (exception != null) {
- throw exception;
- }
}
/**
@@ -1525,7 +1516,8 @@ public class LocationManager {
Preconditions.checkArgument(command != null, "invalid null command");
try {
- return mService.sendExtraCommand(provider, command, extras);
+ mService.sendExtraCommand(provider, command, extras);
+ return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1843,6 +1835,10 @@ public class LocationManager {
} else {
status.setStatus(gnssStatus, ttff);
}
+ } else if (status == null) {
+ // even though this method is marked as nullable, legacy behavior was to never return
+ // a null result, and there are applications that rely on this behavior.
+ status = GpsStatus.createEmpty();
}
return status;
}
@@ -1865,7 +1861,8 @@ public class LocationManager {
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
}
- getGnssStatusTransportMultiplexer().addListener(listener, DIRECT_EXECUTOR);
+ getGnssStatusTransportMultiplexer().addListener(listener,
+ new HandlerExecutor(new Handler()));
return true;
}
@@ -1984,7 +1981,7 @@ public class LocationManager {
@Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean addNmeaListener(@NonNull OnNmeaMessageListener listener) {
- return addNmeaListener(DIRECT_EXECUTOR, listener);
+ return addNmeaListener(listener, null);
}
/**
@@ -2397,12 +2394,10 @@ public class LocationManager {
}
}
- private static class GetCurrentLocationTransport extends ILocationListener.Stub implements
- AlarmManager.OnAlarmListener {
+ private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements
+ ListenerExecutor {
- @GuardedBy("this")
- @Nullable
- private Executor mExecutor;
+ private final Executor mExecutor;
@GuardedBy("this")
@Nullable
@@ -2410,61 +2405,22 @@ public class LocationManager {
@GuardedBy("this")
@Nullable
- private AlarmManager mAlarmManager;
-
- @GuardedBy("this")
- @Nullable
private ICancellationSignal mRemoteCancellationSignal;
- GetCurrentLocationTransport(Executor executor, Consumer<Location> consumer) {
+ GetCurrentLocationTransport(Executor executor, Consumer<Location> consumer,
+ ICancellationSignal remoteCancellationSignal) {
Preconditions.checkArgument(executor != null, "illegal null executor");
Preconditions.checkArgument(consumer != null, "illegal null consumer");
mExecutor = executor;
mConsumer = consumer;
- mAlarmManager = null;
- mRemoteCancellationSignal = null;
- }
-
- public String getListenerId() {
- return AppOpsManager.toReceiverId(mConsumer);
- }
-
- public synchronized void register(AlarmManager alarmManager,
- ICancellationSignal remoteCancellationSignal) {
- if (mConsumer == null) {
- return;
- }
-
- mAlarmManager = alarmManager;
- mAlarmManager.set(
- ELAPSED_REALTIME,
- SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_MAX_TIMEOUT_MS,
- "GetCurrentLocation",
- this,
- null);
-
mRemoteCancellationSignal = remoteCancellationSignal;
}
public void cancel() {
- remove();
- }
-
- private Consumer<Location> remove() {
- Consumer<Location> consumer;
ICancellationSignal cancellationSignal;
synchronized (this) {
- mExecutor = null;
- consumer = mConsumer;
- mConsumer = null;
-
- if (mAlarmManager != null) {
- mAlarmManager.cancel(this);
- mAlarmManager = null;
- }
-
- // ensure only one cancel event will go through
cancellationSignal = mRemoteCancellationSignal;
+ mConsumer = null;
mRemoteCancellationSignal = null;
}
@@ -2472,73 +2428,76 @@ public class LocationManager {
try {
cancellationSignal.cancel();
} catch (RemoteException e) {
- // ignore
+ throw e.rethrowFromSystemServer();
}
}
-
- return consumer;
- }
-
- public void fail() {
- deliverResult(null);
}
@Override
- public void onAlarm() {
+ public void onLocation(@Nullable Location location) {
+ Consumer<Location> consumer;
synchronized (this) {
- // save ourselves a pointless x-process call to cancel the alarm
- mAlarmManager = null;
+ consumer = mConsumer;
+ mConsumer = null;
+ mRemoteCancellationSignal = null;
}
- deliverResult(null);
+ executeSafely(mExecutor, () -> consumer, listener -> listener.accept(location));
}
+ }
- @Override
- public void onLocationChanged(Location location) {
- synchronized (this) {
- // save ourselves a pointless x-process call to cancel the location request
- mRemoteCancellationSignal = null;
- }
+ private static class LocationListenerTransport extends ILocationListener.Stub implements
+ ListenerExecutor {
- deliverResult(location);
- }
+ private Executor mExecutor;
+ @Nullable private volatile LocationListener mListener;
- @Override
- public void onProviderEnabled(String provider) {}
+ LocationListenerTransport(LocationListener listener, Executor executor) {
+ Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+ mListener = listener;
+ setExecutor(executor);
+ }
- @Override
- public void onProviderDisabled(String provider) {
- // in the event of the provider being disabled it is unlikely that we will get further
- // locations, so fail early so the client isn't left waiting hopelessly
- deliverResult(null);
+ void setExecutor(Executor executor) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ mExecutor = executor;
}
- @Override
- public void onRemoved() {
- deliverResult(null);
+ void unregister() {
+ mListener = null;
}
- private synchronized void deliverResult(@Nullable Location location) {
- if (mExecutor == null) {
- return;
- }
+ @Override
+ public void onLocationChanged(Location location,
+ @Nullable IRemoteCallback onCompleteCallback) {
+ executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
+ @Override
+ public void operate(LocationListener listener) {
+ listener.onLocationChanged(location);
+ }
- PooledRunnable runnable =
- obtainRunnable(GetCurrentLocationTransport::acceptResult, this, location)
- .recycleOnUse();
- try {
- mExecutor.execute(runnable);
- } catch (RejectedExecutionException e) {
- runnable.recycle();
- throw e;
- }
+ @Override
+ public void onComplete(boolean success) {
+ if (onCompleteCallback != null) {
+ try {
+ onCompleteCallback.sendResult(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ });
}
- private void acceptResult(Location location) {
- Consumer<Location> consumer = remove();
- if (consumer != null) {
- consumer.accept(location);
- }
+ @Override
+ public void onProviderEnabledChanged(String provider, boolean enabled) {
+ executeSafely(mExecutor, () -> mListener, listener -> {
+ if (enabled) {
+ listener.onProviderEnabled(provider);
+ } else {
+ listener.onProviderDisabled(provider);
+ }
+ });
}
}
diff --git a/location/java/android/location/package.html b/location/java/android/location/package.html
index 2355e725b6c5..20c5c54d6921 100644
--- a/location/java/android/location/package.html
+++ b/location/java/android/location/package.html
@@ -6,7 +6,7 @@
<p class="warning">
<strong>This API is not the recommended method for accessing Android location.</strong><br>
The
-<a href="{@docRoot}reference/com/google/android/gms/location/package-summary.html">Google Location Services API</a>,
+<a href="https://developers.google.com/android/reference/com/google/android/gms/location/package-summary">Google Location Services API</a>,
part of Google Play services, is the preferred way to add location-awareness to
your app. It offers a simpler API, higher accuracy, low-power geofencing, and
more. If you are currently using the android.location API, you are strongly
diff --git a/location/java/android/location/util/LocationListenerTransportManager.java b/location/java/android/location/util/LocationListenerTransportManager.java
deleted file mode 100644
index f16f7e148830..000000000000
--- a/location/java/android/location/util/LocationListenerTransportManager.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.location.util;
-
-import android.annotation.Nullable;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.location.ILocationListener;
-import android.location.ILocationManager;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationRequest;
-import android.os.RemoteException;
-
-import com.android.internal.listeners.ListenerTransportManager;
-import com.android.internal.listeners.RequestListenerTransport;
-
-import java.util.concurrent.Executor;
-
-/**
- * Utility class for managing location listeners.
- *
- * @hide
- */
-public class LocationListenerTransportManager extends
- ListenerTransportManager<LocationListenerTransportManager.LocationListenerTransport> {
-
- protected class LocationListenerTransport extends
- RequestListenerTransport<LocationRequest, LocationListener> {
-
- private final ILocationListener mBinderTransport;
-
- private final String mPackageName;
- @Nullable private final String mAttributionTag;
- private final String mListenerId;
-
- LocationListenerTransport(Context context, LocationRequest locationRequest,
- Executor executor, LocationListener locationListener) {
- super(locationRequest, executor, locationListener);
-
- mBinderTransport = new LocationListenerBinder(this);
-
- mPackageName = context.getPackageName();
- mAttributionTag = context.getAttributionTag();
- mListenerId = AppOpsManager.toReceiverId(locationListener);
- }
-
- ILocationListener getTransport() {
- return mBinderTransport;
- }
-
- String getPackageName() {
- return mPackageName;
- }
-
- String getAttributionTag() {
- return mAttributionTag;
- }
-
- String getListenerId() {
- return mListenerId;
- }
-
- public void onLocationChanged(Location location) {
- execute(listener -> {
- listener.onLocationChanged(location);
- try {
- mService.locationCallbackFinished(mBinderTransport);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- });
- }
-
- public void onProviderEnabled(String provider) {
- execute(listener -> {
- listener.onProviderEnabled(provider);
- try {
- mService.locationCallbackFinished(mBinderTransport);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- });
- }
-
- public void onProviderDisabled(String provider) {
- execute(listener -> {
- listener.onProviderDisabled(provider);
- try {
- mService.locationCallbackFinished(mBinderTransport);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- });
- }
-
- public void onRemoved() {
- // must be executed on the same executor so callbacks cannot be reordered
- execute(listener -> removeTransport(listener, this));
- }
- }
-
- final ILocationManager mService;
-
- public LocationListenerTransportManager(ILocationManager service) {
- mService = service;
- }
-
- /** Adds the given listener. */
- public void addListener(Context context, LocationRequest locationRequest, Executor executor,
- LocationListener listener) {
- registerListener(listener,
- new LocationListenerTransport(context, locationRequest, executor, listener));
- }
-
- /** Removes the given listener. */
- public void removeListener(LocationListener listener) {
- unregisterListener(listener);
- }
-
- @Override
- protected void registerWithServer(LocationListenerTransport transport) throws RemoteException {
- mService.registerLocationListener(transport.getRequest(), transport.getTransport(),
- transport.getPackageName(), transport.getAttributionTag(),
- transport.getListenerId());
- }
-
- @Override
- protected void unregisterWithServer(LocationListenerTransport transport)
- throws RemoteException {
- mService.unregisterLocationListener(transport.getTransport());
- }
-
- private static class LocationListenerBinder extends ILocationListener.Stub {
-
- private final LocationListenerTransport mListener;
-
- LocationListenerBinder(LocationListenerTransport listener) {
- mListener = listener;
- }
-
- @Override
- public void onLocationChanged(Location location) {
- mListener.onLocationChanged(location);
- }
-
- @Override
- public void onProviderEnabled(String provider) {
- mListener.onProviderEnabled(provider);
- }
-
- @Override
- public void onProviderDisabled(String provider) {
- mListener.onProviderDisabled(provider);
- }
-
- @Override
- public void onRemoved() {
- mListener.onRemoved();
- }
- }
-}
diff --git a/location/java/com/android/internal/location/ILocationProviderManager.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl
index 4036d63905ea..2a6cef812db1 100644
--- a/location/java/com/android/internal/location/ILocationProviderManager.aidl
+++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl
@@ -25,15 +25,8 @@ import com.android.internal.location.ProviderProperties;
* @hide
*/
interface ILocationProviderManager {
-
- void onSetAttributionTag(String attributionTag);
-
- @UnsupportedAppUsage
+ void onSetIdentity(@nullable String packageName, @nullable String attributionTag);
void onSetAllowed(boolean allowed);
-
- @UnsupportedAppUsage
void onSetProperties(in ProviderProperties properties);
-
- @UnsupportedAppUsage
void onReportLocation(in Location location);
}
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 403a7667c150..0822cda04417 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -611,24 +611,18 @@ public class GnssMetrics {
if (atomTag != FrameworkStatsLog.GNSS_STATS) {
throw new UnsupportedOperationException("Unknown tagId = " + atomTag);
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(mLocationFailureReportsStatistics.getCount())
- .writeLong(mLocationFailureReportsStatistics.getLongSum())
- .writeLong(mTimeToFirstFixMilliSReportsStatistics.getCount())
- .writeLong(mTimeToFirstFixMilliSReportsStatistics.getLongSum())
- .writeLong(mPositionAccuracyMetersReportsStatistics.getCount())
- .writeLong(mPositionAccuracyMetersReportsStatistics.getLongSum())
- .writeLong(mTopFourAverageCn0DbmHzReportsStatistics.getCount())
- .writeLong(mTopFourAverageCn0DbmHzReportsStatistics.getLongSum())
- .writeLong(mL5TopFourAverageCn0DbmHzReportsStatistics.getCount())
- .writeLong(mL5TopFourAverageCn0DbmHzReportsStatistics.getLongSum())
- .writeLong(mSvStatusReports)
- .writeLong(mSvStatusReportsUsedInFix)
- .writeLong(mL5SvStatusReports)
- .writeLong(mL5SvStatusReportsUsedInFix)
- .build();
- data.add(e);
+ data.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ mLocationFailureReportsStatistics.getCount(),
+ mLocationFailureReportsStatistics.getLongSum(),
+ mTimeToFirstFixMilliSReportsStatistics.getCount(),
+ mTimeToFirstFixMilliSReportsStatistics.getLongSum(),
+ mPositionAccuracyMetersReportsStatistics.getCount(),
+ mPositionAccuracyMetersReportsStatistics.getLongSum(),
+ mTopFourAverageCn0DbmHzReportsStatistics.getCount(),
+ mTopFourAverageCn0DbmHzReportsStatistics.getLongSum(),
+ mL5TopFourAverageCn0DbmHzReportsStatistics.getCount(),
+ mL5TopFourAverageCn0DbmHzReportsStatistics.getLongSum(), mSvStatusReports,
+ mSvStatusReportsUsedInFix, mL5SvStatusReports, mL5SvStatusReportsUsedInFix));
return StatsManager.PULL_SUCCESS;
}
}
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 25b4090187a4..7a3a4b23d532 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -79,7 +79,8 @@ public abstract class LocationProviderBase {
public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
final String mTag;
- final String mAttributionTag;
+ @Nullable final String mPackageName;
+ @Nullable final String mAttributionTag;
final IBinder mBinder;
/**
@@ -93,8 +94,7 @@ public abstract class LocationProviderBase {
protected final ILocationManager mLocationManager;
// write locked on mBinder, read lock is optional depending on atomicity requirements
- @Nullable
- volatile ILocationProviderManager mManager;
+ @Nullable volatile ILocationProviderManager mManager;
volatile ProviderProperties mProperties;
volatile boolean mAllowed;
@@ -116,6 +116,7 @@ public abstract class LocationProviderBase {
public LocationProviderBase(Context context, String tag,
ProviderPropertiesUnbundled properties) {
mTag = tag;
+ mPackageName = context != null ? context.getPackageName() : null;
mAttributionTag = context != null ? context.getAttributionTag() : null;
mBinder = new Service();
@@ -332,8 +333,8 @@ public abstract class LocationProviderBase {
public void setLocationProviderManager(ILocationProviderManager manager) {
synchronized (mBinder) {
try {
- if (mAttributionTag != null) {
- manager.onSetAttributionTag(mAttributionTag);
+ if (mPackageName != null || mAttributionTag != null) {
+ manager.onSetIdentity(mPackageName, mAttributionTag);
}
manager.onSetProperties(mProperties);
manager.onSetAllowed(mAllowed);
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index fe0c2d21d442..158482a6a833 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -452,6 +452,10 @@ public final class AudioAttributes implements Parcelable {
| FLAG_NO_SYSTEM_CAPTURE | FLAG_CAPTURE_PRIVATE;
private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED |
FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY;
+ /* mask of flags that can be set by SDK and System APIs through the Builder */
+ private static final int FLAG_ALL_API_SET = FLAG_ALL_PUBLIC
+ | FLAG_BYPASS_INTERRUPTION_POLICY
+ | FLAG_BYPASS_MUTE;
/**
* Indicates that the audio may be captured by any app.
@@ -838,7 +842,7 @@ public final class AudioAttributes implements Parcelable {
* @return the same Builder instance.
*/
public Builder setFlags(int flags) {
- flags &= AudioAttributes.FLAG_ALL_PUBLIC;
+ flags &= AudioAttributes.FLAG_ALL_API_SET;
mFlags |= flags;
return this;
}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 00a4c7e19f34..6e3fb1991acc 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -18,7 +18,6 @@ package android.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.util.SparseIntArray;
import java.lang.annotation.Retention;
@@ -137,13 +136,15 @@ public final class AudioDeviceInfo {
*/
public static final int TYPE_BUILTIN_SPEAKER_SAFE = 24;
/**
- * @hide
* A device type for rerouting audio within the Android framework between mixes and
- * system applications. Typically created when using
- * {@link android.media.audiopolicy.AudioPolicy} for mixes created with the
- * {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_RENDER} flag.
- */
- @SystemApi
+ * system applications.
+ * This type is for instance encountered when querying the output device of a track
+ * (with {@link AudioTrack#getRoutedDevice()} playing from a device in screen mirroring mode,
+ * where the audio is not heard on the device, but on the remote device.
+ */
+ // Typically created when using
+ // {@link android.media.audiopolicy.AudioPolicy} for mixes created with the
+ // {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_LOOP_BACK} flag.
public static final int TYPE_REMOTE_SUBMIX = 25;
/** @hide */
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 6d690f0aa397..4a6724a09c1e 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1411,9 +1411,9 @@ public class ExifInterface {
private static final int IMAGE_TYPE_WEBP = 14;
static {
- sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+ sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.US);
sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
- sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX");
+ sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX", Locale.US);
sFormatterTz.setTimeZone(TimeZone.getTimeZone("UTC"));
// Build up the hash tables to look up Exif tags for reading Exif tags.
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index ba87f2bbffb5..87c3bb9ffabe 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -200,6 +200,20 @@ public class ImageReader implements AutoCloseable {
* </table>
* Using other combinations may result in {@link IllegalArgumentException}.
* </p>
+ * <p>
+ * If the {@link ImageReader} is used as an output target for a {@link
+ * android.hardware.camera2.CameraDevice}, and if the usage flag contains
+ * {@link HardwareBuffer#USAGE_VIDEO_ENCODE}, the timestamps of the
+ * {@link Image images} produced by the {@link ImageReader} won't be in the same timebase as
+ * {@link android.os.SystemClock#elapsedRealtimeNanos}, even if
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE} is
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME}.
+ * Instead, the timestamps will be roughly in the same timebase as in
+ * {@link android.os.SystemClock#uptimeMillis}, so that A/V synchronization could work for
+ * video recording. In this case, the timestamps from the {@link ImageReader} with
+ * {@link HardwareBuffer#USAGE_VIDEO_ENCODE} usage flag may not be directly comparable with
+ * timestamps of other streams or capture result metadata.
+ * </p>
* @param width The default width in pixels of the Images that this reader will produce.
* @param height The default height in pixels of the Images that this reader will produce.
* @param format The format of the Image that this reader will produce. This must be one of the
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 590def4d4ced..98ca2f9c4253 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -34,7 +34,6 @@ import android.media.tv.tunerresourcemanager.ResourceClientProfile;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IHwBinder;
import android.os.Looper;
@@ -50,6 +49,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -392,7 +392,10 @@ public final class MediaCas implements AutoCloseable {
@Override
public void onReclaimResources() {
synchronized (mSessionMap) {
- mSessionMap.forEach((casSession, sessionResourceHandle) -> casSession.close());
+ List<Session> sessionList = new ArrayList<>(mSessionMap.keySet());
+ for (Session casSession: sessionList) {
+ casSession.close();
+ }
}
mEventHandler.sendMessage(mEventHandler.obtainMessage(
EventHandler.MSG_CAS_RESOURCE_LOST));
@@ -734,7 +737,7 @@ public final class MediaCas implements AutoCloseable {
ResourceClientProfile profile =
new ResourceClientProfile(tvInputServiceSessionId, priorityHint);
mTunerResourceManager.registerClientProfile(
- profile, new HandlerExecutor(mEventHandler), mResourceListener, clientId);
+ profile, context.getMainExecutor(), mResourceListener, clientId);
mClientId = clientId[0];
}
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 197786f42490..c2168f12a351 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -654,6 +654,9 @@ public final class MediaCodecInfo {
* <p>
*
* The following table summarizes the format keys considered by this method.
+ * This is especially important to consider when targeting a higher SDK version than the
+ * minimum SDK version, as this method will disregard some keys on devices below the target
+ * SDK version.
*
* <table style="width: 0%">
* <thead>
@@ -668,7 +671,7 @@ public final class MediaCodecInfo {
* </thead>
* <tbody>
* <tr>
- * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</th>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td>
* <td rowspan=3>{@link MediaFormat#KEY_MIME}<sup>*</sup>,<br>
* {@link MediaFormat#KEY_SAMPLE_RATE},<br>
* {@link MediaFormat#KEY_CHANNEL_COUNT},</td>
@@ -679,30 +682,51 @@ public final class MediaCodecInfo {
* {@link MediaFormat#KEY_WIDTH},<br>
* {@link MediaFormat#KEY_HEIGHT},<br>
* <strong>no</strong> {@code KEY_FRAME_RATE}</td>
- * <td rowspan=4>{@link MediaFormat#KEY_BITRATE_MODE},<br>
+ * <td rowspan=10>as to the left, plus<br>
+ * {@link MediaFormat#KEY_BITRATE_MODE},<br>
* {@link MediaFormat#KEY_PROFILE}
* (and/or {@link MediaFormat#KEY_AAC_PROFILE}<sup>~</sup>),<br>
* <!-- {link MediaFormat#KEY_QUALITY},<br> -->
* {@link MediaFormat#KEY_COMPLEXITY}
* (and/or {@link MediaFormat#KEY_FLAC_COMPRESSION_LEVEL}<sup>~</sup>)</td>
* </tr><tr>
- * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</th>
+ * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td>
* <td rowspan=2>as above, plus<br>
* {@link MediaFormat#KEY_FRAME_RATE}</td>
* </tr><tr>
- * <td>{@link android.os.Build.VERSION_CODES#M}</th>
+ * <td>{@link android.os.Build.VERSION_CODES#M}</td>
* </tr><tr>
- * <td>{@link android.os.Build.VERSION_CODES#N}</th>
- * <td>as above, plus<br>
+ * <td>{@link android.os.Build.VERSION_CODES#N}</td>
+ * <td rowspan=2>as above, plus<br>
* {@link MediaFormat#KEY_PROFILE},<br>
* <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> -->
* {@link MediaFormat#KEY_BIT_RATE}</td>
- * <td>as above, plus<br>
+ * <td rowspan=2>as above, plus<br>
* {@link MediaFormat#KEY_PROFILE},<br>
* {@link MediaFormat#KEY_LEVEL}<sup>+</sup>,<br>
* <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> -->
* {@link MediaFormat#KEY_BIT_RATE},<br>
* {@link CodecCapabilities#FEATURE_IntraRefresh}<sup>E</sup></td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#N_MR1}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#O}</td>
+ * <td rowspan=3 colspan=2>as above, plus<br>
+ * {@link CodecCapabilities#FEATURE_PartialFrame}<sup>D</sup></td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#O_MR1}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#P}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#Q}</td>
+ * <td colspan=2>as above, plus<br>
+ * {@link CodecCapabilities#FEATURE_FrameParsing}<sup>D</sup>,<br>
+ * {@link CodecCapabilities#FEATURE_MultipleFrames},<br>
+ * {@link CodecCapabilities#FEATURE_DynamicTimestamp}</td>
+ * </tr><tr>
+ * <td>{@link android.os.Build.VERSION_CODES#R}</td>
+ * <td colspan=2>as above, plus<br>
+ * {@link CodecCapabilities#FEATURE_LowLatency}<sup>D</sup></td>
* </tr>
* <tr>
* <td colspan=4>
diff --git a/media/java/android/media/MediaFrameworkInitializer.java b/media/java/android/media/MediaFrameworkInitializer.java
new file mode 100644
index 000000000000..577442ec42e4
--- /dev/null
+++ b/media/java/android/media/MediaFrameworkInitializer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+import android.media.session.MediaSessionManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Class for performing registration for all media services
+ *
+ * TODO (b/160513103): Move this class when moving media service code to APEX
+ * @hide
+ */
+public class MediaFrameworkInitializer {
+ private MediaFrameworkInitializer() {
+ }
+
+ private static volatile MediaServiceManager sMediaServiceManager;
+
+ /**
+ * Sets an instance of {@link MediaServiceManager} that allows
+ * the media mainline module to register/obtain media binder services. This is called
+ * by the platform during the system initialization.
+ *
+ * @param mediaServiceManager instance of {@link MediaServiceManager} that allows
+ * the media mainline module to register/obtain media binder services.
+ */
+ public static void setMediaServiceManager(
+ @NonNull MediaServiceManager mediaServiceManager) {
+ Preconditions.checkState(sMediaServiceManager == null,
+ "setMediaServiceManager called twice!");
+ sMediaServiceManager = Objects.requireNonNull(mediaServiceManager);
+ }
+
+ /** @hide */
+ public static MediaServiceManager getMediaServiceManager() {
+ return sMediaServiceManager;
+ }
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all media
+ * services to {@link Context}, so that {@link Context#getSystemService} can return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides
+ * {@link SystemServiceRegistry}
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(
+ Context.MEDIA_SESSION_SERVICE,
+ MediaSessionManager.class,
+ context -> new MediaSessionManager(context)
+ );
+ }
+}
diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java
index 2cfaf4f3dba5..3a5216e1c4e7 100644
--- a/media/java/android/media/MediaMetrics.java
+++ b/media/java/android/media/MediaMetrics.java
@@ -52,6 +52,7 @@ public class MediaMetrics {
public static final String AUDIO_SERVICE = AUDIO + SEPARATOR + "service";
public static final String AUDIO_VOLUME = AUDIO + SEPARATOR + "volume";
public static final String AUDIO_VOLUME_EVENT = AUDIO_VOLUME + SEPARATOR + "event";
+ public static final String AUDIO_MODE = AUDIO + SEPARATOR + "mode";
}
/**
@@ -139,6 +140,10 @@ public class MediaMetrics {
public static final Key<String> REQUEST =
createKey("request", String.class);
+ // For audio mode
+ public static final Key<String> REQUESTED_MODE =
+ createKey("requestedMode", String.class); // audio_mode
+
// For Bluetooth
public static final Key<String> SCO_AUDIO_MODE =
createKey("scoAudioMode", String.class);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index fad25e071f90..7e9d2d809fdd 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -313,6 +313,14 @@ public final class MediaRoute2Info implements Parcelable {
public static final String FEATURE_REMOTE_VIDEO_PLAYBACK =
"android.media.route.feature.REMOTE_VIDEO_PLAYBACK";
+ /**
+ * Route feature: Remote group playback.
+ * <p>
+ * @hide
+ */
+ public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
+ "android.media.route.feature.REMOTE_GROUP_PLAYBACK";
+
final String mId;
final CharSequence mName;
final List<String> mFeatures;
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index f22222d10ad8..ff52a1abc6f4 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -663,13 +663,6 @@ public final class MediaRouter2 {
if (sessionInfo == null) {
notifyTransferFailure(requestedRoute);
return;
- } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
- Log.w(TAG, "The session does not contain the requested route. "
- + "(requestedRouteId=" + requestedRoute.getId()
- + ", actualRoutes=" + sessionInfo.getSelectedRoutes()
- + ")");
- notifyTransferFailure(requestedRoute);
- return;
} else if (!TextUtils.equals(requestedRoute.getProviderId(),
sessionInfo.getProviderId())) {
Log.w(TAG, "The session's provider ID does not match the requested route's. "
diff --git a/media/java/android/media/MediaServiceManager.java b/media/java/android/media/MediaServiceManager.java
new file mode 100644
index 000000000000..21e2d84b0200
--- /dev/null
+++ b/media/java/android/media/MediaServiceManager.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the media
+ * service.
+ *
+ * <p> Only the media mainline module will be able to access an instance of this class.
+ * @hide
+ */
+public class MediaServiceManager {
+ /**
+ * @hide
+ */
+ public MediaServiceManager() {}
+
+ /**
+ * A class that exposes the methods to register and obtain each system service.
+ */
+ public static final class ServiceRegisterer {
+ private final String mServiceName;
+
+ /**
+ * @hide
+ */
+ public ServiceRegisterer(String serviceName) {
+ mServiceName = serviceName;
+ }
+
+ /**
+ * Get the system server binding object for MediaServiceManager.
+ *
+ * <p> This blocks until the service instance is ready.
+ * or a timeout happens, in which case it returns null.
+ */
+ @Nullable
+ public IBinder get() {
+ return ServiceManager.getService(mServiceName);
+ }
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the "media_session" service.
+ */
+ @NonNull
+ public ServiceRegisterer getMediaSessionServiceRegisterer() {
+ return new ServiceRegisterer("media_session");
+ }
+}
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 39376f5ef43b..e959e8e36b42 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -414,6 +414,7 @@ public final class MediaTranscodeManager {
parcel.transcodingType = mType;
parcel.sourceFilePath = mSourceUri.toString();
parcel.destinationFilePath = mDestinationUri.toString();
+ parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat);
if (mTestConfig != null) {
parcel.isForTesting = true;
parcel.testConfig = mTestConfig;
@@ -421,6 +422,66 @@ public final class MediaTranscodeManager {
return parcel;
}
+ /* Converts the MediaFormat to TranscodingVideoTrackFormat. */
+ private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) {
+ if (format == null) {
+ throw new IllegalArgumentException("Invalid MediaFormat");
+ }
+
+ TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat();
+
+ if (format.containsKey(MediaFormat.KEY_MIME)) {
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
+ trackFormat.codecType = TranscodingVideoCodecType.kAvc;
+ } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) {
+ trackFormat.codecType = TranscodingVideoCodecType.kHevc;
+ } else {
+ throw new UnsupportedOperationException("Only support transcode to avc/hevc");
+ }
+ }
+
+ if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
+ int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE);
+ if (bitrateBps <= 0) {
+ throw new IllegalArgumentException("Bitrate must be larger than 0");
+ }
+ trackFormat.bitrateBps = bitrateBps;
+ }
+
+ if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey(
+ MediaFormat.KEY_HEIGHT)) {
+ int width = format.getInteger(MediaFormat.KEY_WIDTH);
+ int height = format.getInteger(MediaFormat.KEY_HEIGHT);
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Width and height must be larger than 0");
+ }
+ // TODO(hkuang): Validate the aspect ratio after adding scaling.
+ trackFormat.width = width;
+ trackFormat.height = height;
+ }
+
+ if (format.containsKey(MediaFormat.KEY_PROFILE)) {
+ int profile = format.getInteger(MediaFormat.KEY_PROFILE);
+ if (profile <= 0) {
+ throw new IllegalArgumentException("Invalid codec profile");
+ }
+ // TODO(hkuang): Validate the profile according to codec type.
+ trackFormat.profile = profile;
+ }
+
+ if (format.containsKey(MediaFormat.KEY_LEVEL)) {
+ int level = format.getInteger(MediaFormat.KEY_LEVEL);
+ if (level <= 0) {
+ throw new IllegalArgumentException("Invalid codec level");
+ }
+ // TODO(hkuang): Validate the level according to codec type.
+ trackFormat.level = level;
+ }
+
+ return trackFormat;
+ }
+
/**
* Builder class for {@link TranscodingRequest} objects.
* Use this class to configure and create a <code>TranscodingRequest</code> instance.
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 42635216e98f..f4fd1fca2ff9 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -53,6 +53,7 @@ import java.util.UUID;
* <li> {@link android.media.audiofx.PresetReverb}</li>
* <li> {@link android.media.audiofx.EnvironmentalReverb}</li>
* <li> {@link android.media.audiofx.DynamicsProcessing}</li>
+ * <li> {@link android.media.audiofx.HapticGenerator}</li>
* </ul>
* <p>To apply the audio effect to a specific AudioTrack or MediaPlayer instance,
* the application must specify the audio session ID of that instance when creating the AudioEffect.
@@ -146,6 +147,14 @@ public class AudioEffect {
.fromString("7261676f-6d75-7369-6364-28e2fd3ac39e");
/**
+ * UUID for Haptic Generator.
+ */
+ // This is taken from system/media/audio/include/system/audio_effects/effect_hapticgenerator.h
+ @NonNull
+ public static final UUID EFFECT_TYPE_HAPTIC_GENERATOR = UUID
+ .fromString("1411e6d6-aecd-4021-a1cf-a6aceb0d71e5");
+
+ /**
* Null effect UUID. See {@link AudioEffect(UUID, UUID, int, int)} for use.
* @hide
*/
@@ -225,7 +234,8 @@ public class AudioEffect {
* {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
* {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
* {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
- * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
+ * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING},
+ * {@link AudioEffect#EFFECT_TYPE_HAPTIC_GENERATOR}.
* </li>
* <li>uuid: UUID for this particular implementation</li>
* <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
@@ -246,8 +256,9 @@ public class AudioEffect {
* {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST},
* {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, {@link AudioEffect#EFFECT_TYPE_EQUALIZER},
* {@link AudioEffect#EFFECT_TYPE_NS}, {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}
- * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}
- * or {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.<br>
+ * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
+ * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING},
+ * or {@link AudioEffect#EFFECT_TYPE_HAPTIC_GENERATOR}.<br>
* For reverberation, bass boost, EQ and virtualizer, the UUID
* corresponds to the OpenSL ES Interface ID.
*/
@@ -284,7 +295,8 @@ public class AudioEffect {
* {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
* {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB},
* {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
- * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
+ * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING},
+ * {@link AudioEffect#EFFECT_TYPE_HAPTIC_GENERATOR}.
* @param uuid UUID for this particular implementation
* @param connectMode {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
* @param name human readable effect name
diff --git a/media/java/android/media/audiofx/HapticGenerator.java b/media/java/android/media/audiofx/HapticGenerator.java
new file mode 100644
index 000000000000..f8529eb05c03
--- /dev/null
+++ b/media/java/android/media/audiofx/HapticGenerator.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.annotation.NonNull;
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.util.UUID;
+
+/**
+ * Haptic Generator(HG).
+ * <p>HG is an audio post-processor which generates haptic data based on the audio channels. The
+ * generated haptic data is sent along with audio data down to the audio HAL, which will require the
+ * device to support audio-coupled-haptic playback. In that case, the effect will only be created on
+ * device supporting audio-coupled-haptic playback. Call {@link HapticGenerator#isAvailable()} to
+ * check if the device supports this effect.
+ * <p>An application can create a HapticGenerator object to initiate and control this audio effect
+ * in the audio framework. An application can set which audio channel to be used to generate
+ * haptic data.
+ * <p>To attach the HapticGenerator to a particular AudioTrack or MediaPlayer, specify the audio
+ * session ID of this AudioTrack or MediaPlayer when constructing the HapticGenerator.
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio
+ * effects.
+ */
+public class HapticGenerator extends AudioEffect implements AutoCloseable {
+
+ private static final String TAG = "HapticGenerator";
+
+ // For every HapticGenerator, it contains a volume control effect so that the volume control
+ // will always be handled in the effect chain. In that case, the HapticGenerator can generate
+ // haptic data based on the raw audio data.
+ private AudioEffect mVolumeControlEffect;
+
+ public static boolean isAvailable() {
+ return AudioManager.isHapticPlaybackSupported()
+ && AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_HAPTIC_GENERATOR);
+ }
+
+ /**
+ * Creates a HapticGenerator and attaches it to the given audio session.
+ * Use {@link android.media.AudioTrack#getAudioSessionId()} or
+ * {@link android.media.MediaPlayer#getAudioSessionId()} to
+ * apply this effect on specific AudioTrack or MediaPlayer instance.
+ *
+ * @param audioSession system wide unique audio session identifier. The HapticGenerator will be
+ * applied to the players with the same audio session.
+ * @return HapticGenerator created or null if the device does not support HapticGenerator or
+ * the audio session is invalid.
+ * @throws java.lang.IllegalArgumentException when HapticGenerator is not supported
+ * @throws java.lang.UnsupportedOperationException when the effect library is not loaded.
+ * @throws java.lang.RuntimeException for all other error
+ */
+ public static @NonNull HapticGenerator create(int audioSession) {
+ return new HapticGenerator(audioSession);
+ }
+
+ /**
+ * Class constructor.
+ *
+ * @param audioSession system wide unique audio session identifier. The HapticGenerator will be
+ * attached to the MediaPlayer or AudioTrack in the same audio session.
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ private HapticGenerator(int audioSession) {
+ super(EFFECT_TYPE_HAPTIC_GENERATOR, EFFECT_TYPE_NULL, 0, audioSession);
+ mVolumeControlEffect = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ audioSession);
+ }
+
+ /**
+ * Enable or disable the effect.
+ *
+ * @param enabled the requested enable state
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
+ * or {@link #ERROR_DEAD_OBJECT} in case of failure.
+ */
+ @Override
+ public int setEnabled(boolean enabled) {
+ int ret = super.setEnabled(enabled);
+ if (ret == SUCCESS) {
+ if (mVolumeControlEffect == null
+ || mVolumeControlEffect.setEnabled(enabled) != SUCCESS) {
+ Log.w(TAG, "Failed to enable volume control effect for HapticGenerator");
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Releases the native AudioEffect resources.
+ */
+ @Override
+ public void release() {
+ if (mVolumeControlEffect != null) {
+ mVolumeControlEffect.release();
+ }
+ super.release();
+ }
+
+ /**
+ * Release the resources that are held by the effect.
+ */
+ @Override
+ public void close() {
+ release();
+ }
+}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 3c2be5f93e30..029e61492b6d 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -25,7 +25,6 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ParceledListSlice;
import android.media.MediaDescription;
-import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Bundle;
@@ -757,8 +756,8 @@ public final class MediaBrowser {
* Flag: Indicates that the item is playable.
* <p>
* The id of this item may be passed to
- * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
- * to start playing it.
+ * {@link android.media.session.MediaController.TransportControls
+ * #playFromMediaId(String, Bundle)} to start playing it.
* </p>
*/
public static final int FLAG_PLAYABLE = 1 << 1;
@@ -1107,13 +1106,7 @@ public final class MediaBrowser {
}
@Override
- public void onLoadChildren(String parentId, ParceledListSlice list) {
- onLoadChildrenWithOptions(parentId, list, null);
- }
-
- @Override
- public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list,
- final Bundle options) {
+ public void onLoadChildren(String parentId, ParceledListSlice list, Bundle options) {
MediaBrowser mediaBrowser = mMediaBrowser.get();
if (mediaBrowser != null) {
mediaBrowser.onLoadChildren(this, parentId, list, options);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 2fd721e6870b..6976a3569655 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -28,14 +28,13 @@ import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.media.AudioManager;
import android.media.IRemoteVolumeController;
+import android.media.MediaFrameworkInitializer;
import android.media.MediaSession2;
import android.media.Session2Token;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.media.MediaBrowserService;
import android.service.notification.NotificationListenerService;
@@ -115,8 +114,10 @@ public final class MediaSessionManager {
// Consider rewriting like DisplayManagerGlobal
// Decide if we need context
mContext = context;
- IBinder b = ServiceManager.getService(Context.MEDIA_SESSION_SERVICE);
- mService = ISessionManager.Stub.asInterface(b);
+ mService = ISessionManager.Stub.asInterface(MediaFrameworkInitializer
+ .getMediaServiceManager()
+ .getMediaSessionServiceRegisterer()
+ .get());
}
/**
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index 7e3f2f8868fb..a8772076af97 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -23,7 +23,5 @@ oneway interface IMediaBrowserServiceCallbacks {
void onConnect(String root, in MediaSession.Token session, in Bundle extras);
@UnsupportedAppUsage
void onConnectFailed();
- void onLoadChildren(String mediaId, in ParceledListSlice list);
- void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list,
- in Bundle options);
+ void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 06adf30a8303..39c7682a2a74 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -687,7 +687,7 @@ public abstract class MediaBrowserService extends Service {
final ParceledListSlice<MediaBrowser.MediaItem> pls =
filteredList == null ? null : new ParceledListSlice<>(filteredList);
try {
- connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options);
+ connection.callbacks.onLoadChildren(parentId, pls, options);
} catch (RemoteException ex) {
// The other side is in the process of crashing.
Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 4a9da62f2517..936edb3fb005 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -86,6 +86,14 @@ public:
void setBufferHeight(int height) { mHeight = height; }
int getBufferHeight() { return mHeight; }
+ void queueAttachedFlag(bool isAttached) {
+ Mutex::Autolock l(mAttachedFlagQueueLock);
+ mAttachedFlagQueue.push_back(isAttached);
+ }
+ void dequeueAttachedFlag() {
+ Mutex::Autolock l(mAttachedFlagQueueLock);
+ mAttachedFlagQueue.pop_back();
+ }
private:
static JNIEnv* getJNIEnv(bool* needsDetach);
static void detachJNI();
@@ -136,6 +144,11 @@ private:
};
static BufferDetacher sBufferDetacher;
+
+ // Buffer queue guarantees both producer and consumer side buffer flows are
+ // in order. See b/19977520. As a result, we can use a queue here.
+ Mutex mAttachedFlagQueueLock;
+ std::deque<bool> mAttachedFlagQueue;
};
JNIImageWriterContext::BufferDetacher JNIImageWriterContext::sBufferDetacher;
@@ -265,11 +278,23 @@ void JNIImageWriterContext::onBufferReleased() {
ALOGV("%s: buffer released", __FUNCTION__);
bool needsDetach = false;
JNIEnv* env = getJNIEnv(&needsDetach);
+
+ bool bufferIsAttached = false;
+ {
+ Mutex::Autolock l(mAttachedFlagQueueLock);
+ if (!mAttachedFlagQueue.empty()) {
+ bufferIsAttached = mAttachedFlagQueue.front();
+ mAttachedFlagQueue.pop_front();
+ } else {
+ ALOGW("onBufferReleased called with no attached flag queued");
+ }
+ }
+
if (env != NULL) {
// Detach the buffer every time when a buffer consumption is done,
// need let this callback give a BufferItem, then only detach if it was attached to this
- // Writer. Do the detach unconditionally for opaque format now. see b/19977520
- if (mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ // Writer. see b/19977520
+ if (mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED || bufferIsAttached) {
sBufferDetacher.detach(mProducer);
}
@@ -622,10 +647,16 @@ static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, j
return;
}
- // Finally, queue input buffer
+ // Finally, queue input buffer.
+ //
+ // Because onBufferReleased may be called before queueBuffer() returns,
+ // queue the "attached" flag before calling queueBuffer. In case
+ // queueBuffer() fails, remove it from the queue.
+ ctx->queueAttachedFlag(false);
res = anw->queueBuffer(anw.get(), buffer, fenceFd);
if (res != OK) {
ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+ ctx->dequeueAttachedFlag();
switch (res) {
case NO_INIT:
jniThrowException(env, "java/lang/IllegalStateException",
@@ -720,10 +751,16 @@ static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nat
}
// Step 3. Queue Image.
+ //
+ // Because onBufferReleased may be called before queueBuffer() returns,
+ // queue the "attached" flag before calling queueBuffer. In case
+ // queueBuffer() fails, remove it from the queue.
+ ctx->queueAttachedFlag(true);
res = anw->queueBuffer(anw.get(), buffer->mGraphicBuffer.get(), /*fenceFd*/
-1);
if (res != OK) {
ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+ ctx->dequeueAttachedFlag();
switch (res) {
case NO_INIT:
jniThrowException(env, "java/lang/IllegalStateException",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 515d610109ab..3cd40818ae2a 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -337,33 +337,44 @@ jobject MediaEvent::getLinearBlock() {
}
mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength);
std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(mIonHandle);
-
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
- context->mBlock = block;
- std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
- context->mBuffer = pC2Buffer;
- mC2Buffer = pC2Buffer;
- if (mAvHandle->numInts > 0) {
- // use first int in the native_handle as the index
- int index = mAvHandle->data[mAvHandle->numFds];
- std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
- std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
- pC2Buffer->setInfo(info);
- }
- pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
- jobject linearBlock =
- env->NewObject(
- env->FindClass("android/media/MediaCodec$LinearBlock"),
- gFields.linearBlockInitID);
- env->CallVoidMethod(
- linearBlock,
- gFields.linearBlockSetInternalStateID,
- (jlong)context.release(),
- true);
- mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
- mAvHandleRefCnt++;
- return mLinearBlockObj;
+ if (block != nullptr) {
+ // CreateLinearBlock delete mIonHandle after it create block successfully.
+ // ToDo: coordinate who is response to delete mIonHandle
+ mIonHandle = NULL;
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+ context->mBlock = block;
+ std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
+ context->mBuffer = pC2Buffer;
+ mC2Buffer = pC2Buffer;
+ if (mAvHandle->numInts > 0) {
+ // use first int in the native_handle as the index
+ int index = mAvHandle->data[mAvHandle->numFds];
+ std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
+ std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
+ pC2Buffer->setInfo(info);
+ }
+ pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
+ jobject linearBlock =
+ env->NewObject(
+ env->FindClass("android/media/MediaCodec$LinearBlock"),
+ gFields.linearBlockInitID);
+ env->CallVoidMethod(
+ linearBlock,
+ gFields.linearBlockSetInternalStateID,
+ (jlong)context.release(),
+ true);
+ mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
+ mAvHandleRefCnt++;
+ return mLinearBlockObj;
+ } else {
+ native_handle_close(const_cast<native_handle_t*>(
+ reinterpret_cast<const native_handle_t*>(mIonHandle)));
+ native_handle_delete(const_cast<native_handle_t*>(
+ reinterpret_cast<const native_handle_t*>(mIonHandle)));
+ mIonHandle = NULL;
+ return NULL;
+ }
}
uint64_t MediaEvent::getAudioHandle() {
@@ -447,7 +458,7 @@ jobjectArray FilterCallback::getMediaEvent(
if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) {
sp<MediaEvent> mediaEventSp =
new MediaEvent(mIFilter, mediaEvent.avMemory,
- mediaEvent.avDataId, dataLength, obj);
+ mediaEvent.avDataId, dataLength + offset, obj);
mediaEventSp->mAvHandleRefCnt++;
env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get());
mediaEventSp->incStrong(obj);
@@ -3497,6 +3508,10 @@ static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong siz
} else {
ALOGE("dvrMq.beginWrite failed");
}
+
+ if (ret > 0) {
+ dvrSp->mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+ }
return (jlong) ret;
}
@@ -3524,7 +3539,7 @@ static jlong android_media_tv_Tuner_read_dvr_from_array(
if (dvrSp->mDvrMQ->write(reinterpret_cast<unsigned char*>(src) + offset, size)) {
env->ReleaseByteArrayElements(buffer, src, 0);
- dvrSp->mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+ dvrSp->mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
} else {
ALOGD("Failed to write FMQ");
env->ReleaseByteArrayElements(buffer, src, 0);
@@ -3585,6 +3600,9 @@ static jlong android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jlong si
} else {
ALOGE("dvrMq.beginRead failed");
}
+ if (ret > 0) {
+ dvrSp->mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+ }
return (jlong) ret;
}
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index efeb3352d393..f4d65d0a397f 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -25,6 +25,7 @@
#include <limits.h>
#include <audio_utils/fixedfft.h>
+#include <cutils/bitops.h>
#include <utils/Thread.h>
#include "Visualizer.h"
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index ebd7658381ee..942f90896eb0 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -167,34 +167,6 @@ public class CameraBinderTest extends AndroidTestCase {
}
}
- @SmallTest
- public void testConnectLegacy() throws Exception {
- final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
- for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
- ICamera cameraUser = null;
- ICameraClient dummyCallbacks = new DummyCameraClient();
-
- String clientPackageName = getContext().getPackageName();
-
- try {
- cameraUser = mUtils.getCameraService()
- .connectLegacy(dummyCallbacks, cameraId, CAMERA_HAL_API_VERSION_1_0,
- clientPackageName,
- ICameraService.USE_CALLING_UID);
- assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
-
- Log.v(TAG, String.format("Camera %s connected as HAL1 legacy device", cameraId));
- } catch (RuntimeException e) {
- // Not all camera device support openLegacy.
- Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
- } finally {
- if (cameraUser != null) {
- cameraUser.disconnect();
- }
- }
- }
- }
-
static class DummyCameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
/*
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java
deleted file mode 100644
index 14bbe44c1516..000000000000
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.mediaframeworktest.unit;
-
-import android.hardware.Camera;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-/**
- * <pre>
- * adb shell am instrument \
- * -e class 'com.android.mediaframeworktest.unit.CameraOpenTest' \
- * -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
- * </pre>
- */
-public class CameraOpenTest extends junit.framework.TestCase {
- private static String TAG = "CameraOpenTest";
-
- private Camera mCamera;
-
- /**
- * Test @hide android.hardware.Camera#openLegacy API that cannot be tested in CTS.
- */
- @SmallTest
- public void testOpenLegacy() {
- int nCameras = Camera.getNumberOfCameras();
- for (int id = 0; id < nCameras; id++) {
- try {
- mCamera.openLegacy(id, Camera.CAMERA_HAL_API_VERSION_1_0);
- } catch (RuntimeException e) {
- Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
- } finally {
- if (mCamera != null) {
- mCamera.release();
- }
- }
- }
- }
-}
diff --git a/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh
index b700ba768e04..43db3530a16b 100644
--- a/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh
+++ b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh
@@ -13,9 +13,6 @@ fi
mm
-# Push the files onto the device.
-. $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/push_assets.sh
-
echo "[==========] waiting for device"
adb root && adb wait-for-device remount
@@ -27,5 +24,13 @@ echo "[==========] build test apk"
mmm -j16 .
adb install -r -g ${OUT}/testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
+# Push the files into app's cache directory/
+FILES=$ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/*
+for file in $FILES
+do
+adb push --sync $file /data/user/0/com.android.mediatranscodingtest/cache/
+done
+
echo "[==========] running real transcoding tests"
adb shell am instrument -e class com.android.mediatranscodingtest.MediaTranscodeManagerTest -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
+
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index c25b72b9b78d..3a5e69293a02 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -58,7 +58,7 @@ public class MediaTranscodeManagerTest
extends ActivityInstrumentationTestCase2<MediaTranscodingTest> {
private static final String TAG = "MediaTranscodeManagerTest";
/** The time to wait for the transcode operation to complete before failing the test. */
- private static final int TRANSCODE_TIMEOUT_SECONDS = 2;
+ private static final int TRANSCODE_TIMEOUT_SECONDS = 10;
private Context mContext;
private MediaTranscodeManager mMediaTranscodeManager = null;
private Uri mSourceHEVCVideoUri = null;
@@ -67,10 +67,13 @@ public class MediaTranscodeManagerTest
// Setting for transcoding to H.264.
private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
- private static final int BIT_RATE = 2000000; // 2Mbps
+ private static final int BIT_RATE = 20000000; // 20Mbps
private static final int WIDTH = 1920;
private static final int HEIGHT = 1080;
+ // Threshold for the psnr to make sure the transcoded video is sane.
+ private static final int PSNR_THRESHOLD = 20;
+
public MediaTranscodeManagerTest() {
super("com.android.MediaTranscodeManagerTest", MediaTranscodingTest.class);
}
@@ -120,6 +123,7 @@ public class MediaTranscodeManagerTest
public void setUp() throws Exception {
Log.d(TAG, "setUp");
super.setUp();
+
mContext = getInstrumentation().getContext();
mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext);
assertNotNull(mMediaTranscodeManager);
@@ -141,7 +145,7 @@ public class MediaTranscodeManagerTest
}
@Test
- public void testTranscodingFromAvcToAvc() throws Exception {
+ public void testTranscodingFromHevcToAvc() throws Exception {
Log.d(TAG, "Starting: testMediaTranscodeManager");
Semaphore transcodeCompleteSemaphore = new Semaphore(0);
@@ -150,13 +154,11 @@ public class MediaTranscodeManagerTest
// The full path of this file is:
// /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
- + mContext.getCacheDir().getAbsolutePath() + "/temp.mp4");
-
- Log.d(TAG, "Transcoding to " + destinationUri.getPath());
+ + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
TranscodingRequest request =
new TranscodingRequest.Builder()
- .setSourceUri(mSourceAVCVideoUri)
+ .setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(destinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
.setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
@@ -164,6 +166,8 @@ public class MediaTranscodeManagerTest
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
+ Log.i(TAG, "transcoding to " + createMediaFormat());
+
TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
transcodingJob -> {
Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
@@ -173,14 +177,58 @@ public class MediaTranscodeManagerTest
assertNotNull(job);
if (job != null) {
- Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
+ Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel.");
boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue("Transcode failed to complete in time.", finishedOnTime);
}
- //TODO(hkuang): Verify the transcoded video's psnr to make sure it is correct.
+ // TODO(hkuang): Validate the transcoded video's width and height, framerate.
+
+ // Validates the transcoded video's psnr.
+ MediaTranscodingTestUtil.VideoTranscodingStatistics stats =
+ MediaTranscodingTestUtil.computeStats(mContext, mSourceAVCVideoUri, destinationUri);
+ assertTrue("PSNR: " + stats.mAveragePSNR + " is too low",
+ stats.mAveragePSNR >= PSNR_THRESHOLD);
}
+ @Test
+ public void testCancelTranscoding() throws Exception {
+ Log.d(TAG, "Starting: testMediaTranscodeManager");
+ Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+
+ // Transcode a 15 seconds video, so that the transcoding is not finished when we cancel.
+ Uri srcUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ + mContext.getCacheDir().getAbsolutePath() + "/longtest_15s.mp4");
+ Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
+
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(srcUri)
+ .setDestinationUri(destinationUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+ TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+ transcodingJob -> {
+ Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
+ assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_CANCELED);
+ transcodeCompleteSemaphore.release();
+ });
+ assertNotNull(job);
+
+ // TODO(hkuang): Wait for progress update before calling cancel to make sure transcoding is
+ // started.
+
+ job.cancel();
+ Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel.");
+ boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+ 30, TimeUnit.MILLISECONDS);
+ assertTrue("Fails to cancel transcoding", finishedOnTime);
+ }
}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
new file mode 100644
index 000000000000..5c87d30068b2
--- /dev/null
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediatranscodingtest;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.MediaFormat;
+import android.media.MediaTranscodeManager;
+import android.media.MediaTranscodeManager.TranscodingJob;
+import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/*
+ * Benchmarking for MediaTranscodeManager in the media framework.
+ *
+ * Note: This benchmarking requires to push all the files from http://go/transcodingbenchmark
+ * to /data/user/0/com.android.mediatranscodingtest/cache/ directory after installing the apk.
+ *
+ * TODO(hkuang): Change it to download from server automatically instead of manually.
+ *
+ * To run this test suite:
+ make frameworks/base/media/tests/MediaTranscodingTest
+ make mediatranscodingtest
+
+ adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
+ // Push the files to /data/user/0/com.android.mediatranscodingtest/cache/
+ adb push $DOWNLOADPATH/*.mp4 /data/user/0/com.android.mediatranscodingtest/cache/
+
+ adb shell am instrument -e class \
+ com.android.mediatranscodingtest.MediaTranscodingBenchmark \
+ -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
+ *
+ */
+public class MediaTranscodingBenchmark
+ extends ActivityInstrumentationTestCase2<MediaTranscodingTest> {
+ private static final String TAG = "MediaTranscodingBenchmark";
+ // TODO(hkuang): Change this to query from MediaCodecInfo.CodecCapabilities for different
+ // resolution.
+ private static final int MINIMUM_TRANSCODING_FPS = 80;
+ private static final int LOOP_COUNT = 3;
+ // Default Setting for transcoding to H.264.
+ private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
+ private static final int BIT_RATE = 20000000; // 20Mbps
+ private static final int WIDTH = 1920;
+ private static final int HEIGHT = 1080;
+ private Context mContext;
+ private MediaTranscodeManager mMediaTranscodeManager = null;
+
+ public MediaTranscodingBenchmark() {
+ super("com.android.MediaTranscodingBenchmark", MediaTranscodingTest.class);
+ }
+
+ /**
+ * Creates a MediaFormat with the basic set of values.
+ */
+ private static MediaFormat createMediaFormat() {
+ MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+ return format;
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ Log.d(TAG, "setUp");
+ super.setUp();
+ mContext = getInstrumentation().getContext();
+ mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /*
+ * Transcode the sourceFileName to destinationFileName with LOOP_COUNT.
+ */
+ private void transcode(final String sourceFileName, final String destinationFileName)
+ throws IOException, InterruptedException {
+ AtomicLong totalTimeMs = new AtomicLong();
+ AtomicLong transcodingTime = new AtomicLong();
+ Uri srcUri = getUri(sourceFileName);
+ Uri dstUri = getUri(destinationFileName);
+
+ MediaTranscodingTestUtil.VideoFileInfo info =
+ MediaTranscodingTestUtil.extractVideoFileInfo(mContext, getUri(sourceFileName));
+
+ int timeoutSeconds = calMaxTranscodingWaitTimeSeconds(info.mNumVideoFrames,
+ MINIMUM_TRANSCODING_FPS);
+ Log.d(TAG, "Start Transcoding " + info.toString() + " " + timeoutSeconds);
+
+ for (int loop = 0; loop < LOOP_COUNT; ++loop) {
+ Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(srcUri)
+ .setDestinationUri(dstUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+ long startTimeMs = System.currentTimeMillis();
+ TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+ transcodingJob -> {
+ Log.d(TAG,
+ "Transcoding completed with result: " + transcodingJob.getResult());
+ assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS);
+ transcodeCompleteSemaphore.release();
+ transcodingTime.set(System.currentTimeMillis() - startTimeMs);
+ totalTimeMs.addAndGet(transcodingTime.get());
+ });
+
+ if (job != null) {
+ Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
+ boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+ timeoutSeconds, TimeUnit.SECONDS);
+ assertTrue("Transcode failed to complete in time.", finishedOnTime);
+ }
+ Log.i(TAG, "Loop: " + loop + " take " + transcodingTime.get() + " ms ");
+ }
+
+ float fps = info.mNumVideoFrames * 1000 * LOOP_COUNT / totalTimeMs.get();
+ Log.i(TAG, "Transcoding " + info.toString() + " Transcoding fps: " + fps);
+ }
+
+ // Calculate the maximum wait time based on minimum transcoding throughput and frame number.
+ private int calMaxTranscodingWaitTimeSeconds(int numberFrames, int minTranscodingFps) {
+ float waitTime = (float) numberFrames / (float) minTranscodingFps;
+ // If waitTimeSeconds is 0, wait for 1 second at least.
+ int waitTimeSeconds = (int) Math.ceil(waitTime);
+ return waitTimeSeconds == 0 ? 1 : waitTimeSeconds;
+ }
+
+ private Uri getUri(final String fileName) {
+ String path = mContext.getCacheDir().getAbsolutePath();
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_FILE).appendPath(path).appendPath(
+ fileName).build();
+ }
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith66FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
+ // more long time that leads to timeout failure.
+ /*
+ @Test
+ public void testBenchmarkingAVCToAVCWith66FramesWithAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps_aac";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }*/
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith361FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_361frame_h264_22Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
+ // more long time that leads to timeout failure.
+ /*@Test
+ public void testBenchmarkingAVCToAVCWith361FramesWithAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_361frame_h264_22Mbps_30fps_aac";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }*/
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith943FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_943frame_h264_22Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
+ // more long time that leads to timeout failure.
+ /* @Test
+ public void testBenchmarkingAVCToAVCWith943FramesWithAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_943frame_h264_22Mbps_30fps_aac";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }*/
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith1822FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_1822frame_h264_22Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
+ // more long time that leads to timeout failure.
+ /*@Test
+ public void testBenchmarkingAVCToAVCWith1822FramesWithAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_1822frame_h264_22Mbps_30fps_aac";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }*/
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith3648FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_3648frame_h264_22Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
+ // more long time that leads to timeout failure.
+ /*@Test
+ public void testBenchmarkingAVCToAVCWith3648FramesWithAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_3648frame_h264_22Mbps_30fps_aac";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }*/
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith11042FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_11042frame_h264_22Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
+ // more long time that leads to timeout failure.
+ /*@Test
+ public void testBenchmarkingAVCToAVCWith11042FramesWithAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_11042frame_h264_22Mbps_30fps_aac";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ } */
+
+ @Test
+ public void testBenchmarkingHEVCToAVCWith107FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_107frame_hevc_4Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ @Test
+ public void testBenchmarkingHEVCToAVCWith928FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_928frame_hevc_4Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ @Test
+ public void testBenchmarkingHEVCToAVCWith1863FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_1863frame_hevc_4Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ @Test
+ public void testBenchmarkingHEVCToAVCWith3863FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_3863frame_hevc_4Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ @Test
+ public void testBenchmarkingHEVCToAVCWith9374FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_9374frame_hevc_4Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
index 3b044c7e3707..53b23927fc64 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
@@ -38,6 +38,7 @@ public class MediaTranscodingTestRunner extends InstrumentationTestRunner {
TestSuite suite = new InstrumentationTestSuite(this);
suite.addTestSuite(MediaTranscodeManagerTest.class);
suite.addTestSuite(MediaTranscodeManagerWithMockServiceTest.class);
+ suite.addTestSuite(MediaTranscodingBenchmark.class);
return suite;
}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
new file mode 100644
index 000000000000..69f124f42abd
--- /dev/null
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediatranscodingtest;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.ImageFormat;
+import android.media.Image;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import android.util.Log;
+import android.util.Size;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+
+
+/* package */ class MediaTranscodingTestUtil {
+ private static final String TAG = "MediaTranscodingTestUtil";
+
+ // Helper class to extract the information from source file and transcoded file.
+ static class VideoFileInfo {
+ String mUri;
+ int mNumVideoFrames = 0;
+ int mWidth = 0;
+ int mHeight = 0;
+ float mVideoFrameRate = 0.0f;
+ boolean mHasAudio = false;
+
+ public String toString() {
+ String str = mUri;
+ str += " Width:" + mWidth;
+ str += " Height:" + mHeight;
+ str += " FrameRate:" + mWidth;
+ str += " FrameCount:" + mNumVideoFrames;
+ str += " HasAudio:" + (mHasAudio ? "Yes" : "No");
+ return str;
+ }
+ }
+
+ static VideoFileInfo extractVideoFileInfo(Context ctx, Uri videoUri) throws IOException {
+ VideoFileInfo info = new VideoFileInfo();
+ AssetFileDescriptor afd = null;
+ MediaMetadataRetriever retriever = null;
+
+ try {
+ afd = ctx.getContentResolver().openAssetFileDescriptor(videoUri, "r");
+ retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+
+ info.mUri = videoUri.getLastPathSegment();
+ Log.i(TAG, "Trying to transcode to " + info.mUri);
+ String width = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+ String height = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+ if (width != null && height != null) {
+ info.mWidth = Integer.parseInt(width);
+ info.mHeight = Integer.parseInt(height);
+ }
+
+ String frameRate = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE);
+ if (frameRate != null) {
+ info.mVideoFrameRate = Float.parseFloat(frameRate);
+ }
+
+ String frameCount = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT);
+ if (frameCount != null) {
+ info.mNumVideoFrames = Integer.parseInt(frameCount);
+ }
+
+ String hasAudio = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO);
+ if (hasAudio != null) {
+ info.mHasAudio = hasAudio.equals("yes");
+ }
+ } finally {
+ if (retriever != null) {
+ retriever.close();
+ }
+ if (afd != null) {
+ afd.close();
+ }
+ }
+ return info;
+ }
+
+ static VideoTranscodingStatistics computeStats(final Context ctx, final Uri sourceMp4,
+ final Uri transcodedMp4)
+ throws Exception {
+ // First decode the sourceMp4 to a temp yuv in yuv420p format.
+ Uri sourceYUV420PUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ + ctx.getCacheDir().getAbsolutePath() + "/sourceYUV420P.yuv");
+ Size sourceSize = decodeMp4ToYuv(ctx, sourceMp4, sourceYUV420PUri);
+
+ // Second decode the transcodedMp4 to a temp yuv in yuv420p format.
+ Uri transcodedYUV420PUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ + ctx.getCacheDir().getAbsolutePath() + "/transcodedYUV420P.yuv");
+ Size transcodedSize = decodeMp4ToYuv(ctx, transcodedMp4, transcodedYUV420PUri);
+
+ assertTrue(sourceSize.equals(transcodedSize));
+
+ // Then Compute the psnr of transcodedYUV420PUri against sourceYUV420PUri.
+ return computePsnr(ctx, sourceYUV420PUri, transcodedYUV420PUri, sourceSize.getWidth(),
+ transcodedSize.getHeight());
+ }
+
+ private static Size decodeMp4ToYuv(final Context ctx, final Uri fileUri, final Uri yuvUri)
+ throws Exception {
+ AssetFileDescriptor fileFd = null;
+ MediaExtractor extractor = null;
+ MediaCodec codec = null;
+ AssetFileDescriptor yuvFd = null;
+ FileOutputStream out = null;
+ int width = 0;
+ int height = 0;
+
+ try {
+ fileFd = ctx.getContentResolver().openAssetFileDescriptor(fileUri,"r");
+ extractor = new MediaExtractor();
+ extractor.setDataSource(fileFd.getFileDescriptor(), fileFd.getStartOffset(),
+ fileFd.getLength());
+
+ // Selects the video track.
+ int trackCount = extractor.getTrackCount();
+ if (trackCount <= 0) {
+ throw new IllegalArgumentException("Invalid mp4 file");
+ }
+ int videoTrackIndex = -1;
+ for (int i = 0; i < trackCount; i++) {
+ extractor.selectTrack(i);
+ MediaFormat format = extractor.getTrackFormat(i);
+ if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
+ videoTrackIndex = i;
+ break;
+ }
+ }
+ if (videoTrackIndex == -1) {
+ throw new IllegalArgumentException("Can not find video track");
+ }
+
+ extractor.selectTrack(videoTrackIndex);
+ MediaFormat format = extractor.getTrackFormat(videoTrackIndex);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
+
+ // Opens the yuv file uri.
+ yuvFd = ctx.getContentResolver().openAssetFileDescriptor(yuvUri,
+ "w");
+ out = new FileOutputStream(yuvFd.getFileDescriptor());
+
+ codec = MediaCodec.createDecoderByType(mime);
+ codec.configure(format,
+ null, // surface
+ null, // crypto
+ 0); // flags
+ codec.start();
+
+ ByteBuffer[] inputBuffers = codec.getInputBuffers();
+ ByteBuffer[] outputBuffers = codec.getOutputBuffers();
+ MediaFormat decoderOutputFormat = codec.getInputFormat();
+
+ // start decode loop
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+
+ final long kTimeOutUs = 1000; // 1ms timeout
+ long lastOutputTimeUs = 0;
+ boolean sawInputEOS = false;
+ boolean sawOutputEOS = false;
+ int inputNum = 0;
+ int outputNum = 0;
+ boolean advanceDone = true;
+
+ long start = System.currentTimeMillis();
+ while (!sawOutputEOS) {
+ // handle input
+ if (!sawInputEOS) {
+ int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
+
+ if (inputBufIndex >= 0) {
+ ByteBuffer dstBuf = inputBuffers[inputBufIndex];
+ // sample contains the buffer and the PTS offset normalized to frame index
+ int sampleSize =
+ extractor.readSampleData(dstBuf, 0 /* offset */);
+ long presentationTimeUs = extractor.getSampleTime();
+ advanceDone = extractor.advance();
+
+ if (sampleSize < 0) {
+ Log.d(TAG, "saw input EOS.");
+ sawInputEOS = true;
+ sampleSize = 0;
+ }
+ codec.queueInputBuffer(
+ inputBufIndex,
+ 0 /* offset */,
+ sampleSize,
+ presentationTimeUs,
+ sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+ } else {
+ Log.d(TAG, "codec.dequeueInputBuffer() unrecognized return value:");
+ }
+ }
+
+ // handle output
+ int outputBufIndex = codec.dequeueOutputBuffer(info, kTimeOutUs);
+
+ if (outputBufIndex >= 0) {
+ if (info.size > 0) { // Disregard 0-sized buffers at the end.
+ outputNum++;
+ Log.i(TAG, "Output frame numer " + outputNum);
+ Image image = codec.getOutputImage(outputBufIndex);
+ dumpYUV420PToFile(image, out);
+ }
+
+ codec.releaseOutputBuffer(outputBufIndex, false /* render */);
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ Log.d(TAG, "saw output EOS.");
+ sawOutputEOS = true;
+ }
+ } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ outputBuffers = codec.getOutputBuffers();
+ Log.d(TAG, "output buffers have changed.");
+ } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ decoderOutputFormat = codec.getOutputFormat();
+ width = decoderOutputFormat.getInteger(MediaFormat.KEY_WIDTH);
+ height = decoderOutputFormat.getInteger(MediaFormat.KEY_HEIGHT);
+ Log.d(TAG, "output resolution " + width + "x" + height);
+ } else {
+ Log.w(TAG, "codec.dequeueOutputBuffer() unrecognized return index");
+ }
+ }
+ } finally {
+ codec.stop();
+ codec.release();
+ extractor.release();
+ out.close();
+ fileFd.close();
+ yuvFd.close();
+ }
+ return new Size(width, height);
+ }
+
+ private static void dumpYUV420PToFile(Image image, FileOutputStream out) throws IOException {
+ int format = image.getFormat();
+
+ if (ImageFormat.YUV_420_888 != format) {
+ throw new UnsupportedOperationException("Only supports YUV420P");
+ }
+
+ int imageWidth = image.getWidth();
+ int imageHeight = image.getHeight();
+ byte[] bb = new byte[imageWidth * imageHeight];
+ byte[] lb = null;
+ Image.Plane[] planes = image.getPlanes();
+ for (int i = 0; i < planes.length; ++i) {
+ ByteBuffer buf = planes[i].getBuffer();
+
+ int width, height, rowStride, pixelStride, x, y;
+ rowStride = planes[i].getRowStride();
+ pixelStride = planes[i].getPixelStride();
+ if (i == 0) {
+ width = imageWidth;
+ height = imageHeight;
+ } else {
+ width = imageWidth / 2;
+ height = imageHeight / 2;
+ }
+
+ if (buf.hasArray()) {
+ byte b[] = buf.array();
+ int offs = buf.arrayOffset();
+ if (pixelStride == 1) {
+ for (y = 0; y < height; ++y) {
+ System.arraycopy(bb, y * width, b, y * rowStride + offs, width);
+ }
+ } else {
+ // do it pixel-by-pixel
+ for (y = 0; y < height; ++y) {
+ int lineOffset = offs + y * rowStride;
+ for (x = 0; x < width; ++x) {
+ bb[y * width + x] = b[lineOffset + x * pixelStride];
+ }
+ }
+ }
+ } else { // almost always ends up here due to direct buffers
+ int pos = buf.position();
+ if (pixelStride == 1) {
+ for (y = 0; y < height; ++y) {
+ buf.position(pos + y * rowStride);
+ buf.get(bb, y * width, width);
+ }
+ } else {
+ // Reallocate linebuffer if necessary.
+ if (lb == null || lb.length < rowStride) {
+ lb = new byte[rowStride];
+ }
+ // do it pixel-by-pixel
+ for (y = 0; y < height; ++y) {
+ buf.position(pos + y * rowStride);
+ // we're only guaranteed to have pixelStride * (width - 1) + 1 bytes
+ buf.get(lb, 0, pixelStride * (width - 1) + 1);
+ for (x = 0; x < width; ++x) {
+ bb[y * width + x] = lb[x * pixelStride];
+ }
+ }
+ }
+ buf.position(pos);
+ }
+ // Write out the buffer to the output.
+ out.write(bb, 0, width * height);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // The following psnr code is leveraged from the following file with minor modification:
+ // cts/tests/tests/media/src/android/media/cts/VideoCodecTestBase.java
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Calculates PSNR value between two video frames.
+ */
+ private static double computePSNR(byte[] data0, byte[] data1) {
+ long squareError = 0;
+ assertTrue(data0.length == data1.length);
+ int length = data0.length;
+ for (int i = 0; i < length; i++) {
+ int diff = ((int) data0[i] & 0xff) - ((int) data1[i] & 0xff);
+ squareError += diff * diff;
+ }
+ double meanSquareError = (double) squareError / length;
+ double psnr = 10 * Math.log10((double) 255 * 255 / meanSquareError);
+ return psnr;
+ }
+
+ /**
+ * Calculates average and minimum PSNR values between
+ * set of reference and decoded video frames.
+ * Runs PSNR calculation for the full duration of the decoded data.
+ */
+ private static VideoTranscodingStatistics computePsnr(
+ Context ctx,
+ Uri referenceYuvFileUri,
+ Uri decodedYuvFileUri,
+ int width,
+ int height) throws Exception {
+ VideoTranscodingStatistics statistics = new VideoTranscodingStatistics();
+ AssetFileDescriptor referenceFd = ctx.getContentResolver().openAssetFileDescriptor(
+ referenceYuvFileUri, "r");
+ InputStream referenceStream = new FileInputStream(referenceFd.getFileDescriptor());
+
+ AssetFileDescriptor decodedFd = ctx.getContentResolver().openAssetFileDescriptor(
+ decodedYuvFileUri, "r");
+ InputStream decodedStream = new FileInputStream(decodedFd.getFileDescriptor());
+
+ int ySize = width * height;
+ int uvSize = width * height / 4;
+ byte[] yRef = new byte[ySize];
+ byte[] yDec = new byte[ySize];
+ byte[] uvRef = new byte[uvSize];
+ byte[] uvDec = new byte[uvSize];
+
+ int frames = 0;
+ double averageYPSNR = 0;
+ double averageUPSNR = 0;
+ double averageVPSNR = 0;
+ double minimumYPSNR = Integer.MAX_VALUE;
+ double minimumUPSNR = Integer.MAX_VALUE;
+ double minimumVPSNR = Integer.MAX_VALUE;
+ int minimumPSNRFrameIndex = 0;
+
+ while (true) {
+ // Calculate Y PSNR.
+ int bytesReadRef = referenceStream.read(yRef);
+ int bytesReadDec = decodedStream.read(yDec);
+ if (bytesReadDec == -1) {
+ break;
+ }
+ if (bytesReadRef == -1) {
+ // Reference file wrapping up
+ referenceStream.close();
+ break;
+ }
+ double curYPSNR = computePSNR(yRef, yDec);
+ averageYPSNR += curYPSNR;
+ minimumYPSNR = Math.min(minimumYPSNR, curYPSNR);
+ double curMinimumPSNR = curYPSNR;
+
+ // Calculate U PSNR.
+ bytesReadRef = referenceStream.read(uvRef);
+ bytesReadDec = decodedStream.read(uvDec);
+ double curUPSNR = computePSNR(uvRef, uvDec);
+ averageUPSNR += curUPSNR;
+ minimumUPSNR = Math.min(minimumUPSNR, curUPSNR);
+ curMinimumPSNR = Math.min(curMinimumPSNR, curUPSNR);
+
+ // Calculate V PSNR.
+ bytesReadRef = referenceStream.read(uvRef);
+ bytesReadDec = decodedStream.read(uvDec);
+ double curVPSNR = computePSNR(uvRef, uvDec);
+ averageVPSNR += curVPSNR;
+ minimumVPSNR = Math.min(minimumVPSNR, curVPSNR);
+ curMinimumPSNR = Math.min(curMinimumPSNR, curVPSNR);
+
+ // Frame index for minimum PSNR value - help to detect possible distortions
+ if (curMinimumPSNR < statistics.mMinimumPSNR) {
+ statistics.mMinimumPSNR = curMinimumPSNR;
+ minimumPSNRFrameIndex = frames;
+ }
+
+ String logStr = String.format(Locale.US, "PSNR #%d: Y: %.2f. U: %.2f. V: %.2f",
+ frames, curYPSNR, curUPSNR, curVPSNR);
+ Log.v(TAG, logStr);
+
+ frames++;
+ }
+
+ averageYPSNR /= frames;
+ averageUPSNR /= frames;
+ averageVPSNR /= frames;
+ statistics.mAveragePSNR = (4 * averageYPSNR + averageUPSNR + averageVPSNR) / 6;
+
+ Log.d(TAG, "PSNR statistics for " + frames + " frames.");
+ String logStr = String.format(Locale.US,
+ "Average PSNR: Y: %.1f. U: %.1f. V: %.1f. Average: %.1f",
+ averageYPSNR, averageUPSNR, averageVPSNR, statistics.mAveragePSNR);
+ Log.d(TAG, logStr);
+ logStr = String.format(Locale.US,
+ "Minimum PSNR: Y: %.1f. U: %.1f. V: %.1f. Overall: %.1f at frame %d",
+ minimumYPSNR, minimumUPSNR, minimumVPSNR,
+ statistics.mMinimumPSNR, minimumPSNRFrameIndex);
+ Log.d(TAG, logStr);
+
+ referenceStream.close();
+ decodedStream.close();
+ referenceFd.close();
+ decodedFd.close();
+ return statistics;
+ }
+
+ /**
+ * Transcoding PSNR statistics.
+ */
+ protected static class VideoTranscodingStatistics {
+ public double mAveragePSNR;
+ public double mMinimumPSNR;
+
+ VideoTranscodingStatistics() {
+ mMinimumPSNR = Integer.MAX_VALUE;
+ }
+ }
+}
+
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index eb27a49429e0..2b95992b1edf 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -2926,8 +2926,13 @@ package android.accessibilityservice {
field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
field public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; // 0xd
field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; // 0xe
+ field public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; // 0xe
+ field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11; // 0xb
+ field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc
+ field public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; // 0xd
field public static final int GLOBAL_ACTION_BACK = 1; // 0x1
field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
+ field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa
field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8
field public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; // 0x4
field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
@@ -24056,6 +24061,7 @@ package android.media {
field public static final int TYPE_IP = 20; // 0x14
field public static final int TYPE_LINE_ANALOG = 5; // 0x5
field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+ field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
field public static final int TYPE_TELEPHONY = 18; // 0x12
field public static final int TYPE_TV_TUNER = 17; // 0x11
field public static final int TYPE_UNKNOWN = 0; // 0x0
@@ -27661,6 +27667,7 @@ package android.media.audiofx {
field public static final java.util.UUID EFFECT_TYPE_DYNAMICS_PROCESSING;
field public static final java.util.UUID EFFECT_TYPE_ENV_REVERB;
field public static final java.util.UUID EFFECT_TYPE_EQUALIZER;
+ field @NonNull public static final java.util.UUID EFFECT_TYPE_HAPTIC_GENERATOR;
field public static final java.util.UUID EFFECT_TYPE_LOUDNESS_ENHANCER;
field public static final java.util.UUID EFFECT_TYPE_NS;
field public static final java.util.UUID EFFECT_TYPE_PRESET_REVERB;
@@ -28013,6 +28020,13 @@ package android.media.audiofx {
field public short numBands;
}
+ public class HapticGenerator extends android.media.audiofx.AudioEffect implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public static android.media.audiofx.HapticGenerator create(int);
+ method public static boolean isAvailable();
+ method public int setEnabled(boolean);
+ }
+
public class LoudnessEnhancer extends android.media.audiofx.AudioEffect {
ctor public LoudnessEnhancer(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.RuntimeException, java.lang.UnsupportedOperationException;
method public float getTargetGain() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
@@ -45189,7 +45203,7 @@ package android.telephony {
public abstract class CellLocation {
ctor public CellLocation();
method public static android.telephony.CellLocation getEmpty();
- method public static void requestLocationUpdate();
+ method @Deprecated public static void requestLocationUpdate();
}
public abstract class CellSignalStrength {
@@ -47251,6 +47265,7 @@ package android.telephony.ims.feature {
}
public static class MmTelFeature.MmTelCapabilities {
+ method public final boolean isCapable(int);
field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8
field public static final int CAPABILITY_TYPE_UT = 4; // 0x4
field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index c8406f199edd..c1dbfb7a9be7 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -1,4 +1,16 @@
// Signature format: 2.0
+package android.app {
+
+ public class AppOpsManager {
+ field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
+ }
+
+ public class NotificationManager {
+ method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
+ }
+
+}
+
package android.content.rollback {
public class RollbackManagerFrameworkInitializer {
@@ -21,6 +33,10 @@ package android.graphics {
package android.os {
+ public class Binder implements android.os.IBinder {
+ method public final void markVintfStability();
+ }
+
public class StatsServiceManager {
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/non-updatable-api/removed.txt b/non-updatable-api/removed.txt
index ba05a1b89988..f2dfb84eb8fe 100644
--- a/non-updatable-api/removed.txt
+++ b/non-updatable-api/removed.txt
@@ -1,10 +1,6 @@
// Signature format: 2.0
package android.app {
- public class ActivityManager {
- method @Deprecated public static int getMaxNumPictureInPictureActions();
- }
-
public class Notification implements android.os.Parcelable {
method @Deprecated public String getChannel();
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 9dac11876ce3..222e563d4f96 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -2465,6 +2465,7 @@ package android.hardware.hdmi {
public final class HdmiControlManager {
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
method @Nullable public android.hardware.hdmi.HdmiClient getClient(int);
method @NonNull public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevices();
method public int getPhysicalAddress();
@@ -3613,8 +3614,13 @@ package android.hardware.usb {
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+ field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+ field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START";
+ field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+ field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
field public static final long FUNCTION_ADB = 1L; // 0x1L
field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
@@ -4106,10 +4112,6 @@ package android.media {
field public static final int ROLE_OUTPUT = 2; // 0x2
}
- public final class AudioDeviceInfo {
- field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
- }
-
public final class AudioFocusInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.media.AudioAttributes getAttributes();
@@ -11112,7 +11114,6 @@ package android.telephony.ims.feature {
ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
ctor public MmTelFeature.MmTelCapabilities(int);
method public final void addCapabilities(int);
- method public final boolean isCapable(int);
method public final void removeCapabilities(int);
}
@@ -11649,6 +11650,7 @@ package android.webkit {
method public void super_computeScroll();
method public boolean super_dispatchKeyEvent(android.view.KeyEvent);
method public int super_getScrollBarStyle();
+ method @Nullable public android.view.WindowInsets super_onApplyWindowInsets(@Nullable android.view.WindowInsets);
method public void super_onDrawVerticalScrollBar(android.graphics.Canvas, android.graphics.drawable.Drawable, int, int, int, int);
method public boolean super_onGenericMotionEvent(android.view.MotionEvent);
method public boolean super_onHoverEvent(android.view.MotionEvent);
@@ -11851,6 +11853,7 @@ package android.webkit {
method public android.os.Handler getHandler(android.os.Handler);
method public default boolean isVisibleToUserForAutofill(int);
method public void onActivityResult(int, int, android.content.Intent);
+ method @Nullable public default android.view.WindowInsets onApplyWindowInsets(@Nullable android.view.WindowInsets);
method public void onAttachedToWindow();
method public default boolean onCheckIsTextEditor();
method public void onConfigurationChanged(android.content.res.Configuration);
diff --git a/non-updatable-api/system-removed.txt b/non-updatable-api/system-removed.txt
index ab4c6d1a71a0..0c02c43b1084 100644
--- a/non-updatable-api/system-removed.txt
+++ b/non-updatable-api/system-removed.txt
@@ -165,11 +165,6 @@ package android.telecom {
package android.telephony {
- public final class PreciseDataConnectionState implements android.os.Parcelable {
- method @Deprecated @Nullable public android.net.LinkProperties getDataConnectionLinkProperties();
- method @Deprecated public int getDataConnectionNetworkType();
- }
-
public class TelephonyManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void answerRingingCall();
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public boolean endCall();
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 32b33a758535..8598f74e1441 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -49,7 +49,7 @@ android_library {
"androidx.lifecycle_lifecycle-extensions",
"SystemUI-tags",
"SystemUI-proto",
- "dagger2-2.19",
+ "dagger2",
"//external/kotlinc:kotlin-annotations",
],
@@ -59,7 +59,7 @@ android_library {
manifest: "AndroidManifest.xml",
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
}
@@ -104,7 +104,7 @@ android_library {
"mockito-target-inline-minus-junit4",
"testables",
"truth-prebuilt",
- "dagger2-2.19",
+ "dagger2",
"//external/kotlinc:kotlin-annotations",
],
libs: [
@@ -118,7 +118,7 @@ android_library {
"com.android.systemui",
],
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
}
android_app {
@@ -157,7 +157,7 @@ android_app {
kotlincflags: ["-Xjvm-default=enable"],
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
required: ["privapp_whitelist_com.android.systemui"],
}
diff --git a/packages/CarSystemUI/TEST_MAPPING b/packages/CarSystemUI/TEST_MAPPING
index f520c8a07a16..54afb9097f87 100644
--- a/packages/CarSystemUI/TEST_MAPPING
+++ b/packages/CarSystemUI/TEST_MAPPING
@@ -18,5 +18,15 @@
}
]
}
+ ],
+ "carsysui-presubmit": [
+ {
+ "name": "CarSystemUITests",
+ "options" : [
+ {
+ "include-annotation": "com.android.systemui.car.CarSystemUiTest"
+ }
+ ]
+ }
]
}
diff --git a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
index 0a294246dfaa..09fbf7a59a8c 100644
--- a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
@@ -15,7 +15,6 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:fitsSystemWindows="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
diff --git a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
index 1782d2536035..5aab0a172b99 100644
--- a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
+++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
@@ -29,13 +29,12 @@
android:orientation="horizontal"
app:layout_constraintGuide_begin="@dimen/headsup_scrim_height"/>
- <!-- Include a FocusParkingView at the beginning or end. The rotary controller "parks" the
- focus here when the user navigates to another window. This is also used to prevent
- wrap-around which is why it must be first or last in Tab order. -->
+ <!-- Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+ when the user navigates to another window. This is also used to prevent wrap-around. -->
<com.android.car.ui.FocusParkingView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<View
diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml
index 0af74c4462a6..0e45e43132de 100644
--- a/packages/CarSystemUI/res/layout/notification_center_activity.xml
+++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml
@@ -22,6 +22,10 @@
android:layout_height="match_parent"
android:background="@color/notification_shade_background_color">
+ <com.android.car.ui.FocusParkingView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
<View
android:id="@+id/glass_pane"
android:layout_width="match_parent"
@@ -33,16 +37,20 @@
app:layout_constraintTop_toTopOf="parent"
/>
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/notifications"
+ <com.android.car.ui.FocusArea
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
- android:paddingBottom="@dimen/notification_shade_list_padding_bottom"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"/>
+ app:layout_constraintTop_toTopOf="parent">
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/notifications"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/notification_shade_list_padding_bottom"/>
+ </com.android.car.ui.FocusArea>
<include layout="@layout/notification_handle_bar"/>
diff --git a/packages/CarSystemUI/res/layout/super_notification_shade.xml b/packages/CarSystemUI/res/layout/super_notification_shade.xml
deleted file mode 100644
index db71c91ca695..000000000000
--- a/packages/CarSystemUI/res/layout/super_notification_shade.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the notification shade window. -->
-<com.android.systemui.statusbar.phone.NotificationShadeWindowView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <com.android.systemui.statusbar.BackDropView
- android:id="@+id/backdrop"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone"
- sysui:ignoreRightInset="true"
- >
- <ImageView android:id="@+id/backdrop_back"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"/>
- <ImageView android:id="@+id/backdrop_front"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:visibility="invisible"/>
- </com.android.systemui.statusbar.BackDropView>
-
- <com.android.systemui.statusbar.ScrimView
- android:id="@+id/scrim_behind"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- sysui:ignoreRightInset="true"
- />
-
- <include layout="@layout/brightness_mirror"/>
-
- <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout="@layout/car_fullscreen_user_switcher"/>
-
- <include layout="@layout/notification_center_activity"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginBottom="@dimen/navigation_bar_height"
- android:visibility="invisible"/>
-
- <include layout="@layout/status_bar_expanded"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible"/>
-
- <com.android.systemui.statusbar.ScrimView
- android:id="@+id/scrim_in_front"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- sysui:ignoreRightInset="true"
- />
-
-</com.android.systemui.statusbar.phone.NotificationShadeWindowView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
deleted file mode 100644
index d93f62f8809d..000000000000
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the status bar window. -->
-<com.android.systemui.statusbar.phone.StatusBarWindowView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- >
- <FrameLayout
- android:id="@+id/status_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- />
-
- <FrameLayout
- android:id="@+id/car_top_navigation_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-
-</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/CarSystemUI/res/layout/system_icons.xml b/packages/CarSystemUI/res/layout/system_icons.xml
index a7dd65eab550..d23579294ce8 100644
--- a/packages/CarSystemUI/res/layout/system_icons.xml
+++ b/packages/CarSystemUI/res/layout/system_icons.xml
@@ -31,10 +31,4 @@
android:gravity="center_vertical"
android:orientation="horizontal"
/>
-
- <com.android.systemui.BatteryMeterView
- android:id="@+id/battery"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- />
</LinearLayout> \ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2dc499c160c6..2c9788955bfa 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -22,12 +22,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <!-- TODO(b/151617493): replace marginBottom with insets. -->
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout="@layout/notification_panel_container"
- android:layout_marginBottom="@dimen/navigation_bar_height"/>
+ android:layout="@layout/notification_panel_container"/>
<ViewStub android:id="@+id/keyguard_stub"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index cb321cdc6c4d..8359dac6a30f 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -81,6 +81,21 @@
<dimen name="car_keyline_2">96dp</dimen>
<dimen name="car_keyline_3">128dp</dimen>
+ <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
+ <dimen name="ongoing_appops_dialog_icon_height">48dp</dimen>
+ <!-- Margin between text lines in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
+ <!-- Padding around Ongoing App Ops dialog content -->
+ <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
+ <!-- Margins around the Ongoing App Ops chip. In landscape, the side margins are 0 -->
+ <dimen name="ongoing_appops_chip_margin">12dp</dimen>
+ <!-- Start and End padding for Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
+ <!-- Padding between background of Ongoing App Ops chip and content -->
+ <dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
+ <!-- Radius of Ongoing App Ops chip corners -->
+ <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
+
<!-- Car volume dimens. -->
<dimen name="car_volume_item_icon_size">@dimen/car_primary_icon_size</dimen>
<dimen name="car_volume_item_height">@*android:dimen/car_single_line_list_item_height</dimen>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index ffdf378959c9..797a178c9a4b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -21,8 +21,6 @@ import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.car.navigationbar.CarNavigationBar;
import com.android.systemui.car.notification.CarNotificationModule;
import com.android.systemui.car.sideloaded.SideLoadedAppController;
-import com.android.systemui.car.statusbar.CarStatusBar;
-import com.android.systemui.car.statusbar.CarStatusBarModule;
import com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier;
import com.android.systemui.car.volume.VolumeUI;
import com.android.systemui.car.window.OverlayWindowModule;
@@ -37,10 +35,10 @@ import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsModule;
import com.android.systemui.shortcut.ShortcutKeyDispatcher;
import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.InstantAppNotifier;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.tv.TvStatusBar;
import com.android.systemui.theme.ThemeOverlayController;
import com.android.systemui.toast.ToastUI;
import com.android.systemui.util.leak.GarbageMonitor;
@@ -51,7 +49,7 @@ import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/** Binder for car specific {@link SystemUI} modules. */
-@Module(includes = {RecentsModule.class, CarStatusBarModule.class, NotificationsModule.class,
+@Module(includes = {RecentsModule.class, StatusBarModule.class, NotificationsModule.class,
BubbleModule.class, KeyguardModule.class, OverlayWindowModule.class,
CarNotificationModule.class})
public abstract class CarSystemUIBinder {
@@ -162,19 +160,7 @@ public abstract class CarSystemUIBinder {
@Binds
@IntoMap
@ClassKey(StatusBar.class)
- public abstract SystemUI bindsStatusBar(CarStatusBar sysui);
-
- /** Inject into TvStatusBar. */
- @Binds
- @IntoMap
- @ClassKey(TvStatusBar.class)
- public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui);
-
- /** Inject into StatusBarGoogle. */
- @Binds
- @IntoMap
- @ClassKey(CarStatusBar.class)
- public abstract SystemUI bindsCarStatusBar(CarStatusBar sysui);
+ public abstract SystemUI bindsStatusBar(StatusBar sysui);
/** Inject into VolumeUI. */
@Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index 1a1b93b33caf..03ea9418415d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -32,7 +32,7 @@ public class CarSystemUIFactory extends SystemUIFactory {
@Override
protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
return DaggerCarSystemUIRootComponent.builder()
- .contextHolder(new ContextHolder(context))
+ .context(context)
.build();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 5bf989a971b9..7b6dceb5fcd7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -22,14 +22,13 @@ import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
import android.content.Context;
import android.os.Handler;
import android.os.PowerManager;
+import android.view.IWindowManager;
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
import com.android.systemui.car.keyguard.CarKeyguardViewController;
-import com.android.systemui.car.statusbar.CarStatusBar;
-import com.android.systemui.car.statusbar.CarStatusBarKeyguardViewManager;
import com.android.systemui.car.statusbar.DozeServiceHost;
import com.android.systemui.car.statusbar.DummyNotificationShadeWindowController;
import com.android.systemui.car.volume.CarVolumeDialogComponent;
@@ -59,14 +58,17 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.ShadeControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.volume.VolumeDialogComponent;
+import com.android.systemui.wm.DisplaySystemBarsController;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -111,6 +113,31 @@ public abstract class CarSystemUIModule {
return new Recents(context, recentsImplementation, commandQueue);
}
+ @Singleton
+ @Provides
+ static TransactionPool provideTransactionPool() {
+ return new TransactionPool();
+ }
+
+ @Singleton
+ @Provides
+ static DisplayController providerDisplayController(Context context, @Main Handler handler,
+ IWindowManager wmService) {
+ return new DisplayController(context, handler, wmService);
+ }
+
+ @Singleton
+ @Provides
+ static SystemWindows provideSystemWindows(DisplayController displayController,
+ IWindowManager wmService) {
+ return new SystemWindows(displayController, wmService);
+ }
+
+ @Singleton
+ @Binds
+ abstract DisplayImeController bindDisplayImeController(
+ DisplaySystemBarsController displaySystemBarsController);
+
@Binds
abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone);
@@ -152,17 +179,10 @@ public abstract class CarSystemUIModule {
CarSystemUIRootComponent systemUIRootComponent);
@Binds
- public abstract StatusBar bindStatusBar(CarStatusBar statusBar);
-
- @Binds
abstract VolumeDialogComponent bindVolumeDialogComponent(
CarVolumeDialogComponent carVolumeDialogComponent);
@Binds
- abstract StatusBarKeyguardViewManager bindStatusBarKeyguardViewManager(
- CarStatusBarKeyguardViewManager keyguardViewManager);
-
- @Binds
abstract KeyguardViewController bindKeyguardViewController(
CarKeyguardViewController carKeyguardViewController);
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index 088420eefa9e..ece3bee000f9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -36,12 +36,17 @@ import dagger.Component;
DependencyBinder.class,
PipModule.class,
OneHandedModule.class,
- SystemUIFactory.ContextHolder.class,
SystemServicesModule.class,
SystemUIModule.class,
CarSystemUIModule.class,
CarSystemUIBinder.class
})
public interface CarSystemUIRootComponent extends SystemUIRootComponent {
-
+ /**
+ * Builder for a CarSystemUIRootComponent.
+ */
+ @Component.Builder
+ interface Builder extends SystemUIRootComponent.Builder {
+ CarSystemUIRootComponent build();
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java b/packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java
new file mode 100644
index 000000000000..5f593b06c511
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarSystemUiTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates that a test class should be run as part of CarSystemUI presubmit
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface CarSystemUiTest {
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 69766cc6c0d0..51a7245ea5c6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -141,6 +141,11 @@ public class CarKeyguardViewController extends OverlayViewController implements
}
@Override
+ protected boolean shouldShowNavigationBar() {
+ return true;
+ }
+
+ @Override
public void onFinishInflate() {
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
index 37dfce4e16ce..35b2080dddf9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
@@ -16,10 +16,12 @@
package com.android.systemui.car.navigationbar;
+import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_GESTURES;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -368,13 +370,15 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("TopCarNavigationBar");
+ lp.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
+ lp.setFitInsetsTypes(0);
lp.windowAnimations = 0;
lp.gravity = Gravity.TOP;
mWindowManager.addView(mTopNavigationBarWindow, lp);
@@ -388,13 +392,14 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("BottomCarNavigationBar");
+ lp.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR, ITYPE_BOTTOM_GESTURES};
lp.windowAnimations = 0;
lp.gravity = Gravity.BOTTOM;
mWindowManager.addView(mBottomNavigationBarWindow, lp);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
index 029d4c7fa2fb..0ced4021ce38 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
@@ -16,7 +16,10 @@
package com.android.systemui.car.navigationbar;
+import static android.view.WindowInsets.Type.systemBars;
+
import android.content.Context;
+import android.graphics.Insets;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -79,9 +82,28 @@ public class CarNavigationBarView extends LinearLayout {
@Override
public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+ applyMargins(windowInsets.getInsets(systemBars()));
return windowInsets;
}
+ private void applyMargins(Insets insets) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getLayoutParams() instanceof LayoutParams) {
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.rightMargin != insets.right || lp.leftMargin != insets.left
+ || lp.topMargin != insets.top || lp.bottomMargin != insets.bottom) {
+ lp.rightMargin = insets.right;
+ lp.leftMargin = insets.left;
+ lp.topMargin = insets.top;
+ lp.bottomMargin = insets.bottom;
+ child.requestLayout();
+ }
+ }
+ }
+ }
+
// Used to forward touch events even if the touch was initiated from a child component
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
index d60bc418ece2..adf8d4d5acf8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
@@ -148,10 +148,9 @@ public class NavigationBarViewFactory {
CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout,
/* root= */ null);
- // Include a FocusParkingView at the end. The rotary controller "parks" the focus here when
- // the user navigates to another window. This is also used to prevent wrap-around which is
- // why it must be first or last in Tab order.
- view.addView(new FocusParkingView(mContext));
+ // Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+ // when the user navigates to another window. This is also used to prevent wrap-around.
+ view.addView(new FocusParkingView(mContext), 0);
mCachedViewMap.put(type, view);
return mCachedViewMap.get(type);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 1eead62c042a..8d5843635e5f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,6 +16,8 @@
package com.android.systemui.car.notification;
+import static android.view.WindowInsets.Type.navigationBars;
+
import android.app.ActivityManager;
import android.car.Car;
import android.car.drivingstate.CarUxRestrictionsManager;
@@ -197,6 +199,16 @@ public class NotificationPanelViewController extends OverlayPanelViewController
}
@Override
+ protected boolean shouldShowStatusBar() {
+ return true;
+ }
+
+ @Override
+ protected int getInsetTypesToFit() {
+ return navigationBars();
+ }
+
+ @Override
protected boolean shouldShowHUN() {
return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java
deleted file mode 100644
index d692487d410e..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.statusbar;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
-import com.android.systemui.InitController;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.CarDeviceProvisionedListener;
-import com.android.systemui.car.bluetooth.CarBatteryController;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.classifier.FalsingLog;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeScrimController;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.volume.VolumeComponent;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-
-import dagger.Lazy;
-
-/**
- * A status bar tailored for the automotive use case.
- */
-public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
- private static final String TAG = "CarStatusBar";
-
- private final UserSwitcherController mUserSwitcherController;
- private final ScrimController mScrimController;
-
- private CarBatteryController mCarBatteryController;
- private BatteryMeterView mBatteryMeterView;
- private Drawable mNotificationPanelBackground;
-
- private final Object mQueueLock = new Object();
- private final CarNavigationBarController mCarNavigationBarController;
- private final CarDeviceProvisionedController mCarDeviceProvisionedController;
- private final ScreenLifecycle mScreenLifecycle;
-
- private boolean mDeviceIsSetUpForUser = true;
- private boolean mIsUserSetupInProgress = false;
-
- public CarStatusBar(
- Context context,
- NotificationsController notificationsController,
- LightBarController lightBarController,
- AutoHideController autoHideController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarIconController statusBarIconController,
- PulseExpansionHandler pulseExpansionHandler,
- NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- KeyguardBypassController keyguardBypassController,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
- DynamicPrivacyController dynamicPrivacyController,
- BypassHeadsUpNotifier bypassHeadsUpNotifier,
- FalsingManager falsingManager,
- BroadcastDispatcher broadcastDispatcher,
- RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
- NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- NotificationViewHierarchyManager notificationViewHierarchyManager,
- KeyguardViewMediator keyguardViewMediator,
- DisplayMetrics displayMetrics,
- MetricsLogger metricsLogger,
- @UiBackground Executor uiBgExecutor,
- NotificationMediaManager notificationMediaManager,
- NotificationLockscreenUserManager lockScreenUserManager,
- NotificationRemoteInputManager remoteInputManager,
- UserSwitcherController userSwitcherController,
- NetworkController networkController,
- BatteryController batteryController,
- SysuiColorExtractor colorExtractor,
- ScreenLifecycle screenLifecycle,
- WakefulnessLifecycle wakefulnessLifecycle,
- SysuiStatusBarStateController statusBarStateController,
- VibratorHelper vibratorHelper,
- BubbleController bubbleController,
- NotificationGroupManager groupManager,
- VisualStabilityManager visualStabilityManager,
- CarDeviceProvisionedController carDeviceProvisionedController,
- NavigationBarController navigationBarController,
- Lazy<AssistManager> assistManagerLazy,
- ConfigurationController configurationController,
- NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
- DozeParameters dozeParameters,
- ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
- Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
- DozeServiceHost dozeServiceHost,
- PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
- DozeScrimController dozeScrimController,
- VolumeComponent volumeComponent,
- CommandQueue commandQueue,
- Optional<Recents> recents,
- Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
- PluginManager pluginManager,
- Optional<Divider> dividerOptional,
- SuperStatusBarViewFactory superStatusBarViewFactory,
- LightsOutNotifController lightsOutNotifController,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
- ShadeController shadeController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- ViewMediatorCallback viewMediatorCallback,
- InitController initController,
- DarkIconDispatcher darkIconDispatcher,
- @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
- PluginDependencyProvider pluginDependencyProvider,
- KeyguardDismissUtil keyguardDismissUtil,
- ExtensionController extensionController,
- UserInfoControllerImpl userInfoControllerImpl,
- PhoneStatusBarPolicy phoneStatusBarPolicy,
- KeyguardIndicationController keyguardIndicationController,
- DismissCallbackRegistry dismissCallbackRegistry,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
- Lazy<NotificationShadeDepthController> depthControllerLazy,
- /* Car Settings injected components. */
- CarNavigationBarController carNavigationBarController) {
- super(
- context,
- notificationsController,
- lightBarController,
- autoHideController,
- keyguardUpdateMonitor,
- statusBarIconController,
- pulseExpansionHandler,
- notificationWakeUpCoordinator,
- keyguardBypassController,
- keyguardStateController,
- headsUpManagerPhone,
- dynamicPrivacyController,
- bypassHeadsUpNotifier,
- falsingManager,
- broadcastDispatcher,
- remoteInputQuickSettingsDisabler,
- notificationGutsManager,
- notificationLogger,
- notificationInterruptStateProvider,
- notificationViewHierarchyManager,
- keyguardViewMediator,
- displayMetrics,
- metricsLogger,
- uiBgExecutor,
- notificationMediaManager,
- lockScreenUserManager,
- remoteInputManager,
- userSwitcherController,
- networkController,
- batteryController,
- colorExtractor,
- screenLifecycle,
- wakefulnessLifecycle,
- statusBarStateController,
- vibratorHelper,
- bubbleController,
- groupManager,
- visualStabilityManager,
- carDeviceProvisionedController,
- navigationBarController,
- assistManagerLazy,
- configurationController,
- notificationShadeWindowController,
- lockscreenLockIconController,
- dozeParameters,
- scrimController,
- null /* keyguardLiftController */,
- lockscreenWallpaperLazy,
- biometricUnlockControllerLazy,
- dozeServiceHost,
- powerManager,
- screenPinningRequest,
- dozeScrimController,
- volumeComponent,
- commandQueue,
- recents,
- statusBarComponentBuilder,
- pluginManager,
- dividerOptional,
- lightsOutNotifController,
- statusBarNotificationActivityStarterBuilder,
- shadeController,
- superStatusBarViewFactory,
- statusBarKeyguardViewManager,
- viewMediatorCallback,
- initController,
- darkIconDispatcher,
- timeTickHandler,
- pluginDependencyProvider,
- keyguardDismissUtil,
- extensionController,
- userInfoControllerImpl,
- phoneStatusBarPolicy,
- keyguardIndicationController,
- dismissCallbackRegistry,
- depthControllerLazy,
- statusBarTouchableRegionManager);
- mUserSwitcherController = userSwitcherController;
- mScrimController = scrimController;
- mCarDeviceProvisionedController = carDeviceProvisionedController;
- mCarNavigationBarController = carNavigationBarController;
- mScreenLifecycle = screenLifecycle;
- }
-
- @Override
- public void start() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
-
- super.start();
-
- createBatteryController();
- mCarBatteryController.startListening();
-
- mCarDeviceProvisionedController.addCallback(
- new CarDeviceProvisionedListener() {
- @Override
- public void onUserSetupInProgressChanged() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController
- .isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController
- .isCurrentUserSetupInProgress();
- }
-
- @Override
- public void onUserSetupChanged() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController
- .isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController
- .isCurrentUserSetupInProgress();
- }
-
- @Override
- public void onUserSwitched() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController
- .isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController
- .isCurrentUserSetupInProgress();
- }
- });
-
- mNotificationInterruptStateProvider.addSuppressor(new NotificationInterruptSuppressor() {
- @Override
- public String getName() {
- return TAG;
- }
-
- @Override
- public boolean suppressInterruptions(NotificationEntry entry) {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded.
- // Ensure this by not allowing any interruptions (ie: pinning any notifications) if
- // the shade is already opened.
- return !getPresenter().isPresenterFullyCollapsed();
- }
- });
- }
-
- @Override
- public boolean hideKeyguard() {
- boolean result = super.hideKeyguard();
- mCarNavigationBarController.hideAllKeyguardButtons(isDeviceSetupForUser());
- return result;
- }
-
- @Override
- public void showKeyguard() {
- super.showKeyguard();
- mCarNavigationBarController.showAllKeyguardButtons(isDeviceSetupForUser());
- }
-
- private boolean isDeviceSetupForUser() {
- return mDeviceIsSetUpForUser && !mIsUserSetupInProgress;
- }
-
- @Override
- protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
- super.makeStatusBarView(result);
-
- mNotificationPanelBackground = getDefaultWallpaper();
- mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
-
- FragmentHostManager manager = FragmentHostManager.get(mPhoneStatusBarWindow);
- manager.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
- mBatteryMeterView = fragment.getView().findViewById(R.id.battery);
-
- // By default, the BatteryMeterView should not be visible. It will be toggled
- // when a device has connected by bluetooth.
- mBatteryMeterView.setVisibility(View.GONE);
- });
- }
-
- @Override
- public void animateExpandNotificationsPanel() {
- // No op.
- }
-
- @Override
- protected QS createDefaultQSFragment() {
- return null;
- }
-
- private BatteryController createBatteryController() {
- mCarBatteryController = new CarBatteryController(mContext);
- mCarBatteryController.addBatteryViewHandler(this);
- return mCarBatteryController;
- }
-
- @Override
- protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
- // No op.
- }
-
- @Override
- public void notifyBiometricAuthModeChanged() {
- // No op.
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- //When executing dump() function simultaneously, we need to serialize them
- //to get mStackScroller's position correctly.
- synchronized (mQueueLock) {
- pw.println(" mStackScroller: " + viewInfo(mStackScroller));
- pw.println(" mStackScroller: " + viewInfo(mStackScroller)
- + " scroll " + mStackScroller.getScrollX()
- + "," + mStackScroller.getScrollY());
- }
- pw.print(" mCarBatteryController=");
- pw.println(mCarBatteryController);
- pw.print(" mBatteryMeterView=");
- pw.println(mBatteryMeterView);
-
- if (Dependency.get(KeyguardUpdateMonitor.class) != null) {
- Dependency.get(KeyguardUpdateMonitor.class).dump(fd, pw, args);
- }
-
- FalsingLog.dump(pw);
-
- pw.println("SharedPreferences:");
- for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
- pw.print(" ");
- pw.print(entry.getKey());
- pw.print("=");
- pw.println(entry.getValue());
- }
- }
-
- @Override
- public void showBatteryView() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "showBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
- }
-
- if (mBatteryMeterView != null) {
- mBatteryMeterView.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void hideBatteryView() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "hideBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
- }
-
- if (mBatteryMeterView != null) {
- mBatteryMeterView.setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void createUserSwitcher() {
- if (!mUserSwitcherController.useFullscreenUserSwitcher()) {
- super.createUserSwitcher();
- }
- }
-
- /**
- * Dismisses the keyguard and shows bouncer if authentication is necessary.
- */
- public void dismissKeyguard() {
- // Don't dismiss keyguard when the screen is off.
- if (mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF) {
- return;
- }
- executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
- true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
- }
-
- /**
- * Ensures that relevant child views are appropriately recreated when the device's density
- * changes.
- */
- @Override
- public void onDensityOrFontScaleChanged() {
- super.onDensityOrFontScaleChanged();
- // Need to update the background on density changed in case the change was due to night
- // mode.
- mNotificationPanelBackground = getDefaultWallpaper();
- mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
- }
-
- /**
- * Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
- */
- private Drawable getDefaultWallpaper() {
- return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java
deleted file mode 100644
index 96a998a500e1..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.statusbar;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.R;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Car implementation of the {@link StatusBarKeyguardViewManager}. */
-@Singleton
-public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
-
- protected boolean mShouldHideNavBar;
- private final CarNavigationBarController mCarNavigationBarController;
- private Set<OnKeyguardCancelClickedListener> mKeygaurdCancelClickedListenerSet;
-
- @Inject
- public CarStatusBarKeyguardViewManager(Context context,
- ViewMediatorCallback callback,
- LockPatternUtils lockPatternUtils,
- SysuiStatusBarStateController sysuiStatusBarStateController,
- ConfigurationController configurationController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- NavigationModeController navigationModeController,
- DockManager dockManager,
- NotificationShadeWindowController notificationShadeWindowController,
- KeyguardStateController keyguardStateController,
- NotificationMediaManager notificationMediaManager,
- CarNavigationBarController carNavigationBarController) {
- super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
- configurationController, keyguardUpdateMonitor, navigationModeController,
- dockManager, notificationShadeWindowController, keyguardStateController,
- notificationMediaManager);
- mShouldHideNavBar = context.getResources()
- .getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
- mCarNavigationBarController = carNavigationBarController;
- mKeygaurdCancelClickedListenerSet = new HashSet<>();
- }
-
- @Override
- protected void updateNavigationBarVisibility(boolean navBarVisible) {
- if (!mShouldHideNavBar) {
- return;
- }
- int visibility = navBarVisible ? View.VISIBLE : View.GONE;
- mCarNavigationBarController.setBottomWindowVisibility(visibility);
- mCarNavigationBarController.setLeftWindowVisibility(visibility);
- mCarNavigationBarController.setRightWindowVisibility(visibility);
- }
-
- /**
- * Car is a multi-user system. There's a cancel button on the bouncer that allows the user to
- * go back to the user switcher and select another user. Different user may have different
- * security mode which requires bouncer container to be resized. For this reason, the bouncer
- * view is destroyed on cancel.
- */
- @Override
- protected boolean shouldDestroyViewOnReset() {
- return true;
- }
-
- /**
- * Called when cancel button in bouncer is pressed.
- */
- @Override
- public void onCancelClicked() {
- mKeygaurdCancelClickedListenerSet.forEach(OnKeyguardCancelClickedListener::onCancelClicked);
- }
-
- /**
- * Do nothing on this change.
- * The base class hides the keyguard which for automotive we want to avoid b/c this would happen
- * on a configuration change due to day/night (headlight state).
- */
- @Override
- public void onDensityOrFontScaleChanged() { }
-
- /**
- * Add listener for keyguard cancel clicked.
- */
- public void addOnKeyguardCancelClickedListener(
- OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
- mKeygaurdCancelClickedListenerSet.add(keyguardCancelClickedListener);
- }
-
- /**
- * Remove listener for keyguard cancel clicked.
- */
- public void removeOnKeyguardCancelClickedListener(
- OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
- mKeygaurdCancelClickedListenerSet.remove(keyguardCancelClickedListener);
- }
-
-
- /**
- * Defines a callback for keyguard cancel button clicked listeners.
- */
- public interface OnKeyguardCancelClickedListener {
- /**
- * Called when keyguard cancel button is clicked.
- */
- void onCancelClicked();
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java
deleted file mode 100644
index dc2eb04c2990..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.statusbar;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.InitController;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeScrimController;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneDependenciesModule;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.volume.VolumeComponent;
-
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-
-import dagger.Lazy;
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * Dagger Module providing {@link CarStatusBar}.
- */
-@Module(includes = {StatusBarDependenciesModule.class, StatusBarPhoneDependenciesModule.class,
- NotificationRowModule.class})
-public class CarStatusBarModule {
- /**
- * Provides our instance of StatusBar which is considered optional.
- */
- @Provides
- @Singleton
- static CarStatusBar provideStatusBar(
- Context context,
- NotificationsController notificationsController,
- LightBarController lightBarController,
- AutoHideController autoHideController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarIconController statusBarIconController,
- PulseExpansionHandler pulseExpansionHandler,
- NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- KeyguardBypassController keyguardBypassController,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
- DynamicPrivacyController dynamicPrivacyController,
- BypassHeadsUpNotifier bypassHeadsUpNotifier,
- FalsingManager falsingManager,
- BroadcastDispatcher broadcastDispatcher,
- RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
- NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
- NotificationInterruptStateProvider notificationInterruptionStateProvider,
- NotificationViewHierarchyManager notificationViewHierarchyManager,
- KeyguardViewMediator keyguardViewMediator,
- DisplayMetrics displayMetrics,
- MetricsLogger metricsLogger,
- @UiBackground Executor uiBgExecutor,
- NotificationMediaManager notificationMediaManager,
- NotificationLockscreenUserManager lockScreenUserManager,
- NotificationRemoteInputManager remoteInputManager,
- UserSwitcherController userSwitcherController,
- NetworkController networkController,
- BatteryController batteryController,
- SysuiColorExtractor colorExtractor,
- ScreenLifecycle screenLifecycle,
- WakefulnessLifecycle wakefulnessLifecycle,
- SysuiStatusBarStateController statusBarStateController,
- VibratorHelper vibratorHelper,
- BubbleController bubbleController,
- NotificationGroupManager groupManager,
- VisualStabilityManager visualStabilityManager,
- CarDeviceProvisionedController carDeviceProvisionedController,
- NavigationBarController navigationBarController,
- Lazy<AssistManager> assistManagerLazy,
- ConfigurationController configurationController,
- NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
- DozeParameters dozeParameters,
- ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
- Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
- DozeServiceHost dozeServiceHost,
- PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
- DozeScrimController dozeScrimController,
- VolumeComponent volumeComponent,
- CommandQueue commandQueue,
- Optional<Recents> recentsOptional,
- Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
- PluginManager pluginManager,
- Optional<Divider> dividerOptional,
- SuperStatusBarViewFactory superStatusBarViewFactory,
- LightsOutNotifController lightsOutNotifController,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
- ShadeController shadeController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- ViewMediatorCallback viewMediatorCallback,
- InitController initController,
- DarkIconDispatcher darkIconDispatcher,
- @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
- PluginDependencyProvider pluginDependencyProvider,
- KeyguardDismissUtil keyguardDismissUtil,
- ExtensionController extensionController,
- UserInfoControllerImpl userInfoControllerImpl,
- PhoneStatusBarPolicy phoneStatusBarPolicy,
- KeyguardIndicationController keyguardIndicationController,
- DismissCallbackRegistry dismissCallbackRegistry,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
- Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
- CarNavigationBarController carNavigationBarController) {
- return new CarStatusBar(
- context,
- notificationsController,
- lightBarController,
- autoHideController,
- keyguardUpdateMonitor,
- statusBarIconController,
- pulseExpansionHandler,
- notificationWakeUpCoordinator,
- keyguardBypassController,
- keyguardStateController,
- headsUpManagerPhone,
- dynamicPrivacyController,
- bypassHeadsUpNotifier,
- falsingManager,
- broadcastDispatcher,
- remoteInputQuickSettingsDisabler,
- notificationGutsManager,
- notificationLogger,
- notificationInterruptionStateProvider,
- notificationViewHierarchyManager,
- keyguardViewMediator,
- displayMetrics,
- metricsLogger,
- uiBgExecutor,
- notificationMediaManager,
- lockScreenUserManager,
- remoteInputManager,
- userSwitcherController,
- networkController,
- batteryController,
- colorExtractor,
- screenLifecycle,
- wakefulnessLifecycle,
- statusBarStateController,
- vibratorHelper,
- bubbleController,
- groupManager,
- visualStabilityManager,
- carDeviceProvisionedController,
- navigationBarController,
- assistManagerLazy,
- configurationController,
- notificationShadeWindowController,
- lockscreenLockIconController,
- dozeParameters,
- scrimController,
- lockscreenWallpaperLazy,
- biometricUnlockControllerLazy,
- dozeServiceHost,
- powerManager,
- screenPinningRequest,
- dozeScrimController,
- volumeComponent,
- commandQueue,
- recentsOptional,
- statusBarComponentBuilder,
- pluginManager,
- dividerOptional,
- superStatusBarViewFactory,
- lightsOutNotifController,
- statusBarNotificationActivityStarterBuilder,
- shadeController,
- statusBarKeyguardViewManager,
- viewMediatorCallback,
- initController,
- darkIconDispatcher,
- timeTickHandler,
- pluginDependencyProvider,
- keyguardDismissUtil,
- extensionController,
- userInfoControllerImpl,
- phoneStatusBarPolicy,
- keyguardIndicationController,
- dismissCallbackRegistry,
- statusBarTouchableRegionManager,
- notificationShadeDepthControllerLazy,
- carNavigationBarController);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
index a4230032858e..13f2b7ed45db 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
@@ -23,6 +23,7 @@ import android.view.WindowManager;
import com.android.systemui.car.window.SystemUIOverlayWindowController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -49,12 +50,14 @@ public class DummyNotificationShadeWindowController extends NotificationShadeWin
DozeParameters dozeParameters,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
+ KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController,
SysuiColorExtractor colorExtractor,
DumpManager dumpManager,
SystemUIOverlayWindowController overlayWindowController) {
super(context, windowManager, activityManager, dozeParameters, statusBarStateController,
- configurationController, keyguardBypassController, colorExtractor, dumpManager);
+ configurationController, keyguardViewMediator, keyguardBypassController,
+ colorExtractor, dumpManager);
mOverlayWindowController = overlayWindowController;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarStatusBarHeader.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarStatusBarHeader.java
index bab67154e75d..0a677bfaa742 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarStatusBarHeader.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarStatusBarHeader.java
@@ -26,7 +26,6 @@ import android.widget.LinearLayout;
import androidx.annotation.IdRes;
import com.android.settingslib.Utils;
-import com.android.systemui.BatteryMeterView;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -49,10 +48,7 @@ public class CarStatusBarHeader extends LinearLayout {
float intensity = colorForeground == Color.WHITE ? 0f : 1f;
Rect tintArea = new Rect(0, 0, 0, 0);
- applyDarkness(R.id.battery, tintArea, intensity, colorForeground);
applyDarkness(R.id.clock, tintArea, intensity, colorForeground);
-
- ((BatteryMeterView) findViewById(R.id.battery)).setForceShowPercent(true);
}
private void applyDarkness(@IdRes int id, Rect tintArea, float intensity, int color) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java
index 10b2b973071a..1a8f19e46798 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java
@@ -18,6 +18,8 @@ package com.android.systemui.car.userswitcher;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.car.Car;
+import android.car.user.CarUserManager;
import android.content.Context;
import android.content.res.Resources;
import android.view.View;
@@ -25,6 +27,7 @@ import android.view.View;
import androidx.recyclerview.widget.GridLayoutManager;
import com.android.systemui.R;
+import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.car.window.OverlayViewController;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import com.android.systemui.dagger.qualifiers.Main;
@@ -39,7 +42,9 @@ import javax.inject.Singleton;
public class FullScreenUserSwitcherViewController extends OverlayViewController {
private final Context mContext;
private final Resources mResources;
+ private final CarServiceProvider mCarServiceProvider;
private final int mShortAnimationDuration;
+ private CarUserManager mCarUserManager;
private UserGridRecyclerView mUserGridView;
private UserGridRecyclerView.UserSelectionListener mUserSelectionListener;
@@ -47,10 +52,16 @@ public class FullScreenUserSwitcherViewController extends OverlayViewController
public FullScreenUserSwitcherViewController(
Context context,
@Main Resources resources,
+ CarServiceProvider carServiceProvider,
OverlayViewGlobalStateController overlayViewGlobalStateController) {
super(R.id.fullscreen_user_switcher_stub, overlayViewGlobalStateController);
mContext = context;
mResources = resources;
+ mCarServiceProvider = carServiceProvider;
+ mCarServiceProvider.addListener(car -> {
+ mCarUserManager = (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE);
+ registerCarUserManagerIfPossible();
+ });
mShortAnimationDuration = mResources.getInteger(android.R.integer.config_shortAnimTime);
}
@@ -63,6 +74,7 @@ public class FullScreenUserSwitcherViewController extends OverlayViewController
mUserGridView.setLayoutManager(layoutManager);
mUserGridView.buildAdapter();
mUserGridView.setUserSelectionListener(mUserSelectionListener);
+ registerCarUserManagerIfPossible();
}
@Override
@@ -91,18 +103,6 @@ public class FullScreenUserSwitcherViewController extends OverlayViewController
}
/**
- * Invalidate underlying view.
- */
- void invalidate() {
- if (getLayout() == null) {
- // layout hasn't been inflated.
- return;
- }
-
- getLayout().invalidate();
- }
-
- /**
* Set {@link UserGridRecyclerView.UserSelectionListener}.
*/
void setUserGridSelectionListener(
@@ -110,15 +110,9 @@ public class FullScreenUserSwitcherViewController extends OverlayViewController
mUserSelectionListener = userGridSelectionListener;
}
- /**
- * Returns {@code true} when layout is visible.
- */
- boolean isVisible() {
- if (getLayout() == null) {
- // layout hasn't been inflated.
- return false;
+ private void registerCarUserManagerIfPossible() {
+ if (mUserGridView != null && mCarUserManager != null) {
+ mUserGridView.setCarUserManager(mCarUserManager);
}
-
- return getLayout().getVisibility() == View.VISIBLE;
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
index 2ff667093e58..d0a2aebdb80a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
@@ -24,11 +24,15 @@ import static android.view.WindowInsets.Type.statusBars;
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
-import android.car.userlib.CarUserManagerHelper;
+import android.car.user.CarUserManager;
+import android.car.user.UserCreationResult;
+import android.car.user.UserSwitchResult;
+import android.car.userlib.UserHelper;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -40,7 +44,9 @@ import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.UserHandle;
import android.os.UserManager;
+import android.sysprop.CarProperties;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -54,6 +60,7 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.UserIcons;
import com.android.systemui.R;
@@ -61,6 +68,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -68,9 +76,12 @@ import java.util.stream.Collectors;
* One of the uses of this is for the lock screen in auto.
*/
public class UserGridRecyclerView extends RecyclerView {
+ private static final String TAG = UserGridRecyclerView.class.getSimpleName();
+ private static final int TIMEOUT_MS = CarProperties.user_hal_timeout().orElse(5_000) + 500;
+
private UserSelectionListener mUserSelectionListener;
private UserAdapter mAdapter;
- private CarUserManagerHelper mCarUserManagerHelper;
+ private CarUserManager mCarUserManager;
private UserManager mUserManager;
private Context mContext;
private UserIconProvider mUserIconProvider;
@@ -85,7 +96,6 @@ public class UserGridRecyclerView extends RecyclerView {
public UserGridRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
- mCarUserManagerHelper = new CarUserManagerHelper(mContext);
mUserManager = UserManager.get(mContext);
mUserIconProvider = new UserIconProvider();
@@ -184,6 +194,11 @@ public class UserGridRecyclerView extends RecyclerView {
mUserSelectionListener = userSelectionListener;
}
+ /** Sets a {@link CarUserManager}. */
+ public void setCarUserManager(CarUserManager carUserManager) {
+ mCarUserManager = carUserManager;
+ }
+
private void onUsersUpdate() {
mAdapter.clearUsers();
mAdapter.updateUsers(createUserRecords(getUsersForUserGrid()));
@@ -273,7 +288,9 @@ public class UserGridRecyclerView extends RecyclerView {
notifyUserSelected(userRecord);
UserInfo guest = createNewOrFindExistingGuest(mContext);
if (guest != null) {
- mCarUserManagerHelper.switchToUser(guest);
+ if (!switchUser(guest.id)) {
+ Log.e(TAG, "Failed to switch to guest user: " + guest.id);
+ }
}
break;
case UserRecord.ADD_USER:
@@ -289,7 +306,9 @@ public class UserGridRecyclerView extends RecyclerView {
// If the user doesn't want to be a guest or add a user, switch to the user
// selected
notifyUserSelected(userRecord);
- mCarUserManagerHelper.switchToUser(userRecord.mInfo);
+ if (!switchUser(userRecord.mInfo.id)) {
+ Log.e(TAG, "Failed to switch users: " + userRecord.mInfo.id);
+ }
}
});
@@ -430,8 +449,9 @@ public class UserGridRecyclerView extends RecyclerView {
*/
@Nullable
public UserInfo createNewOrFindExistingGuest(Context context) {
+ AndroidFuture<UserCreationResult> future = mCarUserManager.createGuest(mGuestName);
// CreateGuest will return null if a guest already exists.
- UserInfo newGuest = mUserManager.createGuest(context, mGuestName);
+ UserInfo newGuest = getUserInfo(future);
if (newGuest != null) {
new UserIconProvider().assignDefaultIcon(
mUserManager, context.getResources(), newGuest);
@@ -444,7 +464,6 @@ public class UserGridRecyclerView extends RecyclerView {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == BUTTON_POSITIVE) {
- notifyUserSelected(mAddUserRecord);
new AddNewUserTask().execute(mNewUserName);
} else if (which == BUTTON_NEGATIVE) {
// Enable the add button only if cancel
@@ -462,11 +481,77 @@ public class UserGridRecyclerView extends RecyclerView {
}
}
+ @Nullable
+ private UserInfo getUserInfo(AndroidFuture<UserCreationResult> future) {
+ UserCreationResult userCreationResult;
+ try {
+ userCreationResult = future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Log.w(TAG, "Could not create user.", e);
+ return null;
+ }
+
+ if (userCreationResult == null) {
+ Log.w(TAG, "Timed out while creating user: " + TIMEOUT_MS + "ms");
+ return null;
+ }
+ if (!userCreationResult.isSuccess() || userCreationResult.getUser() == null) {
+ Log.w(TAG, "Could not create user: " + userCreationResult);
+ return null;
+ }
+
+ return userCreationResult.getUser();
+ }
+
+ private boolean switchUser(@UserIdInt int userId) {
+ AndroidFuture<UserSwitchResult> userSwitchResultFuture =
+ mCarUserManager.switchUser(userId);
+ UserSwitchResult userSwitchResult;
+ try {
+ userSwitchResult = userSwitchResultFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Log.w(TAG, "Could not switch user.", e);
+ return false;
+ }
+
+ if (userSwitchResult == null) {
+ Log.w(TAG, "Timed out while switching user: " + TIMEOUT_MS + "ms");
+ return false;
+ }
+ if (!userSwitchResult.isSuccess()) {
+ Log.w(TAG, "Could not switch user: " + userSwitchResult);
+ return false;
+ }
+
+ return true;
+ }
+
+ // TODO(b/161539497): Replace AsyncTask with standard {@link java.util.concurrent} code.
private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
@Override
protected UserInfo doInBackground(String... userNames) {
- return mCarUserManagerHelper.createNewNonAdminUser(userNames[0]);
+ AndroidFuture<UserCreationResult> future = mCarUserManager.createUser(userNames[0],
+ /* flags= */ 0);
+ try {
+ UserInfo user = getUserInfo(future);
+ if (user != null) {
+ UserHelper.setDefaultNonAdminRestrictions(mContext, user,
+ /* enable= */ true);
+ UserHelper.assignDefaultIcon(mContext, user);
+ mAddUserRecord = new UserRecord(user, UserRecord.ADD_USER);
+ return user;
+ } else {
+ Log.e(TAG, "Failed to create user in the background");
+ return user;
+ }
+ } catch (Exception e) {
+ if (e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ Log.e(TAG, "Error creating new user: ", e);
+ }
+ return null;
}
@Override
@@ -476,7 +561,11 @@ public class UserGridRecyclerView extends RecyclerView {
@Override
protected void onPostExecute(UserInfo user) {
if (user != null) {
- mCarUserManagerHelper.switchToUser(user);
+ notifyUserSelected(mAddUserRecord);
+ mAddUserView.setEnabled(true);
+ if (!switchUser(user.id)) {
+ Log.e(TAG, "Failed to switch to new user: " + user.id);
+ }
}
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 45f3d342fb6e..0d77c1341ffb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -91,6 +91,11 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
R.integer.config_userSwitchTransitionViewShownTimeoutMs);
}
+ @Override
+ protected int getInsetTypesToFit() {
+ return 0;
+ }
+
/**
* Makes the user switch transition view appear and draws the content inside of it if a user
* that is different from the previous user is provided and if the dialog is not already
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 3969f92c690a..53deb9d9dc5d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -16,9 +16,12 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.statusBars;
+
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
/**
* Owns a {@link View} that is present in SystemUIOverlayWindow.
@@ -140,9 +143,25 @@ public class OverlayViewController {
}
/**
+ * Returns {@code true} if status bar should be displayed over this view.
+ */
+ protected boolean shouldShowStatusBar() {
+ return false;
+ }
+
+ /**
* Returns {@code true} if this view should be hidden during the occluded state.
*/
protected boolean shouldShowWhenOccluded() {
return false;
}
+
+ /**
+ * Returns the insets types to fit to the sysui overlay window when this
+ * {@link OverlayViewController} is in the foreground.
+ */
+ @WindowInsets.Type.InsetsType
+ protected int getInsetTypesToFit() {
+ return statusBars();
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 8e9410964313..2494242c24f0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -16,13 +16,17 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
import android.annotation.Nullable;
import android.util.Log;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsController;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -48,10 +52,7 @@ public class OverlayViewGlobalStateController {
private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName();
private static final int UNKNOWN_Z_ORDER = -1;
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
- private final CarNavigationBarController mCarNavigationBarController;
-
- private boolean mIsOccluded;
-
+ private final WindowInsetsController mWindowInsetsController;
@VisibleForTesting
Map<OverlayViewController, Integer> mZOrderMap;
@VisibleForTesting
@@ -60,14 +61,15 @@ public class OverlayViewGlobalStateController {
Set<OverlayViewController> mViewsHiddenForOcclusion;
@VisibleForTesting
OverlayViewController mHighestZOrder;
+ private boolean mIsOccluded;
@Inject
public OverlayViewGlobalStateController(
- CarNavigationBarController carNavigationBarController,
SystemUIOverlayWindowController systemUIOverlayWindowController) {
mSystemUIOverlayWindowController = systemUIOverlayWindowController;
mSystemUIOverlayWindowController.attach();
- mCarNavigationBarController = carNavigationBarController;
+ mWindowInsetsController =
+ mSystemUIOverlayWindowController.getBaseLayout().getWindowInsetsController();
mZOrderMap = new HashMap<>();
mZOrderVisibleSortedMap = new TreeMap<>();
mViewsHiddenForOcclusion = new HashSet<>();
@@ -115,7 +117,9 @@ public class OverlayViewGlobalStateController {
}
updateInternalsWhenShowingView(viewController);
+ refreshInsetTypesToFit();
refreshNavigationBarVisibility();
+ refreshStatusBarVisibility();
Log.d(TAG, "Content shown: " + viewController.getClass().getName());
debugLog();
@@ -185,7 +189,9 @@ public class OverlayViewGlobalStateController {
mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
refreshHighestZOrderWhenHidingView(viewController);
+ refreshInsetTypesToFit();
refreshNavigationBarVisibility();
+ refreshStatusBarVisibility();
if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(false);
@@ -208,10 +214,28 @@ public class OverlayViewGlobalStateController {
}
private void refreshNavigationBarVisibility() {
+ mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) {
- mCarNavigationBarController.showBars();
+ mWindowInsetsController.show(navigationBars());
} else {
- mCarNavigationBarController.hideBars();
+ mWindowInsetsController.hide(navigationBars());
+ }
+ }
+
+ private void refreshStatusBarVisibility() {
+ mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
+ if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowStatusBar()) {
+ mWindowInsetsController.show(statusBars());
+ } else {
+ mWindowInsetsController.hide(statusBars());
+ }
+ }
+
+ private void refreshInsetTypesToFit() {
+ if (mZOrderVisibleSortedMap.isEmpty()) {
+ setFitInsetsTypes(statusBars());
+ } else {
+ setFitInsetsTypes(mHighestZOrder.getInsetTypesToFit());
}
}
@@ -224,6 +248,10 @@ public class OverlayViewGlobalStateController {
mSystemUIOverlayWindowController.setWindowVisible(visible);
}
+ private void setFitInsetsTypes(@InsetsType int types) {
+ mSystemUIOverlayWindowController.setFitInsetsTypes(types);
+ }
+
/**
* Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
* sysui overlay window.
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
index bcd96f63a2b4..029bd3702afe 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
@@ -25,6 +25,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowManager;
import com.android.systemui.R;
@@ -99,7 +100,6 @@ public class SystemUIOverlayWindowController implements
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
- mLp.setFitInsetsTypes(/* types= */ 0);
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("SystemUIOverlayWindow");
mLp.packageName = mContext.getPackageName();
@@ -110,6 +110,12 @@ public class SystemUIOverlayWindowController implements
setWindowVisible(false);
}
+ /** Sets the types of insets to fit. Note: This should be rarely used. */
+ public void setFitInsetsTypes(@WindowInsets.Type.InsetsType int types) {
+ mLpChanged.setFitInsetsTypes(types);
+ updateWindow();
+ }
+
/** Sets the window to the visible state. */
public void setWindowVisible(boolean visible) {
mVisible = visible;
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
new file mode 100644
index 000000000000..5f9665ff7632
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wm;
+
+import android.car.settings.CarSettings;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.view.WindowInsets;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Util class to load PolicyControl and allow for querying if a package matches immersive filters.
+ * Similar to {@link com.android.server.wm.PolicyControl}, but separate due to CarSystemUI needing
+ * to set its own policies for system bar visibilities.
+ *
+ * This forces immersive mode behavior for one or both system bars (based on a package
+ * list).
+ *
+ * Control by setting {@link Settings.Global#POLICY_CONTROL_AUTO} to one or more name-value pairs.
+ * e.g.
+ * to force immersive mode everywhere:
+ * "immersive.full=*"
+ * to force hide status bars for com.package1 but not com.package2:
+ * "immersive.status=com.package1,-com.package2"
+ *
+ * Separate multiple name-value pairs with ':'
+ * e.g. "immersive.status=com.package:immersive.navigation=*"
+ */
+public class BarControlPolicy {
+
+ private static final String TAG = "BarControlPolicy";
+ private static final boolean DEBUG = false;
+
+ private static final String NAME_IMMERSIVE_FULL = "immersive.full";
+ private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
+ private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation";
+
+ @VisibleForTesting
+ static String sSettingValue;
+ @VisibleForTesting
+ static Filter sImmersiveStatusFilter;
+ private static Filter sImmersiveNavigationFilter;
+
+ /** Loads values from the POLICY_CONTROL setting to set filters. */
+ static boolean reloadFromSetting(Context context) {
+ if (DEBUG) Slog.d(TAG, "reloadFromSetting()");
+ String value = null;
+ try {
+ value = Settings.Global.getStringForUser(context.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ UserHandle.USER_CURRENT);
+ if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) {
+ return false;
+ }
+ setFilters(value);
+ sSettingValue = value;
+ } catch (Throwable t) {
+ Slog.w(TAG, "Error loading policy control, value=" + value, t);
+ return false;
+ }
+ return true;
+ }
+
+ /** Used in testing to reset BarControlPolicy. */
+ @VisibleForTesting
+ static void reset() {
+ sSettingValue = null;
+ sImmersiveStatusFilter = null;
+ sImmersiveNavigationFilter = null;
+ }
+
+ /**
+ * Registers a content observer to listen to updates to the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
+ */
+ static void registerContentObserver(Context context, Handler handler, FilterListener listener) {
+ context.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE), false,
+ new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (reloadFromSetting(context)) {
+ listener.onFilterUpdated();
+ }
+ }
+ }, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Returns bar visibilities based on POLICY_CONTROL_AUTO filters and window policies.
+ * @return int[], where the first value is the inset types that should be shown, and the second
+ * is the inset types that should be hidden.
+ */
+ @WindowInsets.Type.InsetsType
+ static int[] getBarVisibilities(String packageName) {
+ int hideTypes = 0;
+ int showTypes = 0;
+ if (matchesStatusFilter(packageName)) {
+ hideTypes |= WindowInsets.Type.statusBars();
+ } else {
+ showTypes |= WindowInsets.Type.statusBars();
+ }
+ if (matchesNavigationFilter(packageName)) {
+ hideTypes |= WindowInsets.Type.navigationBars();
+ } else {
+ showTypes |= WindowInsets.Type.navigationBars();
+ }
+
+ return new int[] {showTypes, hideTypes};
+ }
+
+ private static boolean matchesStatusFilter(String packageName) {
+ return sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(packageName);
+ }
+
+ private static boolean matchesNavigationFilter(String packageName) {
+ return sImmersiveNavigationFilter != null
+ && sImmersiveNavigationFilter.matches(packageName);
+ }
+
+ private static void setFilters(String value) {
+ if (DEBUG) Slog.d(TAG, "setFilters: " + value);
+ sImmersiveStatusFilter = null;
+ sImmersiveNavigationFilter = null;
+ if (value != null) {
+ String[] nvps = value.split(":");
+ for (String nvp : nvps) {
+ int i = nvp.indexOf('=');
+ if (i == -1) continue;
+ String n = nvp.substring(0, i);
+ String v = nvp.substring(i + 1);
+ if (n.equals(NAME_IMMERSIVE_FULL)) {
+ Filter f = Filter.parse(v);
+ sImmersiveStatusFilter = sImmersiveNavigationFilter = f;
+ } else if (n.equals(NAME_IMMERSIVE_STATUS)) {
+ Filter f = Filter.parse(v);
+ sImmersiveStatusFilter = f;
+ } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) {
+ Filter f = Filter.parse(v);
+ sImmersiveNavigationFilter = f;
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter);
+ Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter);
+ }
+ }
+
+ private static class Filter {
+ private static final String ALL = "*";
+
+ private final ArraySet<String> mWhitelist;
+ private final ArraySet<String> mBlacklist;
+
+ private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) {
+ mWhitelist = whitelist;
+ mBlacklist = blacklist;
+ }
+
+ boolean matches(String packageName) {
+ if (packageName == null) return false;
+ if (onBlacklist(packageName)) return false;
+ return onWhitelist(packageName);
+ }
+
+ private boolean onBlacklist(String packageName) {
+ return mBlacklist.contains(packageName) || mBlacklist.contains(ALL);
+ }
+
+ private boolean onWhitelist(String packageName) {
+ return mWhitelist.contains(ALL) || mWhitelist.contains(packageName);
+ }
+
+ void dump(PrintWriter pw) {
+ pw.print("Filter[");
+ dump("whitelist", mWhitelist, pw); pw.print(',');
+ dump("blacklist", mBlacklist, pw); pw.print(']');
+ }
+
+ private void dump(String name, ArraySet<String> set, PrintWriter pw) {
+ pw.print(name); pw.print("=(");
+ int n = set.size();
+ for (int i = 0; i < n; i++) {
+ if (i > 0) pw.print(',');
+ pw.print(set.valueAt(i));
+ }
+ pw.print(')');
+ }
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ dump(new PrintWriter(sw, true));
+ return sw.toString();
+ }
+
+ // value = comma-delimited list of tokens, where token = (package name|*)
+ // e.g. "com.package1", or "com.android.systemui, com.android.keyguard" or "*"
+ static Filter parse(String value) {
+ if (value == null) return null;
+ ArraySet<String> whitelist = new ArraySet<String>();
+ ArraySet<String> blacklist = new ArraySet<String>();
+ for (String token : value.split(",")) {
+ token = token.trim();
+ if (token.startsWith("-") && token.length() > 1) {
+ token = token.substring(1);
+ blacklist.add(token);
+ } else {
+ whitelist.add(token);
+ }
+ }
+ return new Filter(whitelist, blacklist);
+ }
+ }
+
+ /**
+ * Interface to listen for updates to the filter triggered by the content observer listening to
+ * the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
+ */
+ interface FilterListener {
+
+ /** Callback triggered when the content observer updates the filter. */
+ void onFilterUpdated();
+ }
+
+ private BarControlPolicy() {}
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
new file mode 100644
index 000000000000..5c80202ba592
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wm;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
+import android.view.InsetsController;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.WindowInsets;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.TransactionPool;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controller that maps between displays and {@link IDisplayWindowInsetsController} in order to
+ * give system bar control to SystemUI.
+ * {@link R.bool#config_remoteInsetsControllerControlsSystemBars} determines whether this controller
+ * takes control or not.
+ */
+@Singleton
+public class DisplaySystemBarsController extends DisplayImeController {
+
+ private static final String TAG = "DisplaySystemBarsController";
+
+ private SparseArray<PerDisplay> mPerDisplaySparseArray;
+ private final Context mContext;
+
+ @Inject
+ public DisplaySystemBarsController(
+ Context context,
+ IWindowManager wmService,
+ DisplayController displayController,
+ @Main Handler mainHandler,
+ TransactionPool transactionPool) {
+ super(wmService, displayController, mainHandler, transactionPool);
+ mContext = context;
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ PerDisplay pd = new PerDisplay(displayId);
+ try {
+ mWmService.setDisplayWindowInsetsController(displayId, pd);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to set insets controller on display " + displayId);
+ }
+ // Lazy loading policy control filters instead of during boot.
+ if (mPerDisplaySparseArray == null) {
+ mPerDisplaySparseArray = new SparseArray<>();
+ BarControlPolicy.reloadFromSetting(mContext);
+ BarControlPolicy.registerContentObserver(mContext, mHandler, () -> {
+ int size = mPerDisplaySparseArray.size();
+ for (int i = 0; i < size; i++) {
+ mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
+ }
+ });
+ }
+ mPerDisplaySparseArray.put(displayId, pd);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ try {
+ mWmService.setDisplayWindowInsetsController(displayId, null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
+ }
+ mPerDisplaySparseArray.remove(displayId);
+ }
+
+ @VisibleForTesting
+ class PerDisplay extends IDisplayWindowInsetsController.Stub {
+
+ int mDisplayId;
+ InsetsController mInsetsController;
+ InsetsState mInsetsState = new InsetsState();
+ String mPackageName;
+
+ PerDisplay(int displayId) {
+ mDisplayId = displayId;
+ mInsetsController = new InsetsController(
+ new DisplaySystemBarsInsetsControllerHost(mHandler, this));
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ if (mInsetsState.equals(insetsState)) {
+ return;
+ }
+ mInsetsState.set(insetsState, true /* copySources */);
+ mInsetsController.onStateChanged(insetsState);
+ if (mPackageName != null) {
+ modifyDisplayWindowInsets();
+ }
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ mInsetsController.onControlsChanged(activeControls);
+ }
+
+ @Override
+ public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+ mInsetsController.hide(types);
+ }
+
+ @Override
+ public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+ mInsetsController.show(types);
+ }
+
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ // If both package names are null or both package names are equal, return.
+ if (mPackageName == packageName
+ || (mPackageName != null && mPackageName.equals(packageName))) {
+ return;
+ }
+ mPackageName = packageName;
+ modifyDisplayWindowInsets();
+ }
+
+ private void modifyDisplayWindowInsets() {
+ if (mPackageName == null) {
+ return;
+ }
+ int[] barVisibilities = BarControlPolicy.getBarVisibilities(mPackageName);
+ updateInsetsState(barVisibilities[0], /* visible= */ true);
+ updateInsetsState(barVisibilities[1], /* visible= */ false);
+ showInsets(barVisibilities[0], /* fromIme= */ false);
+ hideInsets(barVisibilities[1], /* fromIme= */ false);
+ try {
+ mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to update window manager service.");
+ }
+ }
+
+ private void updateInsetsState(@WindowInsets.Type.InsetsType int types, boolean visible) {
+ ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ mInsetsState.getSource(internalTypes.valueAt(i)).setVisible(visible);
+ }
+ }
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java
new file mode 100644
index 000000000000..2f8da44ba851
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wm;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IDisplayWindowInsetsController;
+import android.view.InsetsController;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsController;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.List;
+
+/**
+ * Implements {@link InsetsController.Host} for usage by
+ * {@link DisplaySystemBarsController.PerDisplay} instances in {@link DisplaySystemBarsController}.
+ * @hide
+ */
+public class DisplaySystemBarsInsetsControllerHost implements InsetsController.Host {
+
+ private static final String TAG = DisplaySystemBarsInsetsControllerHost.class.getSimpleName();
+
+ private final Handler mHandler;
+ private final IDisplayWindowInsetsController mController;
+ private final float[] mTmpFloat9 = new float[9];
+
+ public DisplaySystemBarsInsetsControllerHost(
+ Handler handler, IDisplayWindowInsetsController controller) {
+ mHandler = handler;
+ mController = controller;
+ }
+
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public void notifyInsetsChanged() {
+ // no-op
+ }
+
+ @Override
+ public void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation) {
+ // no-op
+ }
+
+ @Override
+ public WindowInsetsAnimation.Bounds dispatchWindowInsetsAnimationStart(
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull WindowInsetsAnimation.Bounds bounds) {
+ return null;
+ }
+
+ @Override
+ public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return null;
+ }
+
+ @Override
+ public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
+ // no-op
+ }
+
+ @Override
+ public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+ for (int i = params.length - 1; i >= 0; i--) {
+ SyncRtSurfaceTransactionApplier.applyParams(
+ new SurfaceControl.Transaction(), params[i], mTmpFloat9);
+ }
+
+ }
+
+ @Override
+ public void updateCompatSysUiVisibility(
+ @InsetsState.InternalInsetsType int type, boolean visible, boolean hasControl) {
+ // no-op
+ }
+
+ @Override
+ public void onInsetsModified(InsetsState insetsState) {
+ try {
+ mController.insetsChanged(insetsState);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send insets to controller");
+ }
+ }
+
+ @Override
+ public boolean hasAnimationCallbacks() {
+ return false;
+ }
+
+ @Override
+ public void setSystemBarsAppearance(
+ @WindowInsetsController.Appearance int appearance,
+ @WindowInsetsController.Appearance int mask) {
+ // no-op
+ }
+
+ @Override
+ public @WindowInsetsController.Appearance int getSystemBarsAppearance() {
+ return 0;
+ }
+
+ @Override
+ public void setSystemBarsBehavior(@WindowInsetsController.Behavior int behavior) {
+ // no-op
+ }
+
+ @Override
+ public @WindowInsetsController.Behavior int getSystemBarsBehavior() {
+ return 0;
+ }
+
+ @Override
+ public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
+ surfaceControl.release();
+ }
+
+ @Override
+ public void addOnPreDrawRunnable(Runnable r) {
+ mHandler.post(r);
+ }
+
+ @Override
+ public void postInsetsAnimationCallback(Runnable r) {
+ mHandler.post(r);
+ }
+
+ @Override
+ public InputMethodManager getInputMethodManager() {
+ return null;
+ }
+
+ @Override
+ public String getRootViewTitle() {
+ return null;
+ }
+
+ @Override
+ public int dipToPx(int dips) {
+ return 0;
+ }
+
+ @Override
+ public IBinder getWindowToken() {
+ return null;
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
index fe59cbf20a13..d769cacadf1d 100644
--- a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -33,6 +33,7 @@ import androidx.test.internal.runner.ClassPathScanner.ExternalClassNameFilter;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,6 +56,7 @@ import java.util.Collections;
* test suite causes errors, such as the incorrect settings provider being cached.
* For an example, see {@link com.android.systemui.DependencyTest}.
*/
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestCase {
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
index 7996170ba7d6..e179ef1ce2a4 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
@@ -39,6 +40,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
index 189e240169c3..62dc23624520 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -41,6 +41,7 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -59,6 +60,7 @@ import org.mockito.MockitoAnnotations;
import dagger.Lazy;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java
index a57736bb3502..4b8268052324 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonRoleHolderControllerTest.java
@@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -49,6 +50,7 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java
index 893057e222a9..f623c26d12b6 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java
@@ -28,6 +28,7 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -38,6 +39,7 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
index e84e42c77245..dec8b8ecdfb4 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
@@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.hvac.HvacController;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -41,6 +42,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
index 0caa86f0eab2..d9edfa960858 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
@@ -45,6 +45,7 @@ import com.android.internal.view.AppearanceRegion;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -63,6 +64,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java
index 19e394f69af4..47fd8201d197 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java
@@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.After;
import org.junit.Before;
@@ -38,6 +39,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
index bcaa5e9a03ee..173f5487c728 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
@@ -37,6 +37,7 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.tests.R;
@@ -45,6 +46,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
index ccaeb458fe54..384888ab42c3 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
@@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import org.junit.Before;
@@ -38,6 +39,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java
index 89dac58cd2a7..d51aeb18135d 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java
@@ -37,6 +37,7 @@ import com.android.car.notification.NotificationDataManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -49,6 +50,7 @@ import org.mockito.MockitoAnnotations;
import java.util.Collections;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java
index 77620f3fb345..421e2109356d 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java
@@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
@@ -44,6 +45,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java
index 73f9f6a55afc..20576e9ec11f 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java
@@ -35,6 +35,7 @@ import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
@@ -46,6 +47,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
index 797dbf515b7e..2e9d43b595a1 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
@@ -36,6 +36,7 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import org.junit.Before;
@@ -44,6 +45,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java
index a808e2d40e26..de6feb64391f 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java
@@ -25,6 +25,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
@@ -32,6 +33,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
index eca51e34995c..31f1170c9603 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
@@ -24,6 +24,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
import android.content.Intent;
import android.os.Handler;
@@ -33,25 +35,32 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+ private static final String BLUETOOTH_REMOTE_ADDRESS = "00:11:22:33:44:55";
private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier;
+ private TestableLooper mTestableLooper;
private Handler mTestHandler;
+ private BluetoothDevice mBluetoothDevice;
@Before
public void setUp() throws Exception {
- TestableLooper testableLooper = TestableLooper.get(this);
- mTestHandler = spy(new Handler(testableLooper.getLooper()));
+ mTestableLooper = TestableLooper.get(this);
+ mTestHandler = spy(new Handler(mTestableLooper.getLooper()));
+ mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+ BLUETOOTH_REMOTE_ADDRESS);
mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier(
mContext, mTestHandler);
mVoiceRecognitionNotifier.onBootCompleted();
@@ -61,8 +70,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
public void testReceiveIntent_started_showToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, VOICE_RECOGNITION_STARTED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler).post(any());
}
@@ -71,8 +82,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
public void testReceiveIntent_invalidExtra_noToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, INVALID_VALUE);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler, never()).post(any());
}
@@ -80,8 +93,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
@Test
public void testReceiveIntent_noExtra_noToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler, never()).post(any());
}
@@ -89,8 +104,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
@Test
public void testReceiveIntent_invalidIntent_noToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler, never()).post(any());
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
index 45a05ac69bd7..7311cdb68a3c 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
@@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.tests.R;
@@ -52,6 +53,7 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java
index c24a3b52e348..e784761f6d5d 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java
@@ -29,6 +29,7 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -39,6 +40,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index cba42e5a9be4..ff286650ea50 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -16,9 +16,14 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,22 +33,23 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
+import com.android.systemui.car.CarSystemUiTest;
import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
+@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -56,8 +62,6 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private ViewGroup mBaseLayout;
@Mock
- private CarNavigationBarController mCarNavigationBarController;
- @Mock
private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
@Mock
private OverlayViewMediator mOverlayViewMediator;
@@ -69,18 +73,22 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private OverlayPanelViewController mOverlayPanelViewController;
@Mock
private Runnable mRunnable;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
- mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
- R.layout.overlay_view_global_state_controller_test, /* root= */ null);
+ mBaseLayout = spy((ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.overlay_view_global_state_controller_test, /* root= */ null));
+
+ when(mBaseLayout.getWindowInsetsController()).thenReturn(mWindowInsetsController);
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
- mCarNavigationBarController, mSystemUIOverlayWindowController);
+ mSystemUIOverlayWindowController);
verify(mSystemUIOverlayWindowController).attach();
}
@@ -106,7 +114,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -116,7 +124,37 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -166,10 +204,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -181,7 +220,46 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -214,10 +292,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -229,7 +308,44 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -400,10 +516,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -416,7 +533,48 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(statusBars());
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -439,10 +597,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -455,7 +614,48 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -477,7 +677,27 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_insetsAdjustedToDefault() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(statusBars());
}
@Test
@@ -613,7 +833,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
mOverlayViewGlobalStateController.showView(overlayViewController, /* show= */ null);
- Mockito.reset(mCarNavigationBarController, mSystemUIOverlayWindowController);
+ reset(mSystemUIOverlayWindowController);
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java
new file mode 100644
index 000000000000..da7cb8e4f6ac
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wm;
+
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.car.settings.CarSettings;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class BarControlPolicyTest extends SysuiTestCase {
+
+ private static final String PACKAGE_NAME = "sample.app";
+
+ @Before
+ public void setUp() {
+ BarControlPolicy.reset();
+ }
+
+ @After
+ public void tearDown() {
+ Settings.Global.clearProviderForTest();
+ }
+
+ @Test
+ public void reloadFromSetting_notSet_doesNotSetFilters() {
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
+ }
+
+ @Test
+ public void reloadFromSetting_invalidPolicyControlString_doesNotSetFilters() {
+ String text = "sample text";
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
+ }
+
+ @Test
+ public void reloadFromSetting_validPolicyControlString_setsFilters() {
+ String text = "immersive.status=" + PACKAGE_NAME;
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.sImmersiveStatusFilter).isNotNull();
+ }
+
+ @Test
+ public void reloadFromSetting_filtersSet_doesNotSetFiltersAgain() {
+ String text = "immersive.status=" + PACKAGE_NAME;
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.reloadFromSetting(mContext)).isFalse();
+ }
+
+ @Test
+ public void getBarVisibilities_policyControlNotSet_showsSystemBars() {
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveStatusForAppAndMatchingApp_hidesStatusBar() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.status=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(navigationBars());
+ assertThat(visibilities[1]).isEqualTo(statusBars());
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveStatusForAppAndNonMatchingApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.status=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveStatusForAppsAndNonApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.status=apps");
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveFullForAppAndMatchingApp_hidesSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.full=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(0);
+ assertThat(visibilities[1]).isEqualTo(statusBars() | navigationBars());
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveFullForAppAndNonMatchingApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.full=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveFullForAppsAndNonApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.full=apps");
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
new file mode 100644
index 000000000000..391f75e35382
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.car.settings.CarSettings;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.IWindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.TransactionPool;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class DisplaySystemBarsControllerTest extends SysuiTestCase {
+
+ private DisplaySystemBarsController mController;
+
+ private static final int DISPLAY_ID = 1;
+
+ @Mock
+ private IWindowManager mIWindowManager;
+ @Mock
+ private DisplayController mDisplayController;
+ @Mock
+ private Handler mHandler;
+ @Mock
+ private TransactionPool mTransactionPool;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mController = new DisplaySystemBarsController(
+ mContext,
+ mIWindowManager,
+ mDisplayController,
+ mHandler,
+ mTransactionPool
+ );
+ }
+
+ @Test
+ public void onDisplayAdded_setsDisplayWindowInsetsControllerOnWMService()
+ throws RemoteException {
+ mController.onDisplayAdded(DISPLAY_ID);
+
+ verify(mIWindowManager).setDisplayWindowInsetsController(
+ eq(DISPLAY_ID), any(DisplaySystemBarsController.PerDisplay.class));
+ }
+
+ @Test
+ public void onDisplayAdded_loadsBarControlPolicyFilters() {
+ String text = "sample text";
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ mController.onDisplayAdded(DISPLAY_ID);
+
+ assertThat(BarControlPolicy.sSettingValue).isEqualTo(text);
+ }
+
+ @Test
+ public void onDisplayRemoved_unsetsDisplayWindowInsetsControllerInWMService()
+ throws RemoteException {
+ mController.onDisplayAdded(DISPLAY_ID);
+
+ mController.onDisplayRemoved(DISPLAY_ID);
+
+ verify(mIWindowManager).setDisplayWindowInsetsController(
+ DISPLAY_ID, /* displayWindowInsetsController= */ null);
+ }
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 16ef59f201f1..02f4457bffdb 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -29,6 +29,7 @@ import android.text.Html;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
+import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -75,6 +76,14 @@ public class DeviceChooserActivity extends Activity {
mDeviceListView = findViewById(R.id.device_list);
final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
mDeviceListView.setAdapter(adapter);
+ mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
+ getService().mSelectedDevice =
+ (DeviceFilterPair) adapterView.getItemAtPosition(pos);
+ adapter.notifyDataSetChanged();
+ }
+ });
adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
@@ -157,4 +166,4 @@ public class DeviceChooserActivity extends Activity {
new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
finish();
}
-} \ No newline at end of file
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 7aa997e39307..bcaee367b03c 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -349,10 +349,6 @@ public class DeviceDiscoveryService extends Service {
? WIFI_ICON
: BLUETOOTH_ICON,
null, null, null);
- textView.setOnClickListener((view) -> {
- mSelectedDevice = device;
- notifyDataSetChanged();
- });
}
//TODO move to a layout file
diff --git a/packages/DynamicSystemInstallationService/tests/res/values/strings.xml b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
index fdb620bfe094..019c0c914771 100644
--- a/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
+++ b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- testFromJsonString -->
- <string name="blacklist_json_string" translatable="false">
+ <string name="blocklist_json_string" translatable="false">
{
\"entries\":[
{
diff --git a/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
index 82ce542cf5de..c1233ebab2ec 100644
--- a/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
+++ b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
@@ -47,32 +47,32 @@ public class KeyRevocationListTest {
private static Context sContext;
- private static String sBlacklistJsonString;
+ private static String sBlocklistJsonString;
@BeforeClass
public static void setUpClass() throws Exception {
sContext = InstrumentationRegistry.getInstrumentation().getContext();
- sBlacklistJsonString =
- sContext.getString(com.android.dynsystem.tests.R.string.blacklist_json_string);
+ sBlocklistJsonString =
+ sContext.getString(com.android.dynsystem.tests.R.string.blocklist_json_string);
}
@Test
@SmallTest
public void testFromJsonString() throws JSONException {
- KeyRevocationList blacklist;
- blacklist = KeyRevocationList.fromJsonString(sBlacklistJsonString);
- Assert.assertNotNull(blacklist);
- Assert.assertFalse(blacklist.mEntries.isEmpty());
- blacklist = KeyRevocationList.fromJsonString("{}");
- Assert.assertNotNull(blacklist);
- Assert.assertTrue(blacklist.mEntries.isEmpty());
+ KeyRevocationList blocklist;
+ blocklist = KeyRevocationList.fromJsonString(sBlocklistJsonString);
+ Assert.assertNotNull(blocklist);
+ Assert.assertFalse(blocklist.mEntries.isEmpty());
+ blocklist = KeyRevocationList.fromJsonString("{}");
+ Assert.assertNotNull(blocklist);
+ Assert.assertTrue(blocklist.mEntries.isEmpty());
}
@Test
@SmallTest
public void testFromUrl() throws IOException, JSONException {
URLConnection mockConnection = mock(URLConnection.class);
- doReturn(new ByteArrayInputStream(sBlacklistJsonString.getBytes()))
+ doReturn(new ByteArrayInputStream(sBlocklistJsonString.getBytes()))
.when(mockConnection).getInputStream();
URL mockUrl = new URL(
"http", // protocol
@@ -97,36 +97,36 @@ public class KeyRevocationListTest {
}
});
- KeyRevocationList blacklist = KeyRevocationList.fromUrl(mockUrl);
- Assert.assertNotNull(blacklist);
- Assert.assertFalse(blacklist.mEntries.isEmpty());
+ KeyRevocationList blocklist = KeyRevocationList.fromUrl(mockUrl);
+ Assert.assertNotNull(blocklist);
+ Assert.assertFalse(blocklist.mEntries.isEmpty());
- blacklist = null;
+ blocklist = null;
try {
- blacklist = KeyRevocationList.fromUrl(mockBadUrl);
+ blocklist = KeyRevocationList.fromUrl(mockBadUrl);
// Up should throw, down should be unreachable
Assert.fail("Expected IOException not thrown");
} catch (IOException e) {
// This is expected, do nothing
}
- Assert.assertNull(blacklist);
+ Assert.assertNull(blocklist);
}
@Test
@SmallTest
public void testIsRevoked() {
- KeyRevocationList blacklist = new KeyRevocationList();
- blacklist.addEntry("key1", "REVOKED", "reason for key1");
+ KeyRevocationList blocklist = new KeyRevocationList();
+ blocklist.addEntry("key1", "REVOKED", "reason for key1");
KeyRevocationList.RevocationStatus revocationStatus =
- blacklist.getRevocationStatusForKey("key1");
+ blocklist.getRevocationStatusForKey("key1");
Assert.assertNotNull(revocationStatus);
Assert.assertEquals(revocationStatus.mReason, "reason for key1");
- revocationStatus = blacklist.getRevocationStatusForKey("key2");
+ revocationStatus = blocklist.getRevocationStatusForKey("key2");
Assert.assertNull(revocationStatus);
- Assert.assertTrue(blacklist.isRevoked("key1"));
- Assert.assertFalse(blacklist.isRevoked("key2"));
+ Assert.assertTrue(blocklist.isRevoked("key1"));
+ Assert.assertFalse(blocklist.isRevoked("key2"));
}
}
diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp
index 43ed810b5674..b858ab01ffd9 100644
--- a/packages/EasterEgg/Android.bp
+++ b/packages/EasterEgg/Android.bp
@@ -23,11 +23,23 @@ android_app {
name: "EasterEgg",
+ platform_apis: true,
certificate: "platform",
- sdk_version: "current",
-
optimize: {
enabled: false,
- }
+ },
+
+ static_libs: [
+ "androidx.core_core",
+ "androidx.recyclerview_recyclerview",
+ "androidx.annotation_annotation",
+ "kotlinx-coroutines-android",
+ "kotlinx-coroutines-core",
+ //"kotlinx-coroutines-reactive",
+ ],
+
+ manifest: "AndroidManifest.xml",
+
+ kotlincflags: ["-Xjvm-default=enable"],
}
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
index 2494701071f5..3d62af020627 100644
--- a/packages/EasterEgg/AndroidManifest.xml
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -6,20 +6,24 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <!-- used for cat notifications -->
+ <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <!-- used to save cat images -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <!-- controls -->
+ <uses-permission android:name="android.permission.BIND_CONTROLS" />
+
<application
- android:icon="@drawable/q_icon"
+ android:icon="@drawable/icon"
android:label="@string/app_name">
+
<activity android:name=".quares.QuaresActivity"
android:icon="@drawable/q_icon"
android:label="@string/q_egg_name"
- android:theme="@style/QuaresTheme"
- android:exported="true">
+ android:exported="true"
+ android:theme="@style/QuaresTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.DEFAULT" />
- <!-- <category android:name="android.intent.category.LAUNCHER" /> -->
- <category android:name="com.android.internal.category.PLATLOGO" />
</intent-filter>
</activity>
<activity
@@ -27,16 +31,87 @@
android:configChanges="orientation|keyboardHidden|screenSize|uiMode"
android:icon="@drawable/p_icon"
android:label="@string/p_egg_name"
- android:theme="@style/AppTheme"
- android:exported="true">
+ android:exported="true"
+ android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <!-- Android N easter egg bits -->
+ <activity android:name=".neko.NekoLand"
+ android:theme="@android:style/Theme.Material.NoActionBar"
+ android:exported="true"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <!-- This is where the magic happens -->
+ <service
+ android:name=".neko.NekoService"
+ android:enabled="true"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:exported="true" >
+ </service>
- <!-- <category android:name="android.intent.category.DEFAULT" /> -->
- <!-- <category android:name="android.intent.category.LAUNCHER" /> -->
- <!-- <category android:name="com.android.internal.category.PLATLOGO" /> -->
+ <!-- Used to show over lock screen -->
+ <activity android:name=".neko.NekoLockedActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@android:style/Theme.Material.Light.Dialog.NoActionBar"
+ android:showOnLockScreen="true" />
+
+ <!-- Used to enable easter egg -->
+ <activity android:name=".neko.NekoActivationActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@android:style/Theme.NoDisplay"
+ >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="com.android.internal.category.PLATLOGO" />
</intent-filter>
</activity>
+
+ <!-- The quick settings tile, disabled by default -->
+ <service
+ android:name=".neko.NekoTile"
+ android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
+ android:icon="@drawable/stat_icon"
+ android:enabled="false"
+ android:exported="true"
+ android:label="@string/default_tile_name">
+ <intent-filter>
+ <action android:name="android.service.quicksettings.action.QS_TILE" />
+ </intent-filter>
+ </service>
+
+ <service android:name=".neko.NekoControlsService"
+ android:permission="android.permission.BIND_CONTROLS"
+ android:label="@string/r_egg_name"
+ android:icon="@drawable/ic_fullcat_icon"
+ android:enabled="false"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.controls.ControlsProviderService" />
+ </intent-filter>
+ </service>
+
+ <!-- FileProvider for sending pictures -->
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="com.android.egg.fileprovider"
+ android:grantUriPermissions="true"
+ android:exported="false">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/filepaths" />
+ </provider>
</application>
</manifest>
diff --git a/packages/EasterEgg/build.gradle b/packages/EasterEgg/build.gradle
new file mode 100644
index 000000000000..20b469898498
--- /dev/null
+++ b/packages/EasterEgg/build.gradle
@@ -0,0 +1,82 @@
+buildscript {
+ ext.kotlin_version = '1.3.71'
+
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.0.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+final String ANDROID_ROOT = "${rootDir}/../../../.."
+
+android {
+ compileSdkVersion COMPILE_SDK
+ buildToolsVersion BUILD_TOOLS_VERSION
+
+ defaultConfig {
+ applicationId "com.android.egg"
+ minSdkVersion 28
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ sourceSets {
+ main {
+ res.srcDirs = ['res']
+ java.srcDirs = ['src']
+ manifest.srcFile 'AndroidManifest.xml'
+ }
+ }
+
+ signingConfigs {
+ debug.storeFile file("${ANDROID_ROOT}/vendor/google/certs/devkeys/platform.keystore")
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.core:core-ktx:1.2.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6'
+ implementation "androidx.recyclerview:recyclerview:${ANDROID_X_VERSION}"
+ implementation "androidx.dynamicanimation:dynamicanimation:${ANDROID_X_VERSION}"
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ androidTestImplementation "androidx.annotation:annotation:${ANDROID_X_VERSION}"
+}
+
diff --git a/packages/EasterEgg/gradle.properties b/packages/EasterEgg/gradle.properties
new file mode 100644
index 000000000000..e8e6450e2943
--- /dev/null
+++ b/packages/EasterEgg/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+android.enableJetifier=true
+kotlin.code.style=official
+
+ANDROID_X_VERSION=1+
+COMPILE_SDK=android-30
+BUILD_TOOLS_VERSION=28.0.3
diff --git a/packages/EasterEgg/res/drawable/android_11_dial.xml b/packages/EasterEgg/res/drawable/android_11_dial.xml
new file mode 100644
index 000000000000..73fd37f1bdd6
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/android_11_dial.xml
@@ -0,0 +1,63 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:pathData="M77.773,51.064h-1.583c-0.217,0 -0.393,-0.176 -0.393,-0.393v-1.46c0,-0.217 0.176,-0.393 0.393,-0.393h3.466c0.217,0 0.393,0.176 0.393,0.393v9.921c0,0.217 -0.176,0.393 -0.393,0.393h-1.49c-0.217,0 -0.393,-0.176 -0.393,-0.393V51.064z"
+ android:fillColor="#F86734"/>
+ <path
+ android:pathData="M83.598,51.064h-1.583c-0.217,0 -0.393,-0.176 -0.393,-0.393v-1.46c0,-0.217 0.176,-0.393 0.393,-0.393h3.466c0.217,0 0.393,0.176 0.393,0.393v9.921c0,0.217 -0.176,0.393 -0.393,0.393h-1.49c-0.217,0 -0.393,-0.176 -0.393,-0.393V51.064z"
+ android:fillColor="#F86734"/>
+ <path
+ android:pathData="M70.044,75.974m-0.644,0a0.644,0.644 0,1 1,1.288 0a0.644,0.644 0,1 1,-1.288 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M56.896,80.985m-0.718,0a0.718,0.718 0,1 1,1.436 0a0.718,0.718 0,1 1,-1.436 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M43.408,78.881m-0.795,0a0.795,0.795 0,1 1,1.59 0a0.795,0.795 0,1 1,-1.59 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M32.419,70.115m-0.874,0a0.874,0.874 0,1 1,1.748 0a0.874,0.874 0,1 1,-1.748 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M27.306,56.992m-0.954,0a0.954,0.954 0,1 1,1.908 0a0.954,0.954 0,1 1,-1.908 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M29.313,43.489m-1.036,0a1.036,1.036 0,1 1,2.072 0a1.036,1.036 0,1 1,-2.072 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M37.988,32.445m-1.118,0a1.118,1.118 0,1 1,2.236 0a1.118,1.118 0,1 1,-2.236 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M51.137,27.064m-1.201,0a1.201,1.201 0,1 1,2.402 0a1.201,1.201 0,1 1,-2.402 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M64.553,28.868m-1.284,0a1.284,1.284 0,1 1,2.568 0a1.284,1.284 0,1 1,-2.568 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M75.522,37.652m-1.368,0a1.368,1.368 0,1 1,2.736 0a1.368,1.368 0,1 1,-2.736 0"
+ android:fillColor="#d7effe"/>
+ <path
+ android:pathData="M87.942,115.052l-47.557,-47.557l26.869,-26.87l47.557,47.558z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:startY="56.087"
+ android:startX="55.8464"
+ android:endY="100.0297"
+ android:endX="99.7891"
+ android:type="linear">
+ <item android:offset="0" android:color="#3F000000"/>
+ <item android:offset="1" android:color="#00000000"/>
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:pathData="M53.928,54.17m-18.999,0a18.999,18.999 0,1 1,37.998 0a18.999,18.999 0,1 1,-37.998 0"
+ android:fillColor="#3ddc84"/>
+ <path
+ android:pathData="M66.353,54.17m-3.185,0a3.185,3.185 0,1 1,6.37 0a3.185,3.185 0,1 1,-6.37 0"
+ android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/back.xml b/packages/EasterEgg/res/drawable/back.xml
new file mode 100644
index 000000000000..b55d65cdf76d
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/back.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="back" android:fillColor="#FF000000" android:pathData="M37.1,22c-1.1,0 -1.9,0.8 -1.9,1.9v5.6c0,1.1 0.8,1.9 1.9,1.9H39v-1.9v-5.6V22H37.1z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/belly.xml b/packages/EasterEgg/res/drawable/belly.xml
new file mode 100644
index 000000000000..8b0e9afac463
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/belly.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="belly" android:fillColor="#FF000000" android:pathData="M20.5,25c-3.6,0 -6.5,2.9 -6.5,6.5V38h13v-6.5C27,27.9 24.1,25 20.5,25z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/body.xml b/packages/EasterEgg/res/drawable/body.xml
new file mode 100644
index 000000000000..86087209eff5
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/body.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="body" android:fillColor="#FF000000" android:pathData="M9,20h30v18h-30z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/bowtie.xml b/packages/EasterEgg/res/drawable/bowtie.xml
new file mode 100644
index 000000000000..33fa9216712f
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/bowtie.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="bowtie" android:fillColor="#FF000000" android:pathData="M29,16.8l-10,5l0,-5l10,5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/cap.xml b/packages/EasterEgg/res/drawable/cap.xml
new file mode 100644
index 000000000000..d8b4cc58a261
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/cap.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="cap" android:fillColor="#FF000000" android:pathData="M27.2,3.8c-1,-0.2 -2.1,-0.3 -3.2,-0.3s-2.1,0.1 -3.2,0.3c0.2,1.3 1.5,2.2 3.2,2.2C25.6,6.1 26.9,5.1 27.2,3.8z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/collar.xml b/packages/EasterEgg/res/drawable/collar.xml
new file mode 100644
index 000000000000..5e4d0fd4f886
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/collar.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="collar" android:fillColor="#FF000000" android:pathData="M9,18.4h30v1.7h-30z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/face_spot.xml b/packages/EasterEgg/res/drawable/face_spot.xml
new file mode 100644
index 000000000000..a89fb4fdaadd
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/face_spot.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="face_spot" android:fillColor="#FF000000" android:pathData="M19.5,15.2a4.5,3.2 0,1 0,9 0a4.5,3.2 0,1 0,-9 0z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_bits.xml b/packages/EasterEgg/res/drawable/food_bits.xml
new file mode 100644
index 000000000000..1b2bb6f36947
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_bits.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.1,34l-3.5,1.3c-1,0.4,-2.2,-0.1,-2.6,-1.1l-1.2,-3c-0.4,-1,0.1,-2.2,1.1,-2.6l3.5,-1.3c1,-0.4,2.2,0.1,2.6,1.1l1.2,3 C20.6,32.4,20.1,33.6,19.1,34z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M25.2,28.1L22.9,28c-0.8,0,-1.5,-0.7,-1.4,-1.6l0.1,-2c0,-0.8,0.7,-1.5,1.6,-1.4l2.4,0.1c0.8,0,1.5,0.7,1.4,1.6l-0.1,2 C26.8,27.5,26.1,28.1,25.2,28.1z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.7,23.1L16.5,23c-0.5,0,-0.9,-0.4,-0.8,-0.9l0.1,-2.2c0,-0.5,0.4,-0.9,0.9,-0.8l2.2,0.1c0.5,0,0.9,0.4,0.8,0.9 l-0.1,2.2C19.6,22.8,19.2,23.1,18.7,23.1z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M32.2,35.3l-3.6,-1.8c-1,-0.5,-1.4,-1.7,-0.9,-2.7l1.6,-3.1c0.5,-1,1.7,-1.4,2.7,-0.9l3.6,1.8c1,0.5,1.4,1.7,0.9,2.7 l-1.6,3.1C34.4,35.4,33.2,35.7,32.2,35.3z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_chicken.xml b/packages/EasterEgg/res/drawable/food_chicken.xml
new file mode 100644
index 000000000000..95b2fb55b796
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_chicken.xml
@@ -0,0 +1,39 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,12v14h10V11H9z M11.7,16.3c-0.7,0,-1.3,-0.6,-1.3,-1.3s0.6,-1.3,1.3,-1.3S13,14.3,13,15S12.4,16.3,11.7,16.3z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.7,20.1l1.6,-3.0l-1.6,-3.0l4.4,3.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.0,6.0l-2.3,2.3l-2.7,-2.6l-2.7,2.6l-2.3,-2.3l0.0,4.0l10.0,0.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,25c0,8.3,6.7,15,15,15s15,-6.7,15,-15H9z M29.9,31.5h-11v-1h12L29.9,31.5z M31.9,29.5h-13v-1h14L31.9,29.5z M33.9,27.5 h-15v-1h16L33.9,27.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M27.0,38.6h2.0v6.0h-2.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.4,44.6l-2.1999998,0.0l4.4000006,-6.0l2.1999989,0.0z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_cookie.xml b/packages/EasterEgg/res/drawable/food_cookie.xml
new file mode 100644
index 000000000000..74dd134355e2
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_cookie.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <group>
+ <path
+ android:fillColor="#55FFFFFF"
+ android:fillType="evenOdd"
+ android:pathData="M5.71 18.29A8.99 8.99 0 0 0 22 13c0-3-1.46-5.65-3.71-7.29A8.99 8.99 0 0 0 2 11c0 3 1.46 5.65 3.71 7.29z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:fillType="evenOdd"
+ android:pathData="M7.25 19.18A8.5 8.5 0 0 0 19.19 7.24 9 9 0 0 1 7.24 19.19z"/>
+ <path
+ android:fillColor="#55FFFFFF"
+ android:pathData="M10.5 3a0.5 0.5 0 1 1 1 0v2.05a0.5 0.5 0 1 1-1 0V3zm3.1 0.42a0.5 0.5 0 0 1 0.93 0.39l-0.8 1.88A0.5 0.5 0 1 1 12.8 5.3l0.8-1.88zm2.7 1.57a0.5 0.5 0 1 1 0.71 0.7l-1.45 1.46a0.5 0.5 0 0 1-0.7-0.71l1.44-1.45zm1.9 2.5a0.5 0.5 0 0 1 0.38 0.92l-1.9 0.77a0.5 0.5 0 0 1-0.37-0.93l1.9-0.77zM19 10.5a0.5 0.5 0 1 1 0 1h-2.05a0.5 0.5 0 0 1 0-1H19zm-0.42 3.1a0.5 0.5 0 0 1-0.39 0.93l-1.88-0.8a0.5 0.5 0 1 1 0.39-0.92l1.88 0.8zm-1.57 2.7a0.5 0.5 0 1 1-0.7 0.71l-1.46-1.45a0.5 0.5 0 0 1 0.71-0.7l1.45 1.44zm-2.5 1.9a0.5 0.5 0 1 1-0.92 0.38l-0.77-1.9a0.5 0.5 0 0 1 0.93-0.37l0.77 1.9zM11.5 19a0.5 0.5 0 1 1-1 0v-2.05a0.5 0.5 0 0 1 1 0V19zm-3.1-0.42a0.5 0.5 0 0 1-0.93-0.39l0.8-1.88A0.5 0.5 0 0 1 9.2 16.7l-0.8 1.88zm-2.7-1.57a0.5 0.5 0 1 1-0.71-0.7l1.45-1.46a0.5 0.5 0 0 1 0.7 0.71L5.7 17.01zm-1.9-2.48a0.5 0.5 0 0 1-0.38-0.92l1.88-0.8a0.5 0.5 0 0 1 0.4 0.92l-1.9 0.8zM3 11.5a0.5 0.5 0 1 1 0-1h2.05a0.5 0.5 0 1 1 0 1H3zm0.42-3.1A0.5 0.5 0 0 1 3.8 7.46l1.88 0.8A0.5 0.5 0 1 1 5.3 9.2L3.42 8.4zm1.57-2.7a0.5 0.5 0 1 1 0.7-0.71l1.46 1.45a0.5 0.5 0 0 1-0.71 0.7L4.99 5.7zm2.5-1.9A0.5 0.5 0 0 1 8.4 3.41l0.77 1.9a0.5 0.5 0 0 1-0.93 0.37L7.48 3.8z"/>
+ </group>
+</vector> \ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/food_dish.xml b/packages/EasterEgg/res/drawable/food_dish.xml
new file mode 100644
index 000000000000..3fff6a90fad2
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_dish.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24,13.8C11.3,13.8,1,18.3,1,24c0,5.7,10.3,10.2,23,10.2S47,29.7,47,24C47,18.3,36.7,13.8,24,13.8z M33.7,26.6 c1.1,-0.6,1.8,-1.3,1.8,-2c0,-2.1,-5.2,-3.8,-11.7,-3.8s-11.7,1.7,-11.7,3.8c0,0.6,0.4,1.2,1.2,1.7c-1.7,-0.8,-2.8,-1.7,-2.8,-2.8 c0,-2.5,6,-4.5,13.4,-4.5s13.4,2,13.4,4.5C37.4,24.7,36,25.8,33.7,26.6z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_donut.xml b/packages/EasterEgg/res/drawable/food_donut.xml
new file mode 100644
index 000000000000..eaf831ea560c
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_donut.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24,4.5c-10.5,0,-19,8.5,-19,19s8.5,19,19,19s19,-8.5,19,-19S34.5,4.5,24,4.5z M35.2,15.5l1.6,-1.1 c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1c0.2,0.3,0.1,0.6,-0.1,0.8l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1 C34.9,16.1,35,15.7,35.2,15.5z M32.7,10.7c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6l-0.2,2c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0 c-0.3,0,-0.5,-0.3,-0.5,-0.6L32.7,10.7z M31.7,15.1l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1c0,0.2,-0.1,0.5,-0.4,0.5l-1.5,0.2 c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1C31.3,15.4,31.5,15.2,31.7,15.1z M28.8,10.6l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1 c0.2,0.3,0.1,0.6,-0.1,0.8l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1C28.4,11.1,28.5,10.8,28.8,10.6z M25.8,6 c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6l-0.2,2c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0c-0.3,0,-0.5,-0.3,-0.5,-0.6L25.8,6z M20.7,6.5l1.9,-0.7c0.3,-0.1,0.6,0,0.7,0.3l0,0.1c0.1,0.3,0,0.6,-0.3,0.7l-1.9,0.7c-0.3,0.1,-0.6,0,-0.7,-0.3l0,-0.1 C20.3,6.9,20.4,6.6,20.7,6.5z M19.9,10.9l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1c0,0.2,-0.1,0.5,-0.4,0.5l-1.5,0.2 c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1C19.5,11.1,19.7,10.9,19.9,10.9z M16,10.9L16,10.9c0.2,-0.3,0.4,-0.4,0.6,-0.3l1.3,0.7 c0.2,0.1,0.3,0.4,0.2,0.6L18,12c-0.1,0.2,-0.4,0.3,-0.6,0.2l-1.3,-0.7C15.9,11.4,15.8,11.1,16,10.9z M15.8,18.5c0.2,0,0.4,0.1,0.5,0.4 l0,0.1c0,0.2,-0.1,0.4,-0.4,0.5l-1.5,0.2c-0.2,0,-0.4,-0.1,-0.5,-0.4l0,-0.1c0,-0.2,0.1,-0.4,0.4,-0.5L15.8,18.5z M14,21.8l-1.6,1.1 c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1 C14.3,21.3,14.3,21.6,14,21.8z M12.4,12L12.4,12c0.3,-0.2,0.5,-0.2,0.7,-0.1l1,1.1c0.2,0.2,0.2,0.4,0,0.6L14,13.7 c-0.2,0.2,-0.4,0.2,-0.6,0l-1,-1.1C12.2,12.4,12.2,12.1,12.4,12z M8.3,24.5c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0c-0.3,0,-0.5,-0.3,-0.5,-0.6 l0.2,-2c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6L8.3,24.5z M8.5,16.2v-0.1c0,-0.3,0.2,-0.6,0.6,-0.6h2 c0.3,0,0.6,0.2,0.6,0.6v0.1c0,0.3,-0.2,0.6,-0.6,0.6H9C8.7,16.7,8.5,16.5,8.5,16.2z M10.3,20.7c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1 c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1c0.2,0.3,0.1,0.6,-0.1,0.8L10.3,20.7z M11.3,28.3l0,-0.1 c-0.1,-0.3,0,-0.6,0.3,-0.7l1.9,-0.7c0.3,-0.1,0.6,0,0.7,0.3l0,0.1c0.1,0.3,0,0.6,-0.3,0.7L12,28.6C11.7,28.7,11.4,28.6,11.3,28.3z M14.4,33c0,0.2,-0.2,0.4,-0.4,0.4h-1.5c-0.2,0,-0.4,-0.2,-0.4,-0.4v-0.1c0,-0.2,0.2,-0.4,0.4,-0.4H14c0.2,0,0.4,0.2,0.4,0.4V33z M17.9,35.2 l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1 C18.2,34.7,18.2,35.1,17.9,35.2z M20.7,33.8l-0.1,0.1c-0.1,0.3,-0.5,0.4,-0.8,0.2l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1 c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1C20.7,33.2,20.8,33.5,20.7,33.8z M17.5,23.5c0,-3.6,2.9,-6.5,6.5,-6.5s6.5,2.9,6.5,6.5 c0,3.6,-2.9,6.5,-6.5,6.5S17.5,27.1,17.5,23.5z M27.4,35.7l-1.9,0.7c-0.3,0.1,-0.6,0,-0.7,-0.3l0,-0.1c-0.1,-0.3,0,-0.6,0.3,-0.7l1.9,-0.7 c0.3,-0.1,0.6,0,0.7,0.3l0,0.1C27.9,35.3,27.7,35.6,27.4,35.7z M29.7,32.7l-1.4,0.5c-0.2,0.1,-0.5,0,-0.5,-0.3l0,-0.1 c-0.1,-0.2,0,-0.5,0.3,-0.5l1.4,-0.5c0.2,-0.1,0.5,0,0.5,0.3l0,0.1C30,32.3,29.9,32.6,29.7,32.7z M32.8,35.5l-0.1,0.1 c-0.1,0.3,-0.5,0.4,-0.8,0.2l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1C32.8,34.9,32.9,35.2,32.8,35.5z M33.7,30.9c0,0.2,-0.2,0.4,-0.5,0.4l-0.1,0c-0.2,0,-0.4,-0.2,-0.4,-0.5l0.1,-1.5c0,-0.2,0.2,-0.4,0.5,-0.4l0.1,0c0.2,0,0.4,0.2,0.4,0.5 L33.7,30.9z M34.5,26.5l-1.3,0.9c-0.2,0.1,-0.5,0.1,-0.6,-0.1l-0.1,-0.1c-0.1,-0.2,-0.1,-0.5,0.1,-0.6l1.3,-0.9c0.2,-0.1,0.5,-0.1,0.6,0.1 l0.1,0.1C34.8,26.1,34.7,26.3,34.5,26.5z M35.6,20.6l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1 c0.3,0.1,0.4,0.5,0.2,0.8l-0.1,0.1C36.2,20.6,35.8,20.7,35.6,20.6z M38.6,27.1l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1L36.1,28 c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1C38.9,26.6,38.8,27,38.6,27.1z M39,19.4l-1.5,0.2 c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1c0,-0.2,0.1,-0.5,0.4,-0.5l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1C39.4,19.1,39.2,19.3,39,19.4z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/food_sysuituna.xml b/packages/EasterEgg/res/drawable/food_sysuituna.xml
new file mode 100644
index 000000000000..28cf4a2c7683
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/food_sysuituna.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M46,18.4l-5.8,4.6c-3.9,-3.2,-8.9,-5.6,-14.6,-6.3l1.2,-6l-7.3,5.9C12.5,17.2,6.4,20,2,24.3l7.2,1.4L2,27 c4.3,4.2,10.4,7.1,17.3,7.6l3.1,2.5L22,34.8c7.1,0,13.5,-2.5,18.2,-6.5l5.8,4.6l-1.4,-7.2L46,18.4z M14.3,24.8l-0.6,0.6l-1.1,-1.1 l-1.1,1.1l-0.6,-0.6l1.1,-1.1l-1.1,-1.1l0.6,-0.6l1.1,1.1l1.1,-1.1l0.6,0.6l-1.1,1.1L14.3,24.8z M18.8,29.1c0.7,-0.8,1.1,-2.2,1.1,-3.8 c0,-1.6,-0.4,-3,-1.1,-3.8c1.1,0.5,1.9,2,1.9,3.8S19.9,28.5,18.8,29.1z M20.7,29.1c0.7,-0.8,1.1,-2.2,1.1,-3.8c0,-1.6,-0.4,-3,-1.1,-3.8 c1.1,0.5,1.9,2,1.9,3.8S21.8,28.5,20.7,29.1z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/foot1.xml b/packages/EasterEgg/res/drawable/foot1.xml
new file mode 100644
index 000000000000..0d9085998a18
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/foot1.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="foot1" android:fillColor="#FF000000" android:pathData="M11.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/foot2.xml b/packages/EasterEgg/res/drawable/foot2.xml
new file mode 100644
index 000000000000..364ba0cd861c
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/foot2.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="foot2" android:fillColor="#FF000000" android:pathData="M18.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/foot3.xml b/packages/EasterEgg/res/drawable/foot3.xml
new file mode 100644
index 000000000000..e3a512a2568d
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/foot3.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="foot3" android:fillColor="#FF000000" android:pathData="M29.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/foot4.xml b/packages/EasterEgg/res/drawable/foot4.xml
new file mode 100644
index 000000000000..66b78fa26649
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/foot4.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="foot4" android:fillColor="#FF000000" android:pathData="M36.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/head.xml b/packages/EasterEgg/res/drawable/head.xml
new file mode 100644
index 000000000000..df600a8613cd
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/head.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="head" android:fillColor="#FF000000" android:pathData="M9,18.5c0,-8.3 6.8,-15 15,-15s15,6.7 15,15H9z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_bowl.xml b/packages/EasterEgg/res/drawable/ic_bowl.xml
new file mode 100644
index 000000000000..d55565d92988
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_bowl.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M3,19L21,19"
+ android:strokeWidth="2"
+ android:strokeColor="#FF8000"/>
+ <path
+ android:pathData="M7,12L4.5,19H19.5L17,12H7Z"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:strokeColor="#FF8000"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M7.5257,18.8419L9.5257,12.8419"
+ android:strokeColor="#FF8000"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_close.xml b/packages/EasterEgg/res/drawable/ic_close.xml
new file mode 100644
index 000000000000..60ea36b11fcc
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_close.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M19.0,6.41L17.59,5.0 12.0,10.59 6.41,5.0 5.0,6.41 10.59,12.0 5.0,17.59 6.41,19.0 12.0,13.41 17.59,19.0 19.0,17.59 13.41,12.0z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_foodbowl_filled.xml b/packages/EasterEgg/res/drawable/ic_foodbowl_filled.xml
new file mode 100644
index 000000000000..54961af68aef
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_foodbowl_filled.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M3,19L21,19"
+ android:strokeWidth="2"
+ android:strokeColor="#FF8000"/>
+ <path
+ android:pathData="M9,9m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#FF8000"/>
+ <path
+ android:pathData="M12,9m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#FF8000"/>
+ <path
+ android:pathData="M15,9m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#FF8000"/>
+ <path
+ android:pathData="M13.5,7m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#FF8000"/>
+ <path
+ android:pathData="M10.5,7m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#FF8000"/>
+ <path
+ android:pathData="M6.0583,11.6637C6.2004,11.2657 6.5774,11 7,11H17C17.4226,11 17.7996,11.2657 17.9418,11.6637L19.8476,17H4.1524L6.0583,11.6637ZM7.5,12L6,16H7L8.5,12H7.5Z"
+ android:fillColor="#FF8000"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_fullcat_icon.xml b/packages/EasterEgg/res/drawable/ic_fullcat_icon.xml
new file mode 100644
index 000000000000..5dca3d18f2d4
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_fullcat_icon.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M15.38,1.02l5.12,5.32l-6.32,2.72l1.2,-8.04z"
+ android:fillColor="#808080"/>
+ <path
+ android:pathData="M32.63,1.02l-5.13,5.32l6.32,2.72l-1.19,-8.04z"
+ android:fillColor="#808080"/>
+ <path
+ android:pathData="M33.82,9.06l-4.77,-1.82l3.58,-6.22l1.19,8.04z"
+ android:fillColor="#666"/>
+ <path
+ android:pathData="M15.38,1.02l3.57,6.22l-4.77,1.82l1.2,-8.04z"
+ android:fillColor="#666"/>
+ <path
+ android:pathData="M9,18.5a15,15 0,0 1,30 0Z"
+ android:fillColor="#808080"/>
+ <path
+ android:pathData="M19.5,15.25a4.5,3.25 0,1 0,9 0a4.5,3.25 0,1 0,-9 0z"
+ android:fillColor="#fff"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.5,11c0,1.73 -3,1.73 -3,0S20.5,9.35 20.5,11Z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M30.5,11c0,1.73 -3,1.73 -3,0S30.5,9.35 30.5,11Z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M25.15,13c0,1.28 -2.3,1.28 -2.3,0S25.15,11.73 25.15,13Z"/>
+ <path
+ android:pathData="M29,14.29a2.78,2.78 0,0 1,-2.33 1.41A2.75,2.75 0,0 1,24 13"
+ android:strokeWidth="1.25"
+ android:fillColor="#00000000"
+ android:strokeColor="#000"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M24,13a2.66,2.66 0,0 1,-2.67 2.69A2.53,2.53 0,0 1,19 14.29"
+ android:strokeWidth="1.25"
+ android:fillColor="#00000000"
+ android:strokeColor="#000"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M9,20h30v18h-30z"
+ android:fillColor="#808080"/>
+ <path
+ android:pathData="M11.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="M9,37h5v6h-5z"
+ android:fillColor="#808080"/>
+ <path
+ android:pathData="M29.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="M27,37h5v6h-5z"
+ android:fillColor="#808080"/>
+ <path
+ android:pathData="M36.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="M34,37h5v6h-5z"
+ android:fillColor="#808080"/>
+ <path
+ android:pathData="M18.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="M16,37h5v6h-5z"
+ android:fillColor="#808080"/>
+ <path
+ android:pathData="M35,35.5h5.9a3.8,3.8 0,0 0,3.8 -3.8V25.5"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#808080"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M40,38l0,-5l-1,0l0,5l1,0z"
+ android:fillColor="#666"/>
+ <path
+ android:pathData="M20.5,25A6.47,6.47 0,0 0,14 31.5V38H27V31.5A6.47,6.47 0,0 0,20.5 25Z"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="M16,38h5v1h-5z"
+ android:fillColor="#666"/>
+ <path
+ android:pathData="M9,18.5h30v1.5h-30z"
+ android:fillColor="#3ddc84"/>
+ <path
+ android:pathData="M29,16.75l-10,5l0,-5l10,5l0,-5z"
+ android:fillColor="#3ddc84"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_share.xml b/packages/EasterEgg/res/drawable/ic_share.xml
new file mode 100644
index 000000000000..8cebc7ed46de
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_share.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M18.0,16.08c-0.76,0.0 -1.4,0.3 -1.9,0.77L8.91,12.7c0.05,-0.2 0.09,-0.4 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.5,0.5 1.2,0.81 2.0,0.81 1.66,0.0 3.0,-1.34 3.0,-3.0s-1.34,-3.0 -3.0,-3.0 -3.0,1.34 -3.0,3.0c0.0,0.2 0.0,0.4 0.0,0.7L8.04,9.81C7.5,9.31 6.79,9.0 6.0,9.0c-1.66,0.0 -3.0,1.34 -3.0,3.0s1.34,3.0 3.0,3.0c0.79,0.0 1.5,-0.31 2.04,-0.81l7.12,4.16c0.0,0.21 0.0,0.43 0.0,0.65 0.0,1.61 1.31,2.92 2.92,2.92 1.61,0.0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_toy_ball.xml b/packages/EasterEgg/res/drawable/ic_toy_ball.xml
new file mode 100644
index 000000000000..411084b2a272
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_toy_ball.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF4080"/>
+ <path
+ android:pathData="M12,9C12.5523,9 13,8.5523 13,8C13,7.4477 12.5523,7 12,7V9ZM7,12C7,12.5523 7.4477,13 8,13C8.5523,13 9,12.5523 9,12H7ZM12,7C10.6748,7 9.4332,7.6526 8.5429,8.5429C7.6526,9.4332 7,10.6748 7,12H9C9,11.3252 9.3475,10.5668 9.9571,9.9571C10.5668,9.3475 11.3252,9 12,9V7Z"
+ android:fillColor="#FF4080"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_toy_fish.xml b/packages/EasterEgg/res/drawable/ic_toy_fish.xml
new file mode 100644
index 000000000000..bb01e9f32bfb
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_toy_fish.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M14.8492,8.9498C15.7483,9.8489 16.132,11.201 15.9095,12.7581C15.6871,14.3155 14.8589,16.0111 13.435,17.435C12.0111,18.8589 10.3155,19.6871 8.7581,19.9096C7.201,20.132 5.8488,19.7484 4.9497,18.8493C4.0506,17.9501 3.667,16.598 3.8894,15.0409C4.1119,13.4835 4.9401,11.7879 6.364,10.364C7.7879,8.9401 9.4835,8.1119 11.0409,7.8895C12.598,7.667 13.9501,8.0506 14.8492,8.9498Z"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF4080"/>
+ <path
+ android:pathData="M7,15m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#FF4080"/>
+ <path
+ android:pathData="M14.5,8L17.5,3C17.5,3 18,4.5 19,6C20,7.5 21.5,8.5 21.5,8.5L16.5,10"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF4080"/>
+ <path
+ android:pathData="M8.5,4.5L6.5,10L10,7.5L8.5,4.5Z"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:fillColor="#FF4080"
+ android:strokeColor="#FF4080"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_toy_laser.xml b/packages/EasterEgg/res/drawable/ic_toy_laser.xml
new file mode 100644
index 000000000000..8fe84ffbd38c
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_toy_laser.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.866,3.5C12.6874,3.1906 12.3573,3 12,3C11.6427,3 11.3126,3.1906 11.134,3.5L2.4737,18.5C2.2951,18.8094 2.2951,19.1906 2.4737,19.5C2.6523,19.8094 2.9825,20 3.3398,20H20.6603C21.0175,20 21.3476,19.8094 21.5263,19.5C21.7049,19.1906 21.7049,18.8094 21.5263,18.5L12.866,3.5Z"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF4080"/>
+ <path
+ android:pathData="M8,13.5h11v1h-11z"
+ android:fillColor="#FF4080"/>
+ <path
+ android:pathData="M11.5,10h1v8h-1z"
+ android:fillColor="#FF4080"/>
+ <path
+ android:pathData="M8.86,11.4883l0.6283,-0.6283l5.6547,5.6547l-0.6283,0.6283z"
+ android:fillColor="#FF4080"/>
+ <path
+ android:pathData="M9.4883,17.143l-0.6283,-0.6283l5.6547,-5.6547l0.6283,0.6283z"
+ android:fillColor="#FF4080"/>
+ <path
+ android:pathData="M12,14m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
+ android:fillColor="#FF4080"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_toy_mouse.xml b/packages/EasterEgg/res/drawable/ic_toy_mouse.xml
new file mode 100644
index 000000000000..ba3dc3322083
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_toy_mouse.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M14.8492,8.9498C15.7483,9.8489 16.132,11.201 15.9095,12.7581C15.6871,14.3155 14.8589,16.0111 13.435,17.435C12.0111,18.8589 10.3155,19.6871 8.7581,19.9096C7.201,20.132 5.8488,19.7484 4.9497,18.8493C4.0506,17.9501 3.667,16.598 3.8894,15.0409C4.1119,13.4835 4.9401,11.7879 6.364,10.364C7.7879,8.9401 9.4835,8.1119 11.0409,7.8895C12.598,7.667 13.9501,8.0506 14.8492,8.9498Z"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF4080"/>
+ <path
+ android:pathData="M3.5,11.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF4080"/>
+ <path
+ android:pathData="M7.5,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF4080"/>
+ <path
+ android:pathData="M7,15m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#FF4080"/>
+ <path
+ android:pathData="M9,13m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#FF4080"/>
+ <path
+ android:pathData="M22,4C22,3.4477 21.5523,3 21,3C20.4477,3 20,3.4477 20,4L22,4ZM15,9C14.873,9.9919 14.8735,9.992 14.874,9.992C14.8742,9.9921 14.8747,9.9921 14.8751,9.9922C14.8759,9.9923 14.8768,9.9924 14.8778,9.9925C14.8798,9.9928 14.8821,9.993 14.8848,9.9934C14.8902,9.994 14.8971,9.9948 14.9054,9.9958C14.922,9.9976 14.9442,10 14.9718,10.0026C15.027,10.0079 15.1036,10.0143 15.1985,10.02C15.3881,10.0312 15.6534,10.0396 15.9697,10.0294C16.5957,10.0092 17.455,9.9156 18.3326,9.6062C19.2147,9.2951 20.1482,8.7534 20.8583,7.8203C21.5743,6.8795 22,5.6234 22,4L20,4C20,5.2607 19.6757,6.0717 19.2667,6.6091C18.8518,7.1543 18.2853,7.5021 17.6674,7.72C17.045,7.9395 16.4043,8.0144 15.9053,8.0304C15.6591,8.0384 15.4556,8.0317 15.3171,8.0235C15.248,8.0194 15.1957,8.0149 15.1629,8.0118C15.1466,8.0102 15.1352,8.009 15.129,8.0083C15.126,8.008 15.1242,8.0077 15.1239,8.0077C15.1237,8.0077 15.1239,8.0077 15.1244,8.0078C15.1247,8.0078 15.125,8.0078 15.1254,8.0079C15.1256,8.0079 15.126,8.008 15.1262,8.008C15.1266,8.008 15.127,8.0081 15,9Z"
+ android:fillColor="#FF4080"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_water.xml b/packages/EasterEgg/res/drawable/ic_water.xml
new file mode 100644
index 000000000000..7d94b2409636
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_water.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.654,7.563L12,2L6.346,7.563C5.6036,8.2877 5.0136,9.1533 4.6108,10.1094C4.2079,11.0654 4.0002,12.0924 4,13.1299C4,15.2516 4.8429,17.2863 6.3432,18.7866C7.8434,20.2869 9.8783,21.1299 12,21.1299C14.1217,21.1299 16.1566,20.2869 17.6569,18.7866C19.1572,17.2863 20,15.2516 20,13.1299C20,12.0924 19.7925,11.0654 19.3896,10.1094C18.9867,9.1533 18.3966,8.2875 17.654,7.563V7.563ZM12,19C10.4265,19.0152 8.9113,18.4056 7.7865,17.3052C6.6617,16.2048 6.0192,14.7033 6,13.1299C5.9996,12.3577 6.1541,11.5933 6.4543,10.8818C6.7546,10.1704 7.1945,9.5262 7.748,8.9878L12,4.8061L16.252,8.9888C16.8056,9.5269 17.2456,10.171 17.5458,10.8823C17.8461,11.5936 18.0005,12.3578 18,13.1299C17.9807,14.7033 17.3383,16.2048 16.2135,17.3052C15.0887,18.4056 13.5735,19.0152 12,19Z"
+ android:fillColor="#0080FF"/>
+ <path
+ android:pathData="M16,12C15.7348,12 15.4804,12.1054 15.2929,12.293C15.1054,12.4805 15,12.7348 15,13C15,13.7956 14.6839,14.5585 14.1213,15.1211C13.5587,15.6837 12.7956,16 12,16C11.7348,16 11.4804,16.1054 11.2929,16.293C11.1054,16.4805 11,16.7348 11,17C11,17.2652 11.1054,17.5195 11.2929,17.707C11.4804,17.8946 11.7348,18 12,18C13.3256,17.9984 14.5964,17.471 15.5338,16.5337C16.4711,15.5964 16.9984,14.3256 17,13C17,12.7348 16.8946,12.4805 16.7071,12.293C16.5196,12.1054 16.2652,12 16,12Z"
+ android:fillColor="#0080FF"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_water_filled.xml b/packages/EasterEgg/res/drawable/ic_water_filled.xml
new file mode 100644
index 000000000000..eed171d05668
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_water_filled.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.654,7.563L12,2L6.346,7.563C5.6036,8.2877 5.0136,9.1533 4.6108,10.1094C4.2079,11.0654 4.0002,12.0924 4,13.1299C4.0174,15.2343 4.87,17.2458 6.3703,18.7217C7.8705,20.1975 9.8956,21.017 12,21C14.1044,21.017 16.1295,20.1975 17.6297,18.7217C19.13,17.2458 19.9826,15.2343 20,13.1299C20,12.0924 19.7925,11.0654 19.3896,10.1094C18.9867,9.1533 18.3966,8.2875 17.654,7.563V7.563ZM12,18C11.7348,18 11.4804,17.8946 11.2929,17.707C11.1054,17.5195 11,17.2652 11,17C11,16.7348 11.1054,16.4805 11.2929,16.293C11.4804,16.1054 11.7348,16 12,16C12.7956,16 13.5587,15.6837 14.1213,15.1211C14.6839,14.5585 15,13.7956 15,13C15,12.7348 15.1054,12.4805 15.2929,12.293C15.4804,12.1054 15.7348,12 16,12C16.2652,12 16.5196,12.1054 16.7071,12.293C16.8946,12.4805 17,12.7348 17,13C16.9984,14.3256 16.4711,15.5964 15.5338,16.5337C14.5964,17.471 13.3256,17.9984 12,18Z"
+ android:fillColor="#0080FF"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_waterbowl_filled.xml b/packages/EasterEgg/res/drawable/ic_waterbowl_filled.xml
new file mode 100644
index 000000000000..28b1fa824060
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_waterbowl_filled.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M3,19L21,19"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#000000"/>
+ <path
+ android:pathData="M6.0583,11.6637C6.2004,11.2657 6.5774,11 7,11H17C17.4226,11 17.7996,11.2657 17.9418,11.6637L19.8476,17H4.1524L6.0583,11.6637ZM7.5,12L6,16H7L8.5,12H7.5Z"
+ android:fillColor="#000000"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M13.4135,6.3907L12,5L10.5865,6.3907C10.4009,6.5719 10.2534,6.7883 10.1527,7.0273C10.052,7.2663 10.0001,7.5231 10,7.7825C10.0044,8.3086 10.2175,8.8115 10.5926,9.1804C10.9676,9.5494 11.4739,9.7543 12,9.75C12.5261,9.7543 13.0324,9.5494 13.4074,9.1804C13.7825,8.8115 13.9956,8.3086 14,7.7825C14,7.5231 13.9481,7.2664 13.8474,7.0273C13.7467,6.7883 13.5991,6.5719 13.4135,6.3907V6.3907ZM12,9C11.9337,9 11.8701,8.9736 11.8232,8.9268C11.7763,8.8799 11.75,8.8163 11.75,8.75C11.75,8.6837 11.7763,8.6201 11.8232,8.5732C11.8701,8.5264 11.9337,8.5 12,8.5C12.1989,8.5 12.3897,8.4209 12.5303,8.2803C12.671,8.1396 12.75,7.9489 12.75,7.75C12.75,7.6837 12.7763,7.6201 12.8232,7.5732C12.8701,7.5264 12.9337,7.5 13,7.5C13.0663,7.5 13.1299,7.5264 13.1768,7.5732C13.2237,7.6201 13.25,7.6837 13.25,7.75C13.2496,8.0814 13.1178,8.3991 12.8834,8.6334C12.6491,8.8678 12.3314,8.9996 12,9Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/icon.xml b/packages/EasterEgg/res/drawable/icon.xml
new file mode 100644
index 000000000000..7f8d4fa8833f
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/icon.xml
@@ -0,0 +1,19 @@
+<!--
+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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/icon_bg"/>
+ <foreground android:drawable="@drawable/android_11_dial"/>
+</adaptive-icon>
diff --git a/packages/EasterEgg/res/drawable/icon_bg.xml b/packages/EasterEgg/res/drawable/icon_bg.xml
index 659f98be4f43..31b2a7f9a333 100644
--- a/packages/EasterEgg/res/drawable/icon_bg.xml
+++ b/packages/EasterEgg/res/drawable/icon_bg.xml
@@ -1,8 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2018 The Android Open Source Project
+Copyright (C) 2018 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -15,4 +14,5 @@
limitations under the License.
-->
<color xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/q_clue_text" />
+ android:color="#073042" />
+
diff --git a/packages/EasterEgg/res/drawable/left_ear.xml b/packages/EasterEgg/res/drawable/left_ear.xml
new file mode 100644
index 000000000000..2b98736df039
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/left_ear.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="left_ear" android:fillColor="#FF000000" android:pathData="M15.4,1l5.1000004,5.3l-6.3,2.8000002z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/left_ear_inside.xml b/packages/EasterEgg/res/drawable/left_ear_inside.xml
new file mode 100644
index 000000000000..1d947edc31e2
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/left_ear_inside.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="left_ear_inside" android:fillColor="#FF000000" android:pathData="M15.4,1l3.5,6.2l-4.7,1.9z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/left_eye.xml b/packages/EasterEgg/res/drawable/left_eye.xml
new file mode 100644
index 000000000000..4dde1b661393
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/left_eye.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="left_eye" android:fillColor="#FF000000" android:pathData="M20.5,11c0,1.7 -3,1.7 -3,0C17.5,9.3 20.5,9.3 20.5,11z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg1.xml b/packages/EasterEgg/res/drawable/leg1.xml
new file mode 100644
index 000000000000..d72c746b6232
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg1.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg1" android:fillColor="#FF000000" android:pathData="M9,37h5v6h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2.xml b/packages/EasterEgg/res/drawable/leg2.xml
new file mode 100644
index 000000000000..a772a870af7d
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg2.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg2" android:fillColor="#FF000000" android:pathData="M16,37h5v6h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2_shadow.xml b/packages/EasterEgg/res/drawable/leg2_shadow.xml
new file mode 100644
index 000000000000..b01bd6995c0b
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg2_shadow.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg2_shadow" android:fillColor="#FF000000" android:pathData="M16,37h5v3h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg3.xml b/packages/EasterEgg/res/drawable/leg3.xml
new file mode 100644
index 000000000000..d471236687b5
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg3.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg3" android:fillColor="#FF000000" android:pathData="M27,37h5v6h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/leg4.xml b/packages/EasterEgg/res/drawable/leg4.xml
new file mode 100644
index 000000000000..e5868eb80c59
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/leg4.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="leg4" android:fillColor="#FF000000" android:pathData="M34,37h5v6h-5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/mouth.xml b/packages/EasterEgg/res/drawable/mouth.xml
new file mode 100644
index 000000000000..ddcf2e82f976
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/mouth.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="mouth"
+ android:strokeColor="#FF000000"
+ android:strokeWidth="1.2"
+ android:strokeLineCap="round"
+ android:pathData="M29,14.3c-0.4,0.8 -1.3,1.4 -2.3,1.4c-1.4,0 -2.7,-1.3 -2.7,-2.7
+ M24,13c0,1.5 -1.2,2.7 -2.7,2.7c-1,0 -1.9,-0.5 -2.3,-1.4"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/nose.xml b/packages/EasterEgg/res/drawable/nose.xml
new file mode 100644
index 000000000000..d403cd1baadf
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/nose.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="nose" android:fillColor="#FF000000" android:pathData="M25.2,13c0,1.3 -2.3,1.3 -2.3,0S25.2,11.7 25.2,13z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/octo_bg.xml b/packages/EasterEgg/res/drawable/octo_bg.xml
new file mode 100644
index 000000000000..1e46cf434a8b
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/octo_bg.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient android:angle="-90"
+ android:startColor="#FF205090"
+ android:endColor="#FF001040"
+ android:type="linear"
+ />
+</shape> \ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/right_ear.xml b/packages/EasterEgg/res/drawable/right_ear.xml
new file mode 100644
index 000000000000..b9fb4d1c7470
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/right_ear.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="right_ear" android:fillColor="#FF000000" android:pathData="M32.6,1l-5.0999985,5.3l6.299999,2.8000002z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/right_ear_inside.xml b/packages/EasterEgg/res/drawable/right_ear_inside.xml
new file mode 100644
index 000000000000..86b6e3428d1f
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/right_ear_inside.xml
@@ -0,0 +1,23 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+
+ <path android:name="right_ear_inside" android:fillColor="#FF000000" android:pathData="M33.8,9.1l-4.7,-1.9l3.5,-6.2z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/right_eye.xml b/packages/EasterEgg/res/drawable/right_eye.xml
new file mode 100644
index 000000000000..a1871a62c25b
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/right_eye.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="right_eye" android:fillColor="#FF000000" android:pathData="M30.5,11c0,1.7 -3,1.7 -3,0C27.5,9.3 30.5,9.3 30.5,11z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/stat_icon.xml b/packages/EasterEgg/res/drawable/stat_icon.xml
new file mode 100644
index 000000000000..608cb2017c3f
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/stat_icon.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.5,2 2,6.5 2,12c0,5.5 4.5,10 10,10s10,-4.5 10,-10C22,6.5 17.5,2 12,2zM5.5,11c0,-1.6 3,-1.6 3,0C8.5,12.7 5.5,12.7 5.5,11zM17.5,14.6c-0.6,1 -1.7,1.7 -2.9,1.7c-1.1,0 -2,-0.6 -2.6,-1.4c-0.6,0.9 -1.6,1.4 -2.7,1.4c-1.3,0 -2.3,-0.7 -2.9,-1.8c-0.2,-0.3 0,-0.7 0.3,-0.8c0.3,-0.2 0.7,0 0.8,0.3c0.3,0.7 1,1.1 1.8,1.1c0.9,0 1.6,-0.5 1.9,-1.3c-0.2,-0.2 -0.4,-0.4 -0.4,-0.7c0,-1.3 2.3,-1.3 2.3,0c0,0.3 -0.2,0.6 -0.4,0.7c0.3,0.8 1.1,1.3 1.9,1.3c0.8,0 1.5,-0.6 1.8,-1.1c0.2,-0.3 0.6,-0.4 0.9,-0.2C17.6,13.9 17.7,14.3 17.5,14.6zM15.5,11c0,-1.6 3,-1.6 3,0C18.5,12.7 15.5,12.7 15.5,11z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.2,1.0l4.1000004,4.2l-5.0,2.1000004z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.8,1.0l-4.0999994,4.2l5.000001,2.1000004z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/tail.xml b/packages/EasterEgg/res/drawable/tail.xml
new file mode 100644
index 000000000000..0cca23c3e16c
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/tail.xml
@@ -0,0 +1,26 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="tail"
+ android:strokeColor="#FF000000"
+ android:strokeWidth="5"
+ android:strokeLineCap="round"
+ android:pathData="M35,35.5h5.9c2.1,0 3.8,-1.7 3.8,-3.8v-6.2"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/tail_cap.xml b/packages/EasterEgg/res/drawable/tail_cap.xml
new file mode 100644
index 000000000000..b82f6f9b478a
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/tail_cap.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="tail_cap" android:fillColor="#FF000000" android:pathData="M42.2,25.5c0,-1.4 1.1,-2.5 2.5,-2.5s2.5,1.1 2.5,2.5H42.2z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/tail_shadow.xml b/packages/EasterEgg/res/drawable/tail_shadow.xml
new file mode 100644
index 000000000000..bb1ff12b3afe
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/tail_shadow.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:name="tail_shadow" android:fillColor="#FF000000" android:pathData="M40,38l0,-5l-1,0l0,5z"/>
+</vector>
diff --git a/packages/EasterEgg/res/layout/activity_paint.xml b/packages/EasterEgg/res/layout/activity_paint.xml
index a4c17afd1531..8e916b021bbd 100644
--- a/packages/EasterEgg/res/layout/activity_paint.xml
+++ b/packages/EasterEgg/res/layout/activity_paint.xml
@@ -16,7 +16,7 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- xmlns:app="http://schemas.android.com/apk/res/com.android.egg"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#666"
@@ -45,4 +45,4 @@
/>
-</FrameLayout> \ No newline at end of file
+</FrameLayout>
diff --git a/packages/EasterEgg/res/layout/cat_view.xml b/packages/EasterEgg/res/layout/cat_view.xml
new file mode 100644
index 000000000000..85b494d2e68d
--- /dev/null
+++ b/packages/EasterEgg/res/layout/cat_view.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:gravity="center_horizontal"
+ android:clipToPadding="false">
+
+ <FrameLayout
+ android:layout_width="96dp"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter" />
+
+ <LinearLayout
+ android:id="@+id/contextGroup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ android:layout_gravity="bottom">
+
+ <ImageView
+ android:id="@android:id/shareText"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:padding="8dp"
+ android:src="@drawable/ic_share"
+ android:scaleType="fitCenter"
+ android:background="#40000000"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <ImageView
+ android:id="@android:id/closeButton"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:padding="4dp"
+ android:src="@drawable/ic_close"
+ android:scaleType="fitCenter"
+ android:background="#40000000"/>
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:gravity="center"/>
+</LinearLayout>
+
diff --git a/packages/EasterEgg/res/layout/edit_text.xml b/packages/EasterEgg/res/layout/edit_text.xml
new file mode 100644
index 000000000000..9f7ac802bad4
--- /dev/null
+++ b/packages/EasterEgg/res/layout/edit_text.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT 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:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="20dp"
+ android:paddingEnd="20dp">
+
+ <EditText
+ android:id="@android:id/edit"
+ android:maxLines="1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/food_layout.xml b/packages/EasterEgg/res/layout/food_layout.xml
new file mode 100644
index 000000000000..d0ca0c8899aa
--- /dev/null
+++ b/packages/EasterEgg/res/layout/food_layout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:paddingLeft="4dp" android:paddingRight="4dp"
+ android:paddingBottom="6dp" android:paddingTop="6dp">
+ <ImageView
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:id="@+id/icon"
+ android:tint="?android:attr/colorControlNormal"/>
+ <TextView android:layout_width="64dp" android:layout_height="wrap_content"
+ android:gravity="top|center_horizontal"
+ android:id="@+id/text" />
+</LinearLayout>
diff --git a/packages/EasterEgg/res/layout/neko_activity.xml b/packages/EasterEgg/res/layout/neko_activity.xml
new file mode 100644
index 000000000000..c258137ca710
--- /dev/null
+++ b/packages/EasterEgg/res/layout/neko_activity.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/holder"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"/>
+</FrameLayout> \ No newline at end of file
diff --git a/packages/EasterEgg/res/values/cat_strings.xml b/packages/EasterEgg/res/values/cat_strings.xml
new file mode 100644
index 000000000000..5214fc1ab01d
--- /dev/null
+++ b/packages/EasterEgg/res/values/cat_strings.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="notification_name" translatable="false">Android Neko</string>
+ <string name="notification_channel_name" translatable="false">New cats</string>
+ <string name="default_tile_name" translatable="false">\????</string>
+ <string name="notification_title" translatable="false">A cat is here.</string>
+ <string name="default_cat_name" translatable="false">Cat #%s</string>
+ <string name="directory_name" translatable="false">Cats</string>
+ <string name="confirm_delete" translatable="false">Forget %s?</string>
+ <string-array name="food_names" translatable="false">
+ <item>Empty dish</item>
+ <item>Bits</item>
+ <item>Fish</item>
+ <item>Chicken</item>
+ <item>Treat</item>
+ </string-array>
+ <array name="food_icons">
+ <item>@drawable/food_dish</item>
+ <item>@drawable/food_bits</item>
+ <item>@drawable/food_sysuituna</item>
+ <item>@drawable/food_chicken</item>
+ <item>@drawable/food_cookie</item>
+ </array>
+ <integer-array name="food_intervals">
+ <item>0</item>
+ <item>15</item>
+ <item>30</item>
+ <item>60</item>
+ <item>120</item>
+ </integer-array>
+ <integer-array name="food_new_cat_prob">
+ <item>0</item>
+ <item>5</item>
+ <item>35</item>
+ <item>65</item>
+ <item>90</item>
+ </integer-array>
+ <string-array name="cat_messages" translatable="false">
+ <item>😸</item>
+ <item>😹</item>
+ <item>😺</item>
+ <item>😻</item>
+ <item>😼</item>
+ <item>😽</item>
+ <item>😾</item>
+ <item>😿</item>
+ <item>🙀</item>
+ <item>💩</item>
+ <item>🐁</item>
+ </string-array>
+ <string-array name="rare_cat_messages" translatable="false">
+ <item>🍩</item>
+ <item>🍭</item>
+ <item>🍫</item>
+ <item>🍨</item>
+ <item>🔔</item>
+ <item>🐝</item>
+ <item>🍪</item>
+ <item>🥧</item>
+ </string-array>
+ <string name="control_toy_title" translatable="false">Toy</string>
+ <string name="control_toy_subtitle" translatable="false">Tap to use</string>
+ <string name="control_toy_status" translatable="false">Cat attracted!</string>
+ <string name="control_water_title" translatable="false">Water bubbler</string>
+ <string name="control_water_subtitle" translatable="false">Swipe to fill</string>
+ <string name="control_food_title" translatable="false">Food bowl</string>
+ <string name="control_food_subtitle" translatable="false">Tap to refill</string>
+ <string name="control_food_status_full" translatable="false">Full</string>
+ <string name="control_food_status_empty" translatable="false">Empty</string>
+</resources>
+
diff --git a/packages/EasterEgg/res/values/dimens.xml b/packages/EasterEgg/res/values/dimens.xml
new file mode 100644
index 000000000000..e9dcebd27f7b
--- /dev/null
+++ b/packages/EasterEgg/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <dimen name="neko_display_size">64dp</dimen>
+</resources>
diff --git a/packages/EasterEgg/res/values/strings.xml b/packages/EasterEgg/res/values/strings.xml
index b95ec6be4c84..25f94215d433 100644
--- a/packages/EasterEgg/res/values/strings.xml
+++ b/packages/EasterEgg/res/values/strings.xml
@@ -14,11 +14,13 @@ Copyright (C) 2018 The Android Open Source Project
limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <string name="app_name" translatable="false">Android Q Easter Egg</string>
+ <string name="app_name" translatable="false">Android R Easter Egg</string>
<!-- name of the Q easter egg, a nonogram-style icon puzzle -->
<string name="q_egg_name" translatable="false">Icon Quiz</string>
<!-- name of the P easter egg, a humble paint program -->
<string name="p_egg_name" translatable="false">PAINT.APK</string>
+
+ <string name="r_egg_name" translatable="false">Cat Controls</string>
</resources>
diff --git a/packages/SystemUI/res/drawable/pip_expand.xml b/packages/EasterEgg/res/xml/filepaths.xml
index cdb2ee50482f..2130025e9265 100644
--- a/packages/SystemUI/res/drawable/pip_expand.xml
+++ b/packages/EasterEgg/res/xml/filepaths.xml
@@ -14,16 +14,6 @@ Copyright (C) 2017 The Android Open Source Project
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="36dp"
- android:height="36dp"
- android:viewportWidth="36"
- android:viewportHeight="36">
-
- <path
- android:pathData="M0 0h36v36H0z" />
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M10 21H7v8h8v-3h-5v-5zm-3-6h3v-5h5V7H7v8zm19 11h-5v3h8v-8h-3v5zM21
-7v3h5v5h3V7h-8z" />
-</vector> \ No newline at end of file
+<paths>
+ <external-path name="cats" path="Pictures/Cats" />
+</paths> \ No newline at end of file
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
new file mode 100644
index 000000000000..cd59a735068b
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.egg.neko;
+
+import static com.android.egg.neko.NekoLand.CHAN_ID;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+import com.android.egg.R;
+import com.android.internal.logging.MetricsLogger;
+
+import java.io.ByteArrayOutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+/** It's a cat. */
+public class Cat extends Drawable {
+ public static final long[] PURR = {0, 40, 20, 40, 20, 40, 20, 40, 20, 40, 20, 40};
+
+ public static final boolean ALL_CATS_IN_ONE_CONVERSATION = true;
+
+ public static final String GLOBAL_SHORTCUT_ID = "com.android.egg.neko:allcats";
+ public static final String SHORTCUT_ID_PREFIX = "com.android.egg.neko:cat:";
+
+ private Random mNotSoRandom;
+ private Bitmap mBitmap;
+ private long mSeed;
+ private String mName;
+ private int mBodyColor;
+ private int mFootType;
+ private boolean mBowTie;
+ private String mFirstMessage;
+
+ private synchronized Random notSoRandom(long seed) {
+ if (mNotSoRandom == null) {
+ mNotSoRandom = new Random();
+ mNotSoRandom.setSeed(seed);
+ }
+ return mNotSoRandom;
+ }
+
+ public static final float frandrange(Random r, float a, float b) {
+ return (b - a) * r.nextFloat() + a;
+ }
+
+ public static final Object choose(Random r, Object... l) {
+ return l[r.nextInt(l.length)];
+ }
+
+ public static final int chooseP(Random r, int[] a) {
+ return chooseP(r, a, 1000);
+ }
+
+ public static final int chooseP(Random r, int[] a, int sum) {
+ int pct = r.nextInt(sum);
+ final int stop = a.length - 2;
+ int i = 0;
+ while (i < stop) {
+ pct -= a[i];
+ if (pct < 0) break;
+ i += 2;
+ }
+ return a[i + 1];
+ }
+
+ public static final int getColorIndex(int q, int[] a) {
+ for (int i = 1; i < a.length; i += 2) {
+ if (a[i] == q) {
+ return i / 2;
+ }
+ }
+ return -1;
+ }
+
+ public static final int[] P_BODY_COLORS = {
+ 180, 0xFF212121, // black
+ 180, 0xFFFFFFFF, // white
+ 140, 0xFF616161, // gray
+ 140, 0xFF795548, // brown
+ 100, 0xFF90A4AE, // steel
+ 100, 0xFFFFF9C4, // buff
+ 100, 0xFFFF8F00, // orange
+ 5, 0xFF29B6F6, // blue..?
+ 5, 0xFFFFCDD2, // pink!?
+ 5, 0xFFCE93D8, // purple?!?!?
+ 4, 0xFF43A047, // yeah, why not green
+ 1, 0, // ?!?!?!
+ };
+
+ public static final int[] P_COLLAR_COLORS = {
+ 250, 0xFFFFFFFF,
+ 250, 0xFF000000,
+ 250, 0xFFF44336,
+ 50, 0xFF1976D2,
+ 50, 0xFFFDD835,
+ 50, 0xFFFB8C00,
+ 50, 0xFFF48FB1,
+ 50, 0xFF4CAF50,
+ };
+
+ public static final int[] P_BELLY_COLORS = {
+ 750, 0,
+ 250, 0xFFFFFFFF,
+ };
+
+ public static final int[] P_DARK_SPOT_COLORS = {
+ 700, 0,
+ 250, 0xFF212121,
+ 50, 0xFF6D4C41,
+ };
+
+ public static final int[] P_LIGHT_SPOT_COLORS = {
+ 700, 0,
+ 300, 0xFFFFFFFF,
+ };
+
+ private CatParts D;
+
+ public static void tint(int color, Drawable... ds) {
+ for (Drawable d : ds) {
+ if (d != null) {
+ d.mutate().setTint(color);
+ }
+ }
+ }
+
+ public static boolean isDark(int color) {
+ final int r = (color & 0xFF0000) >> 16;
+ final int g = (color & 0x00FF00) >> 8;
+ final int b = color & 0x0000FF;
+ return (r + g + b) < 0x80;
+ }
+
+ public Cat(Context context, long seed) {
+ D = new CatParts(context);
+ mSeed = seed;
+
+ setName(context.getString(R.string.default_cat_name,
+ String.valueOf(mSeed % 1000)));
+
+ final Random nsr = notSoRandom(seed);
+
+ // body color
+ mBodyColor = chooseP(nsr, P_BODY_COLORS);
+ if (mBodyColor == 0) mBodyColor = Color.HSVToColor(new float[]{
+ nsr.nextFloat() * 360f, frandrange(nsr, 0.5f, 1f), frandrange(nsr, 0.5f, 1f)});
+
+ tint(mBodyColor, D.body, D.head, D.leg1, D.leg2, D.leg3, D.leg4, D.tail,
+ D.leftEar, D.rightEar, D.foot1, D.foot2, D.foot3, D.foot4, D.tailCap);
+ tint(0x20000000, D.leg2Shadow, D.tailShadow);
+ if (isDark(mBodyColor)) {
+ tint(0xFFFFFFFF, D.leftEye, D.rightEye, D.mouth, D.nose);
+ }
+ tint(isDark(mBodyColor) ? 0xFFEF9A9A : 0x20D50000, D.leftEarInside, D.rightEarInside);
+
+ tint(chooseP(nsr, P_BELLY_COLORS), D.belly);
+ tint(chooseP(nsr, P_BELLY_COLORS), D.back);
+ final int faceColor = chooseP(nsr, P_BELLY_COLORS);
+ tint(faceColor, D.faceSpot);
+ if (!isDark(faceColor)) {
+ tint(0xFF000000, D.mouth, D.nose);
+ }
+
+ mFootType = 0;
+ if (nsr.nextFloat() < 0.25f) {
+ mFootType = 4;
+ tint(0xFFFFFFFF, D.foot1, D.foot2, D.foot3, D.foot4);
+ } else {
+ if (nsr.nextFloat() < 0.25f) {
+ mFootType = 2;
+ tint(0xFFFFFFFF, D.foot1, D.foot3);
+ } else if (nsr.nextFloat() < 0.25f) {
+ mFootType = 3; // maybe -2 would be better? meh.
+ tint(0xFFFFFFFF, D.foot2, D.foot4);
+ } else if (nsr.nextFloat() < 0.1f) {
+ mFootType = 1;
+ tint(0xFFFFFFFF, (Drawable) choose(nsr, D.foot1, D.foot2, D.foot3, D.foot4));
+ }
+ }
+
+ tint(nsr.nextFloat() < 0.333f ? 0xFFFFFFFF : mBodyColor, D.tailCap);
+
+ final int capColor = chooseP(nsr, isDark(mBodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS);
+ tint(capColor, D.cap);
+ //tint(chooseP(nsr, isDark(bodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS), D.nose);
+
+ final int collarColor = chooseP(nsr, P_COLLAR_COLORS);
+ tint(collarColor, D.collar);
+ mBowTie = nsr.nextFloat() < 0.1f;
+ tint(mBowTie ? collarColor : 0, D.bowtie);
+
+ String[] messages = context.getResources().getStringArray(
+ nsr.nextFloat() < 0.1f ? R.array.rare_cat_messages : R.array.cat_messages);
+ mFirstMessage = (String) choose(nsr, (Object[]) messages);
+ if (nsr.nextFloat() < 0.5f) mFirstMessage = mFirstMessage + mFirstMessage + mFirstMessage;
+ }
+
+ public static Cat fromShortcutId(Context context, String shortcutId) {
+ if (shortcutId.startsWith(SHORTCUT_ID_PREFIX)) {
+ return new Cat(context, Long.parseLong(shortcutId.replace(SHORTCUT_ID_PREFIX, "")));
+ }
+ return null;
+ }
+
+ public static Cat create(Context context) {
+ return new Cat(context, Math.abs(ThreadLocalRandom.current().nextInt()));
+ }
+
+ public Notification.Builder buildNotification(Context context) {
+ final Bundle extras = new Bundle();
+ extras.putString("android.substName", context.getString(R.string.notification_name));
+
+ final Icon notificationIcon = createNotificationLargeIcon(context);
+
+ final Intent intent = new Intent(Intent.ACTION_MAIN)
+ .setClass(context, NekoLand.class)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ ShortcutInfo shortcut = new ShortcutInfo.Builder(context, getShortcutId())
+ .setActivity(intent.getComponent())
+ .setIntent(intent)
+ .setShortLabel(getName())
+ .setIcon(createShortcutIcon(context))
+ .setLongLived(true)
+ .build();
+ context.getSystemService(ShortcutManager.class).addDynamicShortcuts(List.of(shortcut));
+
+ Notification.BubbleMetadata bubbs = new Notification.BubbleMetadata.Builder()
+ .setIntent(
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE))
+ .setIcon(notificationIcon)
+ .setSuppressNotification(false)
+ .setDesiredHeight(context.getResources().getDisplayMetrics().heightPixels)
+ .build();
+
+ return new Notification.Builder(context, CHAN_ID)
+ .setSmallIcon(Icon.createWithResource(context, R.drawable.stat_icon))
+ .setLargeIcon(notificationIcon)
+ .setColor(getBodyColor())
+ .setContentTitle(context.getString(R.string.notification_title))
+ .setShowWhen(true)
+ .setCategory(Notification.CATEGORY_STATUS)
+ .setContentText(getName())
+ .setContentIntent(
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE))
+ .setAutoCancel(true)
+ .setStyle(new Notification.MessagingStyle(createPerson())
+ .addMessage(mFirstMessage, System.currentTimeMillis(), createPerson())
+ .setConversationTitle(getName())
+ )
+ .setBubbleMetadata(bubbs)
+ .setShortcutId(getShortcutId())
+ .addExtras(extras);
+ }
+
+ private Person createPerson() {
+ return new Person.Builder()
+ .setName(getName())
+ .setBot(true)
+ .setKey(getShortcutId())
+ .build();
+ }
+
+ public long getSeed() {
+ return mSeed;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final int w = Math.min(canvas.getWidth(), canvas.getHeight());
+ final int h = w;
+
+ if (mBitmap == null || mBitmap.getWidth() != w || mBitmap.getHeight() != h) {
+ mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ final Canvas bitCanvas = new Canvas(mBitmap);
+ slowDraw(bitCanvas, 0, 0, w, h);
+ }
+ canvas.drawBitmap(mBitmap, 0, 0, null);
+ }
+
+ private void slowDraw(Canvas canvas, int x, int y, int w, int h) {
+ for (int i = 0; i < D.drawingOrder.length; i++) {
+ final Drawable d = D.drawingOrder[i];
+ if (d != null) {
+ d.setBounds(x, y, x + w, y + h);
+ d.draw(canvas);
+ }
+ }
+
+ }
+
+ public Bitmap createBitmap(int w, int h) {
+ if (mBitmap != null && mBitmap.getWidth() == w && mBitmap.getHeight() == h) {
+ return mBitmap.copy(mBitmap.getConfig(), true);
+ }
+ Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ slowDraw(new Canvas(result), 0, 0, w, h);
+ return result;
+ }
+
+ public static Icon recompressIcon(Icon bitmapIcon) {
+ if (bitmapIcon.getType() != Icon.TYPE_BITMAP) return bitmapIcon;
+ try {
+ final Bitmap bits = (Bitmap) Icon.class.getDeclaredMethod("getBitmap").invoke(bitmapIcon);
+ final ByteArrayOutputStream ostream = new ByteArrayOutputStream(
+ bits.getWidth() * bits.getHeight() * 2); // guess 50% compression
+ final boolean ok = bits.compress(Bitmap.CompressFormat.PNG, 100, ostream);
+ if (!ok) return null;
+ return Icon.createWithData(ostream.toByteArray(), 0, ostream.size());
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
+ return bitmapIcon;
+ }
+ }
+
+ public Icon createNotificationLargeIcon(Context context) {
+ final Resources res = context.getResources();
+ final int w = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
+ final int h = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
+ return recompressIcon(createIcon(context, w, h));
+ }
+
+ public Icon createShortcutIcon(Context context) {
+ // shortcuts do not support compressed bitmaps
+ final Resources res = context.getResources();
+ final int w = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
+ final int h = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
+ return createIcon(context, w, h);
+ }
+
+ public Icon createIcon(Context context, int w, int h) {
+ Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(result);
+ float[] hsv = new float[3];
+ Color.colorToHSV(mBodyColor, hsv);
+ hsv[2] = (hsv[2] > 0.5f)
+ ? (hsv[2] - 0.25f)
+ : (hsv[2] + 0.25f);
+ //final Paint pt = new Paint();
+ //pt.setColor(Color.HSVToColor(hsv));
+ //float r = w/2;
+ //canvas.drawCircle(r, r, r, pt);
+ // int m = w/10;
+
+ // Adaptive bitmaps!
+ canvas.drawColor(Color.HSVToColor(hsv));
+ int m = w / 4;
+
+ slowDraw(canvas, m, m, w - m - m, h - m - m);
+
+ return Icon.createWithAdaptiveBitmap(result);
+ }
+
+ @Override
+ public void setAlpha(int i) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setName(String name) {
+ this.mName = name;
+ }
+
+ public int getBodyColor() {
+ return mBodyColor;
+ }
+
+ public void logAdd(Context context) {
+ logCatAction(context, "egg_neko_add");
+ }
+
+ public void logRename(Context context) {
+ logCatAction(context, "egg_neko_rename");
+ }
+
+ public void logRemove(Context context) {
+ logCatAction(context, "egg_neko_remove");
+ }
+
+ public void logShare(Context context) {
+ logCatAction(context, "egg_neko_share");
+ }
+
+ private void logCatAction(Context context, String prefix) {
+ MetricsLogger.count(context, prefix, 1);
+ MetricsLogger.histogram(context, prefix + "_color",
+ getColorIndex(mBodyColor, P_BODY_COLORS));
+ MetricsLogger.histogram(context, prefix + "_bowtie", mBowTie ? 1 : 0);
+ MetricsLogger.histogram(context, prefix + "_feet", mFootType);
+ }
+
+ public String getShortcutId() {
+ return ALL_CATS_IN_ONE_CONVERSATION
+ ? GLOBAL_SHORTCUT_ID
+ : (SHORTCUT_ID_PREFIX + mSeed);
+ }
+
+ public static class CatParts {
+ public Drawable leftEar;
+ public Drawable rightEar;
+ public Drawable rightEarInside;
+ public Drawable leftEarInside;
+ public Drawable head;
+ public Drawable faceSpot;
+ public Drawable cap;
+ public Drawable mouth;
+ public Drawable body;
+ public Drawable foot1;
+ public Drawable leg1;
+ public Drawable foot2;
+ public Drawable leg2;
+ public Drawable foot3;
+ public Drawable leg3;
+ public Drawable foot4;
+ public Drawable leg4;
+ public Drawable tail;
+ public Drawable leg2Shadow;
+ public Drawable tailShadow;
+ public Drawable tailCap;
+ public Drawable belly;
+ public Drawable back;
+ public Drawable rightEye;
+ public Drawable leftEye;
+ public Drawable nose;
+ public Drawable bowtie;
+ public Drawable collar;
+ public Drawable[] drawingOrder;
+
+ public CatParts(Context context) {
+ body = context.getDrawable(R.drawable.body);
+ head = context.getDrawable(R.drawable.head);
+ leg1 = context.getDrawable(R.drawable.leg1);
+ leg2 = context.getDrawable(R.drawable.leg2);
+ leg3 = context.getDrawable(R.drawable.leg3);
+ leg4 = context.getDrawable(R.drawable.leg4);
+ tail = context.getDrawable(R.drawable.tail);
+ leftEar = context.getDrawable(R.drawable.left_ear);
+ rightEar = context.getDrawable(R.drawable.right_ear);
+ rightEarInside = context.getDrawable(R.drawable.right_ear_inside);
+ leftEarInside = context.getDrawable(R.drawable.left_ear_inside);
+ faceSpot = context.getDrawable(R.drawable.face_spot);
+ cap = context.getDrawable(R.drawable.cap);
+ mouth = context.getDrawable(R.drawable.mouth);
+ foot4 = context.getDrawable(R.drawable.foot4);
+ foot3 = context.getDrawable(R.drawable.foot3);
+ foot1 = context.getDrawable(R.drawable.foot1);
+ foot2 = context.getDrawable(R.drawable.foot2);
+ leg2Shadow = context.getDrawable(R.drawable.leg2_shadow);
+ tailShadow = context.getDrawable(R.drawable.tail_shadow);
+ tailCap = context.getDrawable(R.drawable.tail_cap);
+ belly = context.getDrawable(R.drawable.belly);
+ back = context.getDrawable(R.drawable.back);
+ rightEye = context.getDrawable(R.drawable.right_eye);
+ leftEye = context.getDrawable(R.drawable.left_eye);
+ nose = context.getDrawable(R.drawable.nose);
+ collar = context.getDrawable(R.drawable.collar);
+ bowtie = context.getDrawable(R.drawable.bowtie);
+ drawingOrder = getDrawingOrder();
+ }
+
+ private Drawable[] getDrawingOrder() {
+ return new Drawable[]{
+ collar,
+ leftEar, leftEarInside, rightEar, rightEarInside,
+ head,
+ faceSpot,
+ cap,
+ leftEye, rightEye,
+ nose, mouth,
+ tail, tailCap, tailShadow,
+ foot1, leg1,
+ foot2, leg2,
+ foot3, leg3,
+ foot4, leg4,
+ leg2Shadow,
+ body, belly,
+ bowtie
+ };
+ }
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Food.java b/packages/EasterEgg/src/com/android/egg/neko/Food.java
new file mode 100644
index 000000000000..aeffc4adfd3a
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/Food.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.egg.neko;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Icon;
+
+import com.android.egg.R;
+
+public class Food {
+ private final int mType;
+
+ private static int[] sIcons;
+ private static String[] sNames;
+
+ public Food(int type) {
+ mType = type;
+ }
+
+ public Icon getIcon(Context context) {
+ if (sIcons == null) {
+ TypedArray icons = context.getResources().obtainTypedArray(R.array.food_icons);
+ sIcons = new int[icons.length()];
+ for (int i = 0; i < sIcons.length; i++) {
+ sIcons[i] = icons.getResourceId(i, 0);
+ }
+ icons.recycle();
+ }
+ return Icon.createWithResource(context, sIcons[mType]);
+ }
+
+ public String getName(Context context) {
+ if (sNames == null) {
+ sNames = context.getResources().getStringArray(R.array.food_names);
+ }
+ return sNames[mType];
+ }
+
+ public long getInterval(Context context) {
+ return context.getResources().getIntArray(R.array.food_intervals)[mType];
+ }
+
+ public int getType() {
+ return mType;
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
new file mode 100644
index 000000000000..df461c6878f0
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT 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.egg.neko;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.internal.logging.MetricsLogger;
+
+public class NekoActivationActivity extends Activity {
+ private static final String R_EGG_UNLOCK_SETTING = "egg_mode_r";
+
+ private void toastUp(String s) {
+ Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
+ toast.show();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ final PackageManager pm = getPackageManager();
+ final ComponentName cn = new ComponentName(this, NekoControlsService.class);
+ final boolean componentEnabled = pm.getComponentEnabledSetting(cn)
+ == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ if (Settings.System.getLong(getContentResolver(),
+ R_EGG_UNLOCK_SETTING, 0) == 0) {
+ if (componentEnabled) {
+ Log.v("Neko", "Disabling controls.");
+ pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ MetricsLogger.histogram(this, "egg_neko_enable", 0);
+ toastUp("\uD83D\uDEAB");
+ } else {
+ Log.v("Neko", "Controls already disabled.");
+ }
+ } else {
+ if (!componentEnabled) {
+ Log.v("Neko", "Enabling controls.");
+ pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
+ MetricsLogger.histogram(this, "egg_neko_enable", 1);
+ toastUp("\uD83D\uDC31");
+ } else {
+ Log.v("Neko", "Controls already enabled.");
+ }
+ }
+ finish();
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoControlsService.kt b/packages/EasterEgg/src/com/android/egg/neko/NekoControlsService.kt
new file mode 100644
index 000000000000..56f599a3a219
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoControlsService.kt
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.egg.neko
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.content.res.ColorStateList
+import android.graphics.drawable.Icon
+import android.service.controls.Control
+import android.service.controls.ControlsProviderService
+import android.service.controls.DeviceTypes
+import android.service.controls.actions.ControlAction
+import android.service.controls.actions.FloatAction
+import android.service.controls.templates.ControlButton
+import android.service.controls.templates.RangeTemplate
+import android.service.controls.templates.StatelessTemplate
+import android.service.controls.templates.ToggleTemplate
+import android.text.SpannableStringBuilder
+import android.text.style.ForegroundColorSpan
+import android.util.Log
+import androidx.annotation.RequiresApi
+import com.android.internal.logging.MetricsLogger
+import java.util.Random
+import java.util.concurrent.Flow
+import java.util.function.Consumer
+
+import com.android.egg.R
+
+const val CONTROL_ID_WATER = "water"
+const val CONTROL_ID_FOOD = "food"
+const val CONTROL_ID_TOY = "toy"
+
+const val FOOD_SPAWN_CAT_DELAY_MINS = 5L
+
+const val COLOR_FOOD_FG = 0xFFFF8000.toInt()
+const val COLOR_FOOD_BG = COLOR_FOOD_FG and 0x40FFFFFF.toInt()
+const val COLOR_WATER_FG = 0xFF0080FF.toInt()
+const val COLOR_WATER_BG = COLOR_WATER_FG and 0x40FFFFFF.toInt()
+const val COLOR_TOY_FG = 0xFFFF4080.toInt()
+const val COLOR_TOY_BG = COLOR_TOY_FG and 0x40FFFFFF.toInt()
+
+val P_TOY_ICONS = intArrayOf(
+ 1, R.drawable.ic_toy_mouse,
+ 1, R.drawable.ic_toy_fish,
+ 1, R.drawable.ic_toy_ball,
+ 1, R.drawable.ic_toy_laser
+)
+
+@RequiresApi(30)
+fun Control_toString(control: Control): String {
+ val hc = String.format("0x%08x", control.hashCode())
+ return ("Control($hc id=${control.controlId}, type=${control.deviceType}, " +
+ "title=${control.title}, template=${control.controlTemplate})")
+}
+
+@RequiresApi(30)
+public class NekoControlsService : ControlsProviderService(), PrefState.PrefsListener {
+ private val TAG = "NekoControls"
+
+ private val controls = HashMap<String, Control>()
+ private val publishers = ArrayList<UglyPublisher>()
+ private val rng = Random()
+
+ private var lastToyIcon: Icon? = null
+
+ private lateinit var prefs: PrefState
+
+ override fun onCreate() {
+ super.onCreate()
+
+ prefs = PrefState(this)
+ prefs.setListener(this)
+
+ createDefaultControls()
+ }
+
+ override fun onPrefsChanged() {
+ createDefaultControls()
+ }
+
+ private fun createDefaultControls() {
+ val foodState: Int = prefs.foodState
+ if (foodState != 0) {
+ NekoService.registerJobIfNeeded(this, FOOD_SPAWN_CAT_DELAY_MINS)
+ }
+
+ val water = prefs.waterState
+
+ controls[CONTROL_ID_WATER] = makeWaterBowlControl(water)
+ controls[CONTROL_ID_FOOD] = makeFoodBowlControl(foodState != 0)
+ controls[CONTROL_ID_TOY] = makeToyControl(currentToyIcon(), false)
+ }
+
+ private fun currentToyIcon(): Icon {
+ val icon = lastToyIcon ?: randomToyIcon()
+ lastToyIcon = icon
+ return icon
+ }
+
+ private fun randomToyIcon(): Icon {
+ return Icon.createWithResource(resources, Cat.chooseP(rng, P_TOY_ICONS, 4))
+ }
+
+ private fun colorize(s: CharSequence, color: Int): CharSequence {
+ val ssb = SpannableStringBuilder(s)
+ ssb.setSpan(ForegroundColorSpan(color), 0, s.length, 0)
+ return ssb
+ }
+
+ private fun makeToyControl(icon: Icon?, thrown: Boolean): Control {
+ return Control.StatefulBuilder(CONTROL_ID_TOY, getPendingIntent())
+ .setDeviceType(DeviceTypes.TYPE_UNKNOWN)
+ .setCustomIcon(icon)
+ // ?.setTint(COLOR_TOY_FG)) // TODO(b/159559045): uncomment when fixed
+ .setCustomColor(ColorStateList.valueOf(COLOR_TOY_BG))
+ .setTitle(colorize(getString(R.string.control_toy_title), COLOR_TOY_FG))
+ .setStatusText(colorize(
+ if (thrown) getString(R.string.control_toy_status) else "",
+ COLOR_TOY_FG))
+ .setControlTemplate(StatelessTemplate("toy"))
+ .setStatus(Control.STATUS_OK)
+ .setSubtitle(if (thrown) "" else getString(R.string.control_toy_subtitle))
+ .setAppIntent(getAppIntent())
+ .build()
+ }
+
+ private fun makeWaterBowlControl(fillLevel: Float): Control {
+ return Control.StatefulBuilder(CONTROL_ID_WATER, getPendingIntent())
+ .setDeviceType(DeviceTypes.TYPE_KETTLE)
+ .setTitle(colorize(getString(R.string.control_water_title), COLOR_WATER_FG))
+ .setCustomColor(ColorStateList.valueOf(COLOR_WATER_BG))
+ .setCustomIcon(Icon.createWithResource(resources,
+ if (fillLevel >= 100f) R.drawable.ic_water_filled else R.drawable.ic_water))
+ //.setTint(COLOR_WATER_FG)) // TODO(b/159559045): uncomment when fixed
+ .setControlTemplate(RangeTemplate("waterlevel", 0f, 200f, fillLevel, 10f,
+ "%.0f mL"))
+ .setStatus(Control.STATUS_OK)
+ .setSubtitle(if (fillLevel == 0f) getString(R.string.control_water_subtitle) else "")
+ .build()
+ }
+
+ private fun makeFoodBowlControl(filled: Boolean): Control {
+ return Control.StatefulBuilder(CONTROL_ID_FOOD, getPendingIntent())
+ .setDeviceType(DeviceTypes.TYPE_UNKNOWN)
+ .setCustomColor(ColorStateList.valueOf(COLOR_FOOD_BG))
+ .setTitle(colorize(getString(R.string.control_food_title), COLOR_FOOD_FG))
+ .setCustomIcon(Icon.createWithResource(resources,
+ if (filled) R.drawable.ic_foodbowl_filled else R.drawable.ic_bowl))
+ // .setTint(COLOR_FOOD_FG)) // TODO(b/159559045): uncomment when fixed
+ .setStatusText(
+ if (filled) colorize(
+ getString(R.string.control_food_status_full), 0xCCFFFFFF.toInt())
+ else colorize(
+ getString(R.string.control_food_status_empty), 0x80FFFFFF.toInt()))
+ .setControlTemplate(ToggleTemplate("foodbowl", ControlButton(filled, "Refill")))
+ .setStatus(Control.STATUS_OK)
+ .setSubtitle(if (filled) "" else getString(R.string.control_food_subtitle))
+ .build()
+ }
+
+ private fun getPendingIntent(): PendingIntent {
+ val intent = Intent(Intent.ACTION_MAIN)
+ .setClass(this, NekoLand::class.java)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ return PendingIntent.getActivity(this, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
+ }
+
+ private fun getAppIntent(): PendingIntent {
+ return getPendingIntent()
+ }
+
+
+ override fun performControlAction(
+ controlId: String,
+ action: ControlAction,
+ consumer: Consumer<Int>
+ ) {
+ when (controlId) {
+ CONTROL_ID_FOOD -> {
+ // refill bowl
+ controls[CONTROL_ID_FOOD] = makeFoodBowlControl(true)
+ Log.v(TAG, "Bowl refilled. (Registering job.)")
+ NekoService.registerJob(this, FOOD_SPAWN_CAT_DELAY_MINS)
+ MetricsLogger.histogram(this, "egg_neko_offered_food", 11)
+ prefs.foodState = 11
+ }
+ CONTROL_ID_TOY -> {
+ Log.v(TAG, "Toy tossed.")
+ controls[CONTROL_ID_TOY] =
+ makeToyControl(currentToyIcon(), true)
+ // TODO: re-enable toy
+ Thread() {
+ Thread.sleep((1 + Random().nextInt(4)) * 1000L)
+ NekoService.getExistingCat(prefs)?.let {
+ NekoService.notifyCat(this, it)
+ }
+ controls[CONTROL_ID_TOY] = makeToyControl(randomToyIcon(), false)
+ pushControlChanges()
+ }.start()
+ }
+ CONTROL_ID_WATER -> {
+ if (action is FloatAction) {
+ controls[CONTROL_ID_WATER] = makeWaterBowlControl(action.newValue)
+ Log.v(TAG, "Water level set to " + action.newValue)
+ prefs.waterState = action.newValue
+ }
+ }
+ else -> {
+ return
+ }
+ }
+ consumer.accept(ControlAction.RESPONSE_OK)
+ pushControlChanges()
+ }
+
+ private fun pushControlChanges() {
+ Thread() {
+ publishers.forEach { it.refresh() }
+ }.start()
+ }
+
+ private fun makeStateless(c: Control?): Control? {
+ if (c == null) return null
+ return Control.StatelessBuilder(c.controlId, c.appIntent)
+ .setTitle(c.title)
+ .setSubtitle(c.subtitle)
+ .setStructure(c.structure)
+ .setDeviceType(c.deviceType)
+ .setCustomIcon(c.customIcon)
+ .setCustomColor(c.customColor)
+ .build()
+ }
+
+ override fun createPublisherFor(list: MutableList<String>): Flow.Publisher<Control> {
+ createDefaultControls()
+
+ val publisher = UglyPublisher(list, true)
+ publishers.add(publisher)
+ return publisher
+ }
+
+ override fun createPublisherForAllAvailable(): Flow.Publisher<Control> {
+ createDefaultControls()
+
+ val publisher = UglyPublisher(controls.keys, false)
+ publishers.add(publisher)
+ return publisher
+ }
+
+ private inner class UglyPublisher(
+ val controlKeys: Iterable<String>,
+ val indefinite: Boolean
+ ) : Flow.Publisher<Control> {
+ val subscriptions = ArrayList<UglySubscription>()
+
+ private inner class UglySubscription(
+ val initialControls: Iterator<Control>,
+ var subscriber: Flow.Subscriber<in Control>?
+ ) : Flow.Subscription {
+ override fun cancel() {
+ Log.v(TAG, "cancel subscription: $this for subscriber: $subscriber " +
+ "to publisher: $this@UglyPublisher")
+ subscriber = null
+ unsubscribe(this)
+ }
+
+ override fun request(p0: Long) {
+ (0 until p0).forEach { _ ->
+ if (initialControls.hasNext()) {
+ send(initialControls.next())
+ } else {
+ if (!indefinite) subscriber?.onComplete()
+ }
+ }
+ }
+
+ fun send(c: Control) {
+ Log.v(TAG, "sending update: " + Control_toString(c) + " => " + subscriber)
+ subscriber?.onNext(c)
+ }
+ }
+
+ override fun subscribe(subscriber: Flow.Subscriber<in Control>) {
+ Log.v(TAG, "subscribe to publisher: $this by subscriber: $subscriber")
+ val sub = UglySubscription(controlKeys.mapNotNull { controls[it] }.iterator(),
+ subscriber)
+ subscriptions.add(sub)
+ subscriber.onSubscribe(sub)
+ }
+
+ fun unsubscribe(sub: UglySubscription) {
+ Log.v(TAG, "no more subscriptions, removing subscriber: $sub")
+ subscriptions.remove(sub)
+ if (subscriptions.size == 0) {
+ Log.v(TAG, "no more subscribers, removing publisher: $this")
+ publishers.remove(this)
+ }
+ }
+
+ fun refresh() {
+ controlKeys.mapNotNull { controls[it] }.forEach { control ->
+ subscriptions.forEach { sub ->
+ sub.send(control)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
new file mode 100644
index 000000000000..2bd2228e7bf2
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.egg.neko;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.egg.R;
+
+import java.util.ArrayList;
+
+public class NekoDialog extends Dialog {
+
+ private final Adapter mAdapter;
+
+ public NekoDialog(@NonNull Context context) {
+ super(context, android.R.style.Theme_Material_Dialog_NoActionBar);
+ RecyclerView view = new RecyclerView(getContext());
+ mAdapter = new Adapter(getContext());
+ view.setLayoutManager(new GridLayoutManager(getContext(), 2));
+ view.setAdapter(mAdapter);
+ final float dp = context.getResources().getDisplayMetrics().density;
+ final int pad = (int)(16*dp);
+ view.setPadding(pad, pad, pad, pad);
+ setContentView(view);
+ }
+
+ private void onFoodSelected(Food food) {
+ PrefState prefs = new PrefState(getContext());
+ int currentState = prefs.getFoodState();
+ if (currentState == 0 && food.getType() != 0) {
+ NekoService.registerJob(getContext(), food.getInterval(getContext()));
+ }
+// MetricsLogger.histogram(getContext(), "egg_neko_offered_food", food.getType());
+ prefs.setFoodState(food.getType());
+ dismiss();
+ }
+
+ private class Adapter extends RecyclerView.Adapter<Holder> {
+
+ private final Context mContext;
+ private final ArrayList<Food> mFoods = new ArrayList<>();
+
+ public Adapter(Context context) {
+ mContext = context;
+ int[] foods = context.getResources().getIntArray(R.array.food_names);
+ // skip food 0, you can't choose it
+ for (int i=1; i<foods.length; i++) {
+ mFoods.add(new Food(i));
+ }
+ }
+
+ @Override
+ public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new Holder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.food_layout, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(final Holder holder, int position) {
+ final Food food = mFoods.get(position);
+ ((ImageView) holder.itemView.findViewById(R.id.icon))
+ .setImageIcon(food.getIcon(mContext));
+ ((TextView) holder.itemView.findViewById(R.id.text))
+ .setText(food.getName(mContext));
+ holder.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onFoodSelected(mFoods.get(holder.getAdapterPosition()));
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mFoods.size();
+ }
+ }
+
+ public static class Holder extends RecyclerView.ViewHolder {
+
+ public Holder(View itemView) {
+ super(itemView);
+ }
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
new file mode 100644
index 000000000000..8ed808760dcd
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.egg.neko;
+
+import android.Manifest;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.core.content.FileProvider;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.egg.R;
+import com.android.egg.neko.PrefState.PrefsListener;
+import com.android.internal.logging.MetricsLogger;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class NekoLand extends Activity implements PrefsListener {
+ public static String CHAN_ID = "EGG";
+
+ public static boolean DEBUG = false;
+ public static boolean DEBUG_NOTIFICATIONS = false;
+
+ private static final int EXPORT_BITMAP_SIZE = 600;
+
+ private static final int STORAGE_PERM_REQUEST = 123;
+
+ private static boolean CAT_GEN = false;
+ private PrefState mPrefs;
+ private CatAdapter mAdapter;
+ private Cat mPendingShareCat;
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.neko_activity);
+ final ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setLogo(Cat.create(this));
+ actionBar.setDisplayUseLogoEnabled(false);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mPrefs = new PrefState(this);
+ mPrefs.setListener(this);
+ final RecyclerView recyclerView = findViewById(R.id.holder);
+ mAdapter = new CatAdapter();
+ recyclerView.setAdapter(mAdapter);
+ recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
+ int numCats = updateCats();
+ MetricsLogger.histogram(this, "egg_neko_visit_gallery", numCats);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mPrefs.setListener(null);
+ }
+
+ private int updateCats() {
+ Cat[] cats;
+ if (CAT_GEN) {
+ cats = new Cat[50];
+ for (int i = 0; i < cats.length; i++) {
+ cats[i] = Cat.create(this);
+ }
+ } else {
+ final float[] hsv = new float[3];
+ List<Cat> list = mPrefs.getCats();
+ Collections.sort(list, new Comparator<Cat>() {
+ @Override
+ public int compare(Cat cat, Cat cat2) {
+ Color.colorToHSV(cat.getBodyColor(), hsv);
+ float bodyH1 = hsv[0];
+ Color.colorToHSV(cat2.getBodyColor(), hsv);
+ float bodyH2 = hsv[0];
+ return Float.compare(bodyH1, bodyH2);
+ }
+ });
+ cats = list.toArray(new Cat[0]);
+ }
+ mAdapter.setCats(cats);
+ return cats.length;
+ }
+
+ private void onCatClick(Cat cat) {
+ if (CAT_GEN) {
+ mPrefs.addCat(cat);
+ new AlertDialog.Builder(NekoLand.this)
+ .setTitle("Cat added")
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ } else {
+ showNameDialog(cat);
+ }
+ }
+
+ private void onCatRemove(Cat cat) {
+ cat.logRemove(this);
+ mPrefs.removeCat(cat);
+ }
+
+ private void showNameDialog(final Cat cat) {
+ final Context context = new ContextThemeWrapper(this,
+ android.R.style.Theme_Material_Light_Dialog_NoActionBar);
+ // TODO: Move to XML, add correct margins.
+ View view = LayoutInflater.from(context).inflate(R.layout.edit_text, null);
+ final EditText text = (EditText) view.findViewById(android.R.id.edit);
+ text.setText(cat.getName());
+ text.setSelection(cat.getName().length());
+ final int size = context.getResources()
+ .getDimensionPixelSize(android.R.dimen.app_icon_size);
+ Drawable catIcon = cat.createIcon(this, size, size).loadDrawable(this);
+ new AlertDialog.Builder(context)
+ .setTitle(" ")
+ .setIcon(catIcon)
+ .setView(view)
+ .setPositiveButton(android.R.string.ok, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ cat.logRename(context);
+ cat.setName(text.getText().toString().trim());
+ mPrefs.addCat(cat);
+ }
+ }).show();
+ }
+
+ @Override
+ public void onPrefsChanged() {
+ updateCats();
+ }
+
+ private class CatAdapter extends RecyclerView.Adapter<CatHolder> {
+
+ private Cat[] mCats;
+
+ public void setCats(Cat[] cats) {
+ mCats = cats;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public CatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new CatHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.cat_view, parent, false));
+ }
+
+ private void setContextGroupVisible(final CatHolder holder, boolean vis) {
+ final View group = holder.contextGroup;
+ if (vis && group.getVisibility() != View.VISIBLE) {
+ group.setAlpha(0);
+ group.setVisibility(View.VISIBLE);
+ group.animate().alpha(1.0f).setDuration(333);
+ Runnable hideAction = new Runnable() {
+ @Override
+ public void run() {
+ setContextGroupVisible(holder, false);
+ }
+ };
+ group.setTag(hideAction);
+ group.postDelayed(hideAction, 5000);
+ } else if (!vis && group.getVisibility() == View.VISIBLE) {
+ group.removeCallbacks((Runnable) group.getTag());
+ group.animate().alpha(0f).setDuration(250).withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ group.setVisibility(View.INVISIBLE);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(final CatHolder holder, int position) {
+ Context context = holder.itemView.getContext();
+ final int size = context.getResources().getDimensionPixelSize(R.dimen.neko_display_size);
+ holder.imageView.setImageIcon(mCats[position].createIcon(context, size, size));
+ holder.textView.setText(mCats[position].getName());
+ holder.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onCatClick(mCats[holder.getAdapterPosition()]);
+ }
+ });
+ holder.itemView.setOnLongClickListener(new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ setContextGroupVisible(holder, true);
+ return true;
+ }
+ });
+ holder.delete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setContextGroupVisible(holder, false);
+ new AlertDialog.Builder(NekoLand.this)
+ .setTitle(getString(R.string.confirm_delete, mCats[position].getName()))
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ onCatRemove(mCats[holder.getAdapterPosition()]);
+ }
+ })
+ .show();
+ }
+ });
+ holder.share.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setContextGroupVisible(holder, false);
+ Cat cat = mCats[holder.getAdapterPosition()];
+ if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ mPendingShareCat = cat;
+ requestPermissions(
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ STORAGE_PERM_REQUEST);
+ return;
+ }
+ shareCat(cat);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCats.length;
+ }
+ }
+
+ private void shareCat(Cat cat) {
+ final File dir = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
+ "Cats");
+ if (!dir.exists() && !dir.mkdirs()) {
+ Log.e("NekoLand", "save: error: can't create Pictures directory");
+ return;
+ }
+ final File png = new File(dir, cat.getName().replaceAll("[/ #:]+", "_") + ".png");
+ Bitmap bitmap = cat.createBitmap(EXPORT_BITMAP_SIZE, EXPORT_BITMAP_SIZE);
+ if (bitmap != null) {
+ try {
+ OutputStream os = new FileOutputStream(png);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 0, os);
+ os.close();
+ MediaScannerConnection.scanFile(
+ this,
+ new String[]{png.toString()},
+ new String[]{"image/png"},
+ null);
+ Log.v("Neko", "cat file: " + png);
+ Uri uri = FileProvider.getUriForFile(this, "com.android.egg.fileprovider", png);
+ Log.v("Neko", "cat uri: " + uri);
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
+ intent.putExtra(Intent.EXTRA_SUBJECT, cat.getName());
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setType("image/png");
+ startActivity(Intent.createChooser(intent, null)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
+ cat.logShare(this);
+ } catch (IOException e) {
+ Log.e("NekoLand", "save: error: " + e);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[], int[] grantResults) {
+ if (requestCode == STORAGE_PERM_REQUEST) {
+ if (mPendingShareCat != null) {
+ shareCat(mPendingShareCat);
+ mPendingShareCat = null;
+ }
+ }
+ }
+
+ private static class CatHolder extends RecyclerView.ViewHolder {
+ private final ImageView imageView;
+ private final TextView textView;
+ private final View contextGroup;
+ private final View delete;
+ private final View share;
+
+ public CatHolder(View itemView) {
+ super(itemView);
+ imageView = (ImageView) itemView.findViewById(android.R.id.icon);
+ textView = (TextView) itemView.findViewById(android.R.id.title);
+ contextGroup = itemView.findViewById(R.id.contextGroup);
+ delete = itemView.findViewById(android.R.id.closeButton);
+ share = itemView.findViewById(android.R.id.shareText);
+ }
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java
new file mode 100644
index 000000000000..ca89adc7b6e4
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.egg.neko;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+
+public class NekoLockedActivity extends Activity implements OnDismissListener {
+
+ private NekoDialog mDialog;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+
+ mDialog = new NekoDialog(this);
+ mDialog.setOnDismissListener(this);
+ mDialog.show();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ finish();
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
new file mode 100644
index 000000000000..939e85c07d06
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.egg.neko;
+
+import static com.android.egg.neko.Cat.PURR;
+import static com.android.egg.neko.NekoLand.CHAN_ID;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.egg.R;
+
+import java.util.List;
+import java.util.Random;
+
+public class NekoService extends JobService {
+
+ private static final String TAG = "NekoService";
+
+ public static int JOB_ID = 42;
+
+ public static int CAT_NOTIFICATION = 1;
+ public static int DEBUG_NOTIFICATION = 1234;
+
+ public static float CAT_CAPTURE_PROB = 1.0f; // generous
+
+ public static long SECONDS = 1000;
+ public static long MINUTES = 60 * SECONDS;
+
+ //public static long INTERVAL_FLEX = 15 * SECONDS;
+ public static long INTERVAL_FLEX = 5 * MINUTES;
+
+ public static float INTERVAL_JITTER_FRAC = 0.25f;
+
+ private static void setupNotificationChannels(Context context) {
+ NotificationManager noman = context.getSystemService(NotificationManager.class);
+ NotificationChannel eggChan = new NotificationChannel(CHAN_ID,
+ context.getString(R.string.notification_channel_name),
+ NotificationManager.IMPORTANCE_DEFAULT);
+ eggChan.setSound(Uri.EMPTY, Notification.AUDIO_ATTRIBUTES_DEFAULT); // cats are quiet
+ eggChan.setVibrationPattern(PURR); // not totally quiet though
+ //eggChan.setBlockableSystem(true); // unlike a real cat, you can push this one off your lap
+ eggChan.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); // cats sit in the window
+ noman.createNotificationChannel(eggChan);
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ Log.v(TAG, "Starting job: " + String.valueOf(params));
+
+ if (NekoLand.DEBUG_NOTIFICATIONS) {
+ NotificationManager noman = getSystemService(NotificationManager.class);
+ final Bundle extras = new Bundle();
+ extras.putString("android.substName", getString(R.string.notification_name));
+ final int size = getResources()
+ .getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
+ final Cat cat = Cat.create(this);
+ final Notification.Builder builder
+ = cat.buildNotification(this)
+ .setContentTitle("DEBUG")
+ .setChannelId(NekoLand.CHAN_ID)
+ .setContentText("Ran job: " + params);
+
+ noman.notify(DEBUG_NOTIFICATION, builder.build());
+ }
+
+ triggerFoodResponse(this);
+ cancelJob(this);
+ return false;
+ }
+
+ private static void triggerFoodResponse(Context context) {
+ final PrefState prefs = new PrefState(context);
+ int food = prefs.getFoodState();
+ if (food != 0) {
+ prefs.setFoodState(0); // nom
+ final Random rng = new Random();
+ if (rng.nextFloat() <= CAT_CAPTURE_PROB) {
+ Cat cat;
+ List<Cat> cats = prefs.getCats();
+ final int[] probs = context.getResources().getIntArray(R.array.food_new_cat_prob);
+ final float waterLevel100 = prefs.getWaterState() / 2; // water is 0..200
+ final float new_cat_prob = (float) ((food < probs.length)
+ ? probs[food]
+ : waterLevel100) / 100f;
+ Log.v(TAG, "Food type: " + food);
+ Log.v(TAG, "New cat probability: " + new_cat_prob);
+
+ if (cats.size() == 0 || rng.nextFloat() <= new_cat_prob) {
+ cat = newRandomCat(context, prefs);
+ Log.v(TAG, "A new cat is here: " + cat.getName());
+ } else {
+ cat = getExistingCat(prefs);
+ Log.v(TAG, "A cat has returned: " + cat.getName());
+ }
+
+ notifyCat(context, cat);
+ }
+ }
+ }
+
+ static void notifyCat(Context context, Cat cat) {
+ NotificationManager noman = context.getSystemService(NotificationManager.class);
+ final Notification.Builder builder = cat.buildNotification(context);
+ noman.notify(cat.getShortcutId(), CAT_NOTIFICATION, builder.build());
+ }
+
+ static Cat newRandomCat(Context context, PrefState prefs) {
+ final Cat cat = Cat.create(context);
+ prefs.addCat(cat);
+ cat.logAdd(context);
+ return cat;
+ }
+
+ static Cat getExistingCat(PrefState prefs) {
+ final List<Cat> cats = prefs.getCats();
+ if (cats.size() == 0) return null;
+ return cats.get(new Random().nextInt(cats.size()));
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ return false;
+ }
+
+ public static void registerJobIfNeeded(Context context, long intervalMinutes) {
+ JobScheduler jss = context.getSystemService(JobScheduler.class);
+ JobInfo info = jss.getPendingJob(JOB_ID);
+ if (info == null) {
+ registerJob(context, intervalMinutes);
+ }
+ }
+
+ public static void registerJob(Context context, long intervalMinutes) {
+ setupNotificationChannels(context);
+
+ JobScheduler jss = context.getSystemService(JobScheduler.class);
+ jss.cancel(JOB_ID);
+ long interval = intervalMinutes * MINUTES;
+ long jitter = (long) (INTERVAL_JITTER_FRAC * interval);
+ interval += (long) (Math.random() * (2 * jitter)) - jitter;
+ final JobInfo jobInfo = new JobInfo.Builder(JOB_ID,
+ new ComponentName(context, NekoService.class))
+ .setPeriodic(interval, INTERVAL_FLEX)
+ .build();
+
+ Log.v(TAG, "A cat will visit in " + interval + "ms: " + String.valueOf(jobInfo));
+ jss.schedule(jobInfo);
+
+ if (NekoLand.DEBUG_NOTIFICATIONS) {
+ NotificationManager noman = context.getSystemService(NotificationManager.class);
+ noman.notify(DEBUG_NOTIFICATION, new Notification.Builder(context)
+ .setSmallIcon(R.drawable.stat_icon)
+ .setContentTitle(String.format("Job scheduled in %d min", (interval / MINUTES)))
+ .setContentText(String.valueOf(jobInfo))
+ .setPriority(Notification.PRIORITY_MIN)
+ .setCategory(Notification.CATEGORY_SERVICE)
+ .setChannelId(NekoLand.CHAN_ID)
+ .setShowWhen(true)
+ .build());
+ }
+ }
+
+ public static void cancelJob(Context context) {
+ JobScheduler jss = context.getSystemService(JobScheduler.class);
+ Log.v(TAG, "Canceling job");
+ jss.cancel(JOB_ID);
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
new file mode 100644
index 000000000000..d02433f40e89
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.egg.neko;
+
+import android.content.Intent;
+import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
+import android.util.Log;
+
+import com.android.egg.neko.PrefState.PrefsListener;
+import com.android.internal.logging.MetricsLogger;
+
+public class NekoTile extends TileService implements PrefsListener {
+
+ private static final String TAG = "NekoTile";
+
+ private PrefState mPrefs;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mPrefs = new PrefState(this);
+ }
+
+ @Override
+ public void onStartListening() {
+ super.onStartListening();
+ mPrefs.setListener(this);
+ updateState();
+ }
+
+ @Override
+ public void onStopListening() {
+ super.onStopListening();
+ mPrefs.setListener(null);
+ }
+
+ @Override
+ public void onTileAdded() {
+ super.onTileAdded();
+ MetricsLogger.count(this, "egg_neko_tile_added", 1);
+ }
+
+ @Override
+ public void onTileRemoved() {
+ super.onTileRemoved();
+ MetricsLogger.count(this, "egg_neko_tile_removed", 1);
+ }
+
+ @Override
+ public void onPrefsChanged() {
+ updateState();
+ }
+
+ private void updateState() {
+ Tile tile = getQsTile();
+ int foodState = mPrefs.getFoodState();
+ Food food = new Food(foodState);
+ if (foodState != 0) {
+ NekoService.registerJobIfNeeded(this, food.getInterval(this));
+ }
+ tile.setIcon(food.getIcon(this));
+ tile.setLabel(food.getName(this));
+ tile.setState(foodState != 0 ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
+ tile.updateTile();
+ }
+
+ @Override
+ public void onClick() {
+ if (mPrefs.getFoodState() != 0) {
+ // there's already food loaded, let's empty it
+ MetricsLogger.count(this, "egg_neko_empty_food", 1);
+ mPrefs.setFoodState(0);
+ NekoService.cancelJob(this);
+ } else {
+ // time to feed the cats
+ if (isLocked()) {
+ if (isSecure()) {
+ Log.d(TAG, "startActivityAndCollapse");
+ Intent intent = new Intent(this, NekoLockedActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivityAndCollapse(intent);
+ } else {
+ unlockAndRun(new Runnable() {
+ @Override
+ public void run() {
+ showNekoDialog();
+ }
+ });
+ }
+ } else {
+ showNekoDialog();
+ }
+ }
+ }
+
+ private void showNekoDialog() {
+ Log.d(TAG, "showNekoDialog");
+ MetricsLogger.count(this, "egg_neko_select_food", 1);
+ showDialog(new NekoDialog(this));
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/PrefState.java b/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
new file mode 100644
index 000000000000..49ff315392b4
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.egg.neko;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class PrefState implements OnSharedPreferenceChangeListener {
+
+ private static final String FILE_NAME = "mPrefs";
+
+ private static final String FOOD_STATE = "food";
+
+ private static final String WATER_STATE = "water";
+
+ private static final String CAT_KEY_PREFIX = "cat:";
+
+ private final Context mContext;
+ private final SharedPreferences mPrefs;
+ private PrefsListener mListener;
+
+ public PrefState(Context context) {
+ mContext = context;
+ mPrefs = mContext.getSharedPreferences(FILE_NAME, 0);
+ }
+
+ // Can also be used for renaming.
+ public void addCat(Cat cat) {
+ mPrefs.edit()
+ .putString(CAT_KEY_PREFIX + String.valueOf(cat.getSeed()), cat.getName())
+ .apply();
+ }
+
+ public void removeCat(Cat cat) {
+ mPrefs.edit().remove(CAT_KEY_PREFIX + String.valueOf(cat.getSeed())).apply();
+ }
+
+ public List<Cat> getCats() {
+ ArrayList<Cat> cats = new ArrayList<>();
+ Map<String, ?> map = mPrefs.getAll();
+ for (String key : map.keySet()) {
+ if (key.startsWith(CAT_KEY_PREFIX)) {
+ long seed = Long.parseLong(key.substring(CAT_KEY_PREFIX.length()));
+ Cat cat = new Cat(mContext, seed);
+ cat.setName(String.valueOf(map.get(key)));
+ cats.add(cat);
+ }
+ }
+ return cats;
+ }
+
+ public int getFoodState() {
+ return mPrefs.getInt(FOOD_STATE, 0);
+ }
+
+ public void setFoodState(int foodState) {
+ mPrefs.edit().putInt(FOOD_STATE, foodState).apply();
+ }
+
+ public float getWaterState() {
+ return mPrefs.getFloat(WATER_STATE, 0f);
+ }
+
+ public void setWaterState(float waterState) {
+ mPrefs.edit().putFloat(WATER_STATE, waterState).apply();
+ }
+
+ public void setListener(PrefsListener listener) {
+ mListener = listener;
+ if (mListener != null) {
+ mPrefs.registerOnSharedPreferenceChangeListener(this);
+ } else {
+ mPrefs.unregisterOnSharedPreferenceChangeListener(this);
+ }
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ mListener.onPrefsChanged();
+ }
+
+ public interface PrefsListener {
+ void onPrefsChanged();
+ }
+}
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index ed7f3df5222d..8efbad64f3d2 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -199,7 +199,7 @@ public class FusedLocationServiceTest {
}
@Override
- public void onSetAttributionTag(String attributionTag) {
+ public void onSetIdentity(String packageName, String attributionTag) {
}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_georgian.kcm b/packages/InputDevices/res/raw/keyboard_layout_georgian.kcm
new file mode 100644
index 000000000000..35b66a37336b
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_georgian.kcm
@@ -0,0 +1,383 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Georgian QWERTY keyboard layout.
+# This is a typical Georgian PC keyboard layout.
+# As an added convenience, English characters are accessible using ralt (Alt Gr).
+#
+
+type OVERLAY
+
+map key 86 BACKSLASH
+
+### ROW 1
+
+key GRAVE {
+ label: '\u201e'
+ base: '\u201e'
+ shift, capslock: '\u201c'
+ ralt: '`'
+ ralt+shift: '~'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+ ralt: '!'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '@'
+ ralt: '@'
+ ralt+shift: '\u201e'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+ ralt: '#'
+ ralt+shift: '\u201c'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+ ralt: '$'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+ ralt: '%'
+ ralt+shift: '\u20ac'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '^'
+ ralt: '^'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '&'
+ ralt: '&'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '*'
+ ralt: '*'
+ ralt+shift: '\u00b0'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+ ralt: '('
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+ ralt: ')'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+ ralt: '_'
+ ralt+shift: '\u2014'
+}
+
+key EQUALS {
+ label: '='
+ base: '='
+ shift: '+'
+ ralt: '+'
+ ralt+shift: '\u2013'
+}
+
+### ROW 2
+
+key Q {
+ label: '\u10e5'
+ base: '\u10e5'
+ ralt: 'q'
+ ralt+shift, ralt+capslock: 'Q'
+}
+
+key W {
+ label: '\u10ec'
+ base: '\u10ec'
+ shift, capslock: '\u10ed'
+ ralt: 'w'
+ ralt+shift, ralt+capslock: 'W'
+}
+
+key E {
+ label: '\u10d4'
+ base: '\u10d4'
+ ralt: 'e'
+ ralt+shift, ralt+capslock: 'E'
+}
+
+key R {
+ label: '\u10e0'
+ base: '\u10e0'
+ shift, capslock: '\u10e6'
+ ralt: 'r'
+ ralt+shift, ralt+capslock: 'R'
+}
+
+key T {
+ label: '\u10e2'
+ base: '\u10e2'
+ shift, capslock: '\u10d7'
+ ralt: 't'
+ ralt+shift, ralt+capslock: 'T'
+}
+
+key Y {
+ label: '\u10e7'
+ base: '\u10e7'
+ ralt: 'y'
+ ralt+shift, ralt+capslock: 'Y'
+}
+
+key U {
+ label: '\u10e3'
+ base: '\u10e3'
+ ralt: 'u'
+ ralt+shift, ralt+capslock: 'U'
+}
+
+key I {
+ label: '\u10d8'
+ base: '\u10d8'
+ ralt: 'i'
+ ralt+shift, ralt+capslock: 'I'
+}
+
+key O {
+ label: '\u10dd'
+ base: '\u10dd'
+ ralt: 'o'
+ ralt+shift, ralt+capslock: 'O'
+}
+
+key P {
+ label: '\u10de'
+ base: '\u10de'
+ ralt: 'p'
+ ralt+shift, ralt+capslock: 'P'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ base: '['
+ shift, capslock: '{'
+ ralt: '['
+ ralt+shift: '{'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ base: ']'
+ shift, capslock: '}'
+ ralt: ']'
+ ralt+shift: '}'
+}
+
+key BACKSLASH {
+ label: '~'
+ base: '~'
+ shift: '|'
+ ralt: '\\'
+ ralt+shift: '|'
+}
+
+### ROW 3
+
+key A {
+ label: '\u10d0'
+ base: '\u10d0'
+ ralt: 'a'
+ ralt+shift, ralt+capslock: 'A'
+}
+
+key S {
+ label: '\u10e1'
+ base: '\u10e1'
+ shift, capslock: '\u10e8'
+ ralt: 's'
+ ralt+shift, ralt+capslock: 'S'
+}
+
+key D {
+ label: '\u10d3'
+ base: '\u10d3'
+ ralt: 'd'
+ ralt+shift, ralt+capslock: 'D'
+}
+
+key F {
+ label: '\u10e4'
+ base: '\u10e4'
+ ralt: 'f'
+ ralt+shift, ralt+capslock: 'F'
+}
+
+key G {
+ label: '\u10d2'
+ base: '\u10d2'
+ ralt: 'g'
+ ralt+shift, ralt+capslock: 'G'
+}
+
+key H {
+ label: '\u10f0'
+ base: '\u10f0'
+ ralt: 'h'
+ ralt+shift, ralt+capslock: 'H'
+}
+
+key J {
+ label: '\u10ef'
+ base: '\u10ef'
+ shift, capslock: '\u10df'
+ ralt: 'j'
+ ralt+shift, ralt+capslock: 'J'
+}
+
+key K {
+ label: '\u10d9'
+ base: '\u10d9'
+ ralt: 'k'
+ ralt+shift, ralt+capslock: 'K'
+}
+
+key L {
+ label: '\u10da'
+ base: '\u10da'
+ shift, capslock: '\u20be'
+ ralt: 'l'
+ ralt+shift, ralt+capslock: 'L'
+}
+
+key SEMICOLON {
+ label: ';'
+ base: ';'
+ shift, capslock: ':'
+ ralt: ';'
+ ralt+shift: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base: '\''
+ shift, capslock: '"'
+ ralt: '\''
+ ralt+shift: '"'
+}
+
+### ROW 4
+
+key Z {
+ label: '\u10d6'
+ base: '\u10d6'
+ shift, capslock: '\u10eb'
+ ralt: 'z'
+ ralt+shift, ralt+capslock: 'Z'
+}
+
+key X {
+ label: '\u10ee'
+ base: '\u10ee'
+ ralt: 'x'
+ ralt+shift, ralt+capslock: 'X'
+}
+
+key C {
+ label: '\u10ea'
+ base: '\u10ea'
+ shift, capslock: '\u10e9'
+ ralt: 'c'
+ ralt+shift, ralt+capslock: 'C'
+}
+
+key V {
+ label: '\u10d5'
+ base: '\u10d5'
+ ralt: 'v'
+ ralt+shift, ralt+capslock: 'V'
+}
+
+key B {
+ label: '\u10d1'
+ base: '\u10d1'
+ ralt: 'b'
+ ralt+shift, ralt+capslock: 'B'
+}
+
+key N {
+ label: '\u10dc'
+ base: '\u10dc'
+ ralt: 'n'
+ ralt+shift, ralt+capslock: 'N'
+}
+
+key M {
+ label: '\u10db'
+ base: '\u10db'
+ ralt: 'm'
+ ralt+shift, ralt+capslock: 'M'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift, capslock: '<'
+ ralt: ','
+ ralt+shift: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift, capslock: '>'
+ ralt: '.'
+ ralt+shift: '>'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift: '?'
+ ralt: '/'
+ ralt+shift: '?'
+}
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 6789dc910b5b..22436052b6e6 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Pools"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarussies"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongools"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgies"</string>
</resources>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index cd2142c7cadf..3257e9852a7a 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"ፖላንድኛ"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ቤላሩስኛ"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ሞንጎሊያኛ"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ጂዮርጂያኛ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index 5ef7caf9a5af..dc1c89fce99b 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"البولندية"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"البيلاروسية"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"المنغولية"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"الجورجية"</string>
</resources>
diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml
index de4305933745..c572df90417e 100644
--- a/packages/InputDevices/res/values-as/strings.xml
+++ b/packages/InputDevices/res/values-as/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"পোলিশ্ব"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"বেলাৰুছিয়ান"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
</resources>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index a91121920aab..51ca6fbe100d 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polyak"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarus dili"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Monqol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gürcü"</string>
</resources>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index fdbae29fa04a..aa071f338404 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"poljski"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"beloruski"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolska"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijska"</string>
</resources>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index 49d80007722e..7acffe3f480f 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Польская"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Беларуская"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Мангольская"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузінская"</string>
</resources>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index d32fa4f85fef..9d4479164988 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Полски"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"беларуски"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монголски"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузински"</string>
</resources>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index 5305c7075f82..20ddf15efbef 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"পোলিশ"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"বেলারুশীয়"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"মঙ্গোলিয়ান"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"জর্জিয়ান"</string>
</resources>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index 39b08acc8e76..6b26b3734b0f 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"poljski"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"bjeloruska"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolski"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijski"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index a9ffe5b0d8ee..9f53d35e0050 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polonès"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorús"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgià"</string>
</resources>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index be458ee8279b..1dbe4abf989a 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"polština"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"běloruština"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolština"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzínština"</string>
</resources>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index 80638c571739..4b2ba1f57824 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polsk"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Hviderussisk"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolsk"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisk"</string>
</resources>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 3db8190e89e0..5d3f42c3235d 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polnisch"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Weißrussisch"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolisch"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisch"</string>
</resources>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index 89b39ff536a8..b6366ea38317 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Πολωνικά"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Λευκορωσικά"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Μογγολικά"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Γεωργιανά"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index ceace167ca2e..0b41ccc9bf33 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polish"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index ceace167ca2e..0b41ccc9bf33 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polish"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index ceace167ca2e..0b41ccc9bf33 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polish"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index ceace167ca2e..0b41ccc9bf33 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polish"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index 2850391897fc..85c2b2849199 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‎‏‎Polish‎‏‎‎‏‎"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‏‎‎‏‎‎‎Belarusian‎‏‎‎‏‎"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎Mongolian‎‏‎‎‏‎"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎Georgian‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index aa7226428e6f..11b9a4662bf1 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polaco"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorruso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 5306caf694da..096526746844 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polaco"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorruso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index 1a2e1aad425c..d84be12c05a9 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Poola"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"valgevene"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongoli"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruusia"</string>
</resources>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 5eeb6a07db29..c4252491d287 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Poloniarra"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusiera"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongoliera"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiera"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index e4fd5c91df4b..153c3c5ad85c 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"لهستانی"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"بلاروسی"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"مغولی"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"گرجستانی"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index 87fbd5b28554..dddb23dc1163 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"puola"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"valkovenäjä"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongoli"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgia"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 64071ddda46b..40b9280bc4db 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polonais"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Biélorusse"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Géorgien"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index f78c497f217f..be7489fadd4e 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polonais"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Biélorusse"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Géorgien"</string>
</resources>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index e9a7fe6f7461..e03946546051 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polaco"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belaruso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Xeorxiano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index 5c6e200970fa..f60eaf39155f 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"પોલિશ"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"બેલારુશિયન"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"મોંગોલિયન"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"જ્યોર્જિઅન"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 865023298bc4..d7fce5288e62 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"पोलिश"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"बेलारूसी"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"मंगोलियन"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"जॉर्जियन कीबोर्ड का लेआउट"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index 3fe49e47bf64..c155dd368487 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"poljski"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"bjeloruski"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolski"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzijska"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index e2f9adaf40f5..998a68c14cf8 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"lengyel"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"belarusz"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"grúz"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index 2cfa8574c232..0672387a42a0 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"լեհերեն"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"բելառուսերեն"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Մոնղոլերեն"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"վրացերեն"</string>
</resources>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index 39499e482741..9b09bdeb6622 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polandia"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusia"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolia"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgia"</string>
</resources>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index 481797c10ba1..a23e41bf6a83 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Pólska"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"hvítrússneska"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongólska"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgíska"</string>
</resources>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index b736f547c797..49baaf93b429 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polacco"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorusso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolo"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 03cfa1f29823..8eae0375b5f6 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"פולנית"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"בלארוסית"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"מונגולית"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"גיאורגית"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index ab151229e400..31ae22732fde 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"ポーランド語"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ベラルーシ語"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"モンゴル語"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ジョージア語"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index 0061b3e4e764..328432922501 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"პოლონური"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ბელორუსული"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"მონღოლური"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ქართული"</string>
</resources>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index f56a5d810f3f..634eaf9d675c 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Поляк"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Белорус"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Моңғол"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузин"</string>
</resources>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index d5456cbd50b6..a4946ab80f98 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"ប៉ូឡូញ"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"បេឡារុស"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"មុងហ្គោលី"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ហ្សក​ហ្ស៊ី"</string>
</resources>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index 5fd27a9b7594..073ce98892d7 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"ಪೋಲಿಶ್"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ಬೆಲರೂಸಿಯನ್"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ಮಂಗೋಲಿಯನ್"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ಜಾರ್ಜಿಯನ್"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 96968c55882b..97ed6a03be88 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"폴란드어"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"벨라루스어"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"몽골어"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"조지아어"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index 3d91bc7f3180..ab775ffce31e 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Полякча"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Беларусча"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монголчо"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинче"</string>
</resources>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index 9f775a30df57..392430f07752 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"ໂພລິຊ"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ເບລາຣັສຊຽນ"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ມອງໂກລຽນ"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ຈໍຈຽນ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index 378d36c8e12b..18e33e916da5 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Lenkų"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Baltarusių k."</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolų"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzinų"</string>
</resources>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 56f418fe45d1..96ab62f64e46 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Poļu valoda"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Baltkrievu"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongoļu"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzīnu"</string>
</resources>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index daadc0a1e171..33d9723dddef 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"полски"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"белоруски"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монголски"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузиски"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index 6280a620a273..3b29f3c1df9f 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"പോളിഷ്"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ബെലാറുഷ്യൻ"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"മംഗോളിയൻ"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ജോര്‍ജ്ജിയൻ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index fd7b85f8efd4..01e8c967bfa4 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Польш"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Беларусь хэл"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монгол"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Гүрж"</string>
</resources>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index b5f81d1e9cf7..b4ebf76302eb 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"पोलिश"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"बेलारुशियन"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"मंगोलियन"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"जॉर्जियन"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 71d7cc4d4427..3e716aef7571 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Bahasa Poland"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bahasa Belarus"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Bahasa Mongolia"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Bahasa Georgia"</string>
</resources>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 46074f1231e8..35c52d318481 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"ပိုလန်"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ဘီလာရုဇ်"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"မွန်ဂိုလီးယား"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ဂျော်ဂျီယာ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 0b440ba3c7e6..fe0a92a0f5b5 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polsk"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Hviterussisk"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolsk"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisk"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 4bffacfedc49..c847058e6692 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"पोलिस"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"बेलारुसियाली"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"मङ्गोलियाली"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"जर्जियाली"</string>
</resources>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index 474e3a4efba0..483e821de209 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Pools"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Wit-Russisch"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongools"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisch"</string>
</resources>
diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml
index 79f3bc7819bd..57df24ec8660 100644
--- a/packages/InputDevices/res/values-or/strings.xml
+++ b/packages/InputDevices/res/values-or/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"ପଲିଶ୍"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ବେଲାରୁସିଆନ୍"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ମଙ୍ଗୋଲିଆନ୍"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ଜର୍ଜିଆନ୍"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index 2cca529b477a..90203a7377ff 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"ਪੋਲਿਸ਼"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"ਬੇਲਾਰੂਸੀ"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ਮੰਗੋਲੀਆਈ"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ਜਾਰਜੀਆਈ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 05ad76835e2c..c10596a19e76 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polski"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Białoruski"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolski"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruziński"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index 7e0e3c2e24d6..b956a01eadcb 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polonês"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index e0f88eeae86c..d9aa91d34c8b 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polaco"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index 7e0e3c2e24d6..b956a01eadcb 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polonês"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bielorrusso"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index dce9b357af90..9deaa452042c 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Poloneză"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusă"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolă"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiană"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 9b3ca897d67e..888c8f57fd32 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"польский"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Белорусский"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монгольский"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинский"</string>
</resources>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index 55d093c2759a..895e7b5065d5 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"පෝලන්ත"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"බෙලරුසියානු"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"මොන්ගෝලියානු"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ජෝර්ජියානු"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index 6071ba8cc6c2..70843d28f90e 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Poľština"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"bieloruština"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolčina"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzínčina"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index f86053eb6f95..410be33fa2ad 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"poljščina"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"beloruščina"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolščina"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzinščina"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index 461b6f4c0c13..b620067d2233 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polonisht"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Bjellorusisht"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolisht"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gjeorgjisht"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index 2f288825cfee..543af8a66399 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"пољски"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"белоруски"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монголска"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузијска"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index 6664296a324a..f776a43db81a 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polska"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"vitryska"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongoliska"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgiska"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 27df01a3cd76..3d25dadab70c 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Kipolandi"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Kibelarusi"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Kimongolia"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Kijojia"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index bbc9b7102b5d..ff8e474cf504 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"போலிஷ்"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"பெலரூசியன்"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"மங்கோலியன்"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ஜார்ஜியன்"</string>
</resources>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index 69636b26cc81..c22ab22c56d0 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"పోలిష్"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"బెలారష్యన్"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"మంగోలియన్"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"జార్జియన్"</string>
</resources>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 7b33a0489080..5dfc0f92eabf 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"โปแลนด์"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"เบลารุส"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ภาษามองโกเลีย"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ภาษาจอร์เจีย"</string>
</resources>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 616261eca145..5441b0634643 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polish"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
</resources>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index 43fdfe0eb958..4b8a728f0ae9 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Lehçe"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusça"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Moğolca"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gürcüce"</string>
</resources>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 2d6c013da278..6c0934f6e340 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Польська"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Білоруська"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монгольська"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинська"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index 3ee66af0bf2b..80c67f5032b5 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"پولش"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"بيلاروسی"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"منگؤلی"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"جارجیائی"</string>
</resources>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 4802fb60734c..d205a560be8f 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Polyak"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarus"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongol"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzin"</string>
</resources>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index c2ea0fc4e650..4e446946cc50 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Tiếng Ba Lan"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Tiếng Belarus"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Tiếng Mông Cổ"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Tiếng Georgia"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index 96af97e9c20e..fecaca8644ca 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"波兰语"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"白俄罗斯语"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"蒙古语"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"格鲁吉亚语"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index 80c606749382..f87b7f8c4919 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"波蘭文"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"白俄羅斯文"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"蒙古文"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"格魯吉亞文"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index 030c0b23f12f..13514dffea9f 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"波蘭文"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"白俄羅斯文"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"蒙古文"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"喬治亞文"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 15c4ea0f3163..36e2001068c2 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -46,4 +46,5 @@
<string name="keyboard_layout_polish" msgid="1121588624094925325">"Isi-Polish"</string>
<string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Isi-Belarusian"</string>
<string name="keyboard_layout_mongolian" msgid="7678483495823936626">"isi-Mongolian"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Okwesi-Georgian"</string>
</resources>
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index a21d13d0e986..987814626689 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -134,4 +134,7 @@
<!-- Mongolian keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_mongolian">Mongolian</string>
+
+ <!-- Georgian keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_georgian">Georgian</string>
</resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 7c5510544090..0d7a13b3eb34 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -171,4 +171,8 @@
<keyboard-layout android:name="keyboard_layout_mongolian"
android:label="@string/keyboard_layout_mongolian"
android:keyboardLayout="@raw/keyboard_layout_mongolian" />
+
+ <keyboard-layout android:name="keyboard_layout_georgian"
+ android:label="@string/keyboard_layout_georgian"
+ android:keyboardLayout="@raw/keyboard_layout_georgian" />
</keyboard-layouts>
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 9420954748c4..75bd32ec0301 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -20,6 +20,7 @@ android_app {
certificate: "platform",
privileged: true,
platform_apis: true,
+ rename_resources_package: false,
static_libs: [
"xz-java",
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
index 5a51ac22b88f..973ab8956304 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
@@ -239,8 +239,8 @@ public class UninstallFinish extends BroadcastReceiver {
builder.addAction((new Notification.Action.Builder(
Icon.createWithResource(context, R.drawable.ic_settings_multiuser),
context.getString(R.string.manage_users),
- PendingIntent.getActivity(context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT))).build());
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE))).build());
}
/**
@@ -259,7 +259,7 @@ public class UninstallFinish extends BroadcastReceiver {
builder.addAction((new Notification.Action.Builder(
Icon.createWithResource(context, R.drawable.ic_lock),
context.getString(R.string.manage_device_administrators),
- PendingIntent.getActivity(context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT))).build());
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE))).build());
}
}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
index 9ae31989eeb2..5a756fe50209 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
@@ -46,7 +46,7 @@ public class RecommendationServiceImpl extends RecommendationService
private static final String LOG_TAG = "PrintServiceRecService";
/** All registered plugins */
- private ArrayList<RemotePrintServicePlugin> mPlugins;
+ private final ArrayList<RemotePrintServicePlugin> mPlugins = new ArrayList<>();
/** Lock to keep multi-cast enabled */
private WifiManager.MulticastLock mMultiCastLock;
@@ -62,8 +62,6 @@ public class RecommendationServiceImpl extends RecommendationService
mMultiCastLock.acquire();
}
- mPlugins = new ArrayList<>();
-
try {
for (VendorConfig config : VendorConfig.getAllConfigs(this)) {
try {
@@ -138,6 +136,7 @@ public class RecommendationServiceImpl extends RecommendationService
Log.e(LOG_TAG, "Could not stop plugin", e);
}
}
+ mPlugins.clear();
if (mMultiCastLock != null) {
mMultiCastLock.release();
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/ServiceListener.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/ServiceListener.java
index 9535ef06f888..348c971e17cc 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/ServiceListener.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/ServiceListener.java
@@ -22,6 +22,8 @@ import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.text.TextUtils;
+import androidx.annotation.GuardedBy;
+
import com.android.printservice.recommendation.R;
import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
import com.android.printservice.recommendation.util.PrinterHashMap;
@@ -40,7 +42,12 @@ public class ServiceListener implements ServiceResolveQueue.ResolveCallback {
private final String[] mServiceType;
private final Observer mObserver;
private final ServiceResolveQueue mResolveQueue;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private List<NsdManager.DiscoveryListener> mListeners = new ArrayList<>();
+
+ @GuardedBy("mLock")
public HashMap<String, PrinterHashMap> mVendorHashMap = new HashMap<>();
public interface Observer {
@@ -73,108 +80,120 @@ public class ServiceListener implements ServiceResolveQueue.ResolveCallback {
printerFound(nsdServiceInfo);
}
- private synchronized void printerFound(NsdServiceInfo nsdServiceInfo) {
+ private void printerFound(NsdServiceInfo nsdServiceInfo) {
if (nsdServiceInfo == null) return;
if (TextUtils.isEmpty(PrinterHashMap.getKey(nsdServiceInfo))) return;
String vendor = MDnsUtils.getVendor(nsdServiceInfo);
if (vendor == null) vendor = "";
- for(Map.Entry<String,VendorInfo> entry : mVendorInfoHashMap.entrySet()) {
- for(String vendorValues : entry.getValue().mDNSValues) {
- if (vendor.equalsIgnoreCase(vendorValues)) {
+
+ boolean mapsChanged;
+ synchronized (mLock) {
+ for (Map.Entry<String, VendorInfo> entry : mVendorInfoHashMap.entrySet()) {
+ for (String vendorValues : entry.getValue().mDNSValues) {
+ if (vendor.equalsIgnoreCase(vendorValues)) {
+ vendor = entry.getValue().mVendorID;
+ break;
+ }
+ }
+ // intentional pointer check
+ //noinspection StringEquality
+ if ((vendor != entry.getValue().mVendorID) &&
+ MDnsUtils.isVendorPrinter(nsdServiceInfo, entry.getValue().mDNSValues)) {
vendor = entry.getValue().mVendorID;
- break;
}
+ // intentional pointer check
+ //noinspection StringEquality
+ if (vendor == entry.getValue().mVendorID) break;
}
- // intentional pointer check
- //noinspection StringEquality
- if ((vendor != entry.getValue().mVendorID) &&
- MDnsUtils.isVendorPrinter(nsdServiceInfo, entry.getValue().mDNSValues)) {
- vendor = entry.getValue().mVendorID;
- }
- // intentional pointer check
- //noinspection StringEquality
- if (vendor == entry.getValue().mVendorID) break;
- }
- if (TextUtils.isEmpty(vendor)) {
- return;
- }
+ if (TextUtils.isEmpty(vendor)) {
+ return;
+ }
- if (!mObserver.matchesCriteria(vendor, nsdServiceInfo))
- return;
- boolean mapsChanged;
+ if (!mObserver.matchesCriteria(vendor, nsdServiceInfo))
+ return;
- PrinterHashMap vendorHash = mVendorHashMap.get(vendor);
- if (vendorHash == null) {
- vendorHash = new PrinterHashMap();
+ PrinterHashMap vendorHash = mVendorHashMap.get(vendor);
+ if (vendorHash == null) {
+ vendorHash = new PrinterHashMap();
+ }
+ mapsChanged = (vendorHash.addPrinter(nsdServiceInfo) == null);
+ mVendorHashMap.put(vendor, vendorHash);
}
- mapsChanged = (vendorHash.addPrinter(nsdServiceInfo) == null);
- mVendorHashMap.put(vendor, vendorHash);
if (mapsChanged) {
mObserver.dataSetChanged();
}
}
- private synchronized void printerRemoved(NsdServiceInfo nsdServiceInfo) {
+ private void printerRemoved(NsdServiceInfo nsdServiceInfo) {
boolean wasRemoved = false;
- Set<String> vendors = mVendorHashMap.keySet();
- for(String vendor : vendors) {
- PrinterHashMap map = mVendorHashMap.get(vendor);
- wasRemoved |= (map.removePrinter(nsdServiceInfo) != null);
- if (map.isEmpty()) wasRemoved |= (mVendorHashMap.remove(vendor) != null);
+
+ synchronized (mLock) {
+ Set<String> vendors = mVendorHashMap.keySet();
+ for (String vendor : vendors) {
+ PrinterHashMap map = mVendorHashMap.get(vendor);
+ wasRemoved |= (map.removePrinter(nsdServiceInfo) != null);
+ if (map.isEmpty()) wasRemoved |= (mVendorHashMap.remove(vendor) != null);
+ }
}
+
if (wasRemoved) {
mObserver.dataSetChanged();
}
}
public void start() {
- stop();
- for(final String service :mServiceType) {
- NsdManager.DiscoveryListener listener = new NsdManager.DiscoveryListener() {
- @Override
- public void onStartDiscoveryFailed(String s, int i) {
+ synchronized (mLock) {
+ stop();
- }
+ for (final String service : mServiceType) {
+ NsdManager.DiscoveryListener listener = new NsdManager.DiscoveryListener() {
+ @Override
+ public void onStartDiscoveryFailed(String s, int i) {
- @Override
- public void onStopDiscoveryFailed(String s, int i) {
+ }
- }
+ @Override
+ public void onStopDiscoveryFailed(String s, int i) {
- @Override
- public void onDiscoveryStarted(String s) {
+ }
- }
+ @Override
+ public void onDiscoveryStarted(String s) {
- @Override
- public void onDiscoveryStopped(String s) {
+ }
- }
+ @Override
+ public void onDiscoveryStopped(String s) {
- @Override
- public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
- mResolveQueue.queueRequest(nsdServiceInfo, ServiceListener.this);
- }
+ }
- @Override
- public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
- mResolveQueue.removeRequest(nsdServiceInfo, ServiceListener.this);
- printerRemoved(nsdServiceInfo);
- }
- };
- DiscoveryListenerMultiplexer.addListener(mNSDManager, service, listener);
- mListeners.add(listener);
+ @Override
+ public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
+ mResolveQueue.queueRequest(nsdServiceInfo, ServiceListener.this);
+ }
+
+ @Override
+ public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
+ mResolveQueue.removeRequest(nsdServiceInfo, ServiceListener.this);
+ printerRemoved(nsdServiceInfo);
+ }
+ };
+ DiscoveryListenerMultiplexer.addListener(mNSDManager, service, listener);
+ mListeners.add(listener);
+ }
}
}
public void stop() {
- for(NsdManager.DiscoveryListener listener : mListeners) {
- DiscoveryListenerMultiplexer.removeListener(mNSDManager, listener);
+ synchronized (mLock) {
+ for (NsdManager.DiscoveryListener listener : mListeners) {
+ DiscoveryListenerMultiplexer.removeListener(mNSDManager, listener);
+ }
+ mVendorHashMap.clear();
+ mListeners.clear();
}
- mVendorHashMap.clear();
- mListeners.clear();
}
/**
@@ -183,8 +202,10 @@ public class ServiceListener implements ServiceResolveQueue.ResolveCallback {
public ArrayList<InetAddress> getPrinters() {
ArrayList<InetAddress> printerAddressess = new ArrayList<>();
- for (PrinterHashMap oneVendorPrinters : mVendorHashMap.values()) {
- printerAddressess.addAll(oneVendorPrinters.getPrinterAddresses());
+ synchronized (mLock) {
+ for (PrinterHashMap oneVendorPrinters : mVendorHashMap.values()) {
+ printerAddressess.addAll(oneVendorPrinters.getPrinterAddresses());
+ }
}
return printerAddressess;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index abdfad5bb27a..88b181fc5e0c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -276,21 +276,23 @@ final class NotificationController {
intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId.flattenToString());
intent.setData(Uri.fromParts("printjob", printJobId.flattenToString(), null));
}
- return PendingIntent.getActivity(mContext, 0, intent, 0);
+ return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
private PendingIntent createCancelIntent(PrintJobInfo printJob) {
Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + printJob.getId().flattenToString());
intent.putExtra(EXTRA_PRINT_JOB_ID, printJob.getId());
- return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ return PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
}
private PendingIntent createRestartIntent(PrintJobId printJobId) {
Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + printJobId.flattenToString());
intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId);
- return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ return PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
}
private static boolean shouldNotifyForState(int state) {
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
index 3565b0e3a9ae..e82997431290 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -16,9 +16,6 @@
package com.android.settingslib.widget;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_DEFAULT;
-
import android.annotation.ColorInt;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -48,11 +45,12 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper {
private static final float ADVANCED_ICON_CENTER = 50f;
private static final float ADVANCED_ICON_RADIUS = 48f;
+ public static final int ICON_TYPE_DEFAULT = 0;
+ public static final int ICON_TYPE_ADVANCED = 1;
+
@Retention(RetentionPolicy.SOURCE)
- @IntDef({TYPE_DEFAULT, TYPE_ADVANCED})
+ @IntDef({ICON_TYPE_DEFAULT, ICON_TYPE_ADVANCED})
public @interface AdaptiveOutlineIconType {
- int TYPE_DEFAULT = 0;
- int TYPE_ADVANCED = 1;
}
@VisibleForTesting
@@ -66,7 +64,7 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper {
public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
super(new AdaptiveIconShapeDrawable(resources));
- init(resources, bitmap, TYPE_DEFAULT);
+ init(resources, bitmap, ICON_TYPE_DEFAULT);
}
public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap,
@@ -96,10 +94,10 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper {
private @ColorInt int getColor(Resources resources, @AdaptiveOutlineIconType int type) {
int resId;
switch (type) {
- case TYPE_ADVANCED:
+ case ICON_TYPE_ADVANCED:
resId = R.color.advanced_outline_color;
break;
- case TYPE_DEFAULT:
+ case ICON_TYPE_DEFAULT:
default:
resId = R.color.bt_outline_color;
break;
@@ -110,10 +108,10 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper {
private int getDimensionPixelSize(Resources resources, @AdaptiveOutlineIconType int type) {
int resId;
switch (type) {
- case TYPE_ADVANCED:
+ case ICON_TYPE_ADVANCED:
resId = R.dimen.advanced_dashboard_tile_foreground_image_inset;
break;
- case TYPE_DEFAULT:
+ case ICON_TYPE_DEFAULT:
default:
resId = R.dimen.dashboard_tile_foreground_image_inset;
break;
@@ -133,7 +131,7 @@ public class AdaptiveOutlineDrawable extends DrawableWrapper {
final int count = canvas.save();
canvas.scale(scaleX, scaleY);
// Draw outline
- if (mType == TYPE_DEFAULT) {
+ if (mType == ICON_TYPE_DEFAULT) {
canvas.drawPath(mPath, mOutlinePaint);
} else {
canvas.drawCircle(ADVANCED_ICON_CENTER, ADVANCED_ICON_CENTER, ADVANCED_ICON_RADIUS,
diff --git a/packages/SettingsLib/HelpUtils/res/values-en-rAU/strings.xml b/packages/SettingsLib/HelpUtils/res/values-en-rAU/strings.xml
index 759da1d0b021..150020cb17c5 100644
--- a/packages/SettingsLib/HelpUtils/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-en-rAU/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="help_feedback_label" msgid="7106780063063027882">"Help &amp; feedback"</string>
+ <string name="help_feedback_label" msgid="7106780063063027882">"Help and feedback"</string>
</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-en-rCA/strings.xml b/packages/SettingsLib/HelpUtils/res/values-en-rCA/strings.xml
index 759da1d0b021..150020cb17c5 100644
--- a/packages/SettingsLib/HelpUtils/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-en-rCA/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="help_feedback_label" msgid="7106780063063027882">"Help &amp; feedback"</string>
+ <string name="help_feedback_label" msgid="7106780063063027882">"Help and feedback"</string>
</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-en-rIN/strings.xml b/packages/SettingsLib/HelpUtils/res/values-en-rIN/strings.xml
index 759da1d0b021..150020cb17c5 100644
--- a/packages/SettingsLib/HelpUtils/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-en-rIN/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="help_feedback_label" msgid="7106780063063027882">"Help &amp; feedback"</string>
+ <string name="help_feedback_label" msgid="7106780063063027882">"Help and feedback"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
index e57d1cc11a20..908e2fbbff5b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
@@ -18,5 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
- <string name="disabled_by_admin" msgid="4023569940620832713">"Desativada pelo administrador"</string>
+ <string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-in/strings.xml b/packages/SettingsLib/SearchWidget/res/values-in/strings.xml
index edf51cc601ac..ccf11d26273c 100644
--- a/packages/SettingsLib/SearchWidget/res/values-in/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-in/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="1914043873178389845">"Setelan penelusuran"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Telusuri setelan"</string>
</resources>
diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml
index fd89676ab6d7..96d9e518663f 100644
--- a/packages/SettingsLib/Utils/AndroidManifest.xml
+++ b/packages/SettingsLib/Utils/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.utils">
+ package="com.android.settingslib.widget">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
index 6125b8509fcf..e7c0d9659d11 100644
--- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
+++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
@@ -22,7 +22,7 @@ import android.content.pm.PackageManager;
import android.os.UserManager;
import android.util.Log;
-import com.android.settingslib.utils.R;
+import com.android.settingslib.widget.R;
public class AppUtils {
diff --git a/packages/SettingsLib/res/drawable/ic_media_display_device.xml b/packages/SettingsLib/res/drawable/ic_media_display_device.xml
index 78b4e2a23d45..54fec782c7d1 100644
--- a/packages/SettingsLib/res/drawable/ic_media_display_device.xml
+++ b/packages/SettingsLib/res/drawable/ic_media_display_device.xml
@@ -15,16 +15,12 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="11dp"
- android:viewportWidth="14"
- android:viewportHeight="11"
- android:tint="?android:attr/colorControlNormal">
+ android:width="18dp"
+ android:height="18dp"
+ android:viewportWidth="18"
+ android:viewportHeight="18">
<path
- android:pathData="M10,10v1H4v-1H1.5A1.5,1.5 0,0 1,0 8.5v-7A1.5,1.5 0,
- 0 1,1.5 0h11A1.5,1.5 0,0 1,14 1.5v7a1.5,1.5 0,0 1,-1.5 1.5H10zM1.5,
- 1a0.5,0.5 0,0 0,-0.5 0.5v7a0.5,0.5 0,0 0,0.5 0.5h11a0.5,0.5 0,0 0,
- 0.5 -0.5v-7a0.5,0.5 0,0 0,-0.5 -0.5h-11z"
- android:fillColor="#000000"
+ android:pathData="M12,14V15H6V14H3.5C3.1022,14 2.7206,13.842 2.4393,13.5607C2.158,13.2794 2,12.8978 2,12.5V5.5C2,5.1022 2.158,4.7206 2.4393,4.4393C2.7206,4.158 3.1022,4 3.5,4H14.5C14.8978,4 15.2794,4.158 15.5607,4.4393C15.842,4.7206 16,5.1022 16,5.5V12.5C16,12.8978 15.842,13.2794 15.5607,13.5607C15.2794,13.842 14.8978,14 14.5,14H12ZM3.5,5C3.3674,5 3.2402,5.0527 3.1465,5.1465C3.0527,5.2402 3,5.3674 3,5.5V12.5C3,12.6326 3.0527,12.7598 3.1465,12.8536C3.2402,12.9473 3.3674,13 3.5,13H14.5C14.6326,13 14.7598,12.9473 14.8536,12.8536C14.9473,12.7598 15,12.6326 15,12.5V5.5C15,5.3674 14.9473,5.2402 14.8536,5.1465C14.7598,5.0527 14.6326,5 14.5,5H3.5Z"
+ android:fillColor="#5F6368"
android:fillType="evenOdd"/>
</vector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index e17c0f015ff8..39777cd5f881 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -47,7 +47,7 @@
<string name="wifi_limited_connection" msgid="1184778285475204682">"اتصال محدود"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"لا يتوفر اتصال إنترنت."</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"يلزم تسجيل الدخول"</string>
- <string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"نقطة الدخول ممتلئة مؤقتًا"</string>
+ <string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"نقطة الوصول ممتلئة مؤقتًا"</string>
<string name="connected_via_carrier" msgid="1968057009076191514">"‏تم الاتصال عبر %1$s"</string>
<string name="available_via_carrier" msgid="465598683092718294">"‏متوفرة عبر %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"فتح <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
@@ -125,7 +125,7 @@
<string name="bluetooth_talkback_phone" msgid="868393783858123880">"هاتف"</string>
<string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"تصوير"</string>
<string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"السمّاعة"</string>
- <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"جهاز إدخال طرفي"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"جهاز إدخال ملحق"</string>
<string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"بلوتوث"</string>
<string name="bluetooth_hearingaid_left_pairing_message" msgid="8561855779703533591">"جارٍ إقران سماعة الأذن الطبية اليسرى…"</string>
<string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"جارٍ إقران سماعة الأذن الطبية اليمنى…"</string>
@@ -348,7 +348,7 @@
<string name="simulate_color_space" msgid="1206503300335835151">"محاكاة مسافة اللون"</string>
<string name="enable_opengl_traces_title" msgid="4638773318659125196">"‏تفعيل عمليات تتبع OpenGL"</string>
<string name="usb_audio_disable_routing" msgid="3367656923544254975">"‏إيقاف توجيه الصوت عبر USB"</string>
- <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"‏إيقاف التوجيه التلقائي إلى أجهزة الصوت الطرفية عبر USB"</string>
+ <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"‏إيقاف التوجيه التلقائي إلى أجهزة الصوت الملحقة عبر USB"</string>
<string name="debug_layout" msgid="1659216803043339741">"عرض حدود المخطط"</string>
<string name="debug_layout_summary" msgid="8825829038287321978">"عرض حدود وهوامش المقطع وما إلى ذلك"</string>
<string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"فرض اتجاه التنسيق ليكون من اليمين إلى اليسار"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 16029ee2b3b0..d042c0f89b1e 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -508,7 +508,7 @@
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Да се пита винаги"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"До изключване"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Току-що"</string>
- <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Високоговорител на телефона"</string>
+ <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Високоговорител"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"При свързването възникна проблем. Изключете устройството и го включете отново"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Аудиоустройство с кабел"</string>
<string name="help_label" msgid="3528360748637781274">"Помощ и отзиви"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 6ca48283a103..2db23f73e683 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -508,7 +508,7 @@
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"প্রতিবার জিজ্ঞেস করা হবে"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string>
- <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফেনের স্পিকার"</string>
+ <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফোনের স্পিকার"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string>
<string name="help_label" msgid="3528360748637781274">"সহায়তা ও মতামত"</string>
@@ -552,6 +552,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইসের ডিফল্ট"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"বন্ধ করা আছে"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই পরিবর্তনটি প্রয়োগ করার জন্য আপনার ডিভাইসটি অবশ্যই রিবুট করতে হবে। এখন রিবুট করুন বা বাতিল করুন।"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই পরিবর্তনটি প্রয়োগ করার জন্য আপনার ডিভাইসটি অবশ্যই রিবুট করতে হবে। এখনই রিবুট করুন বা বাতিল করুন।"</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"তার যুক্ত হেডফোন"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 0dda52accbc5..8ca22d7ba797 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -552,6 +552,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhedens standardindstilling"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiveret"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Din enhed skal genstartes for at denne enhed bliver anvendt. Genstart nu, eller annuller."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Din enhed skal genstartes for at anvende denne ændring. Genstart nu, eller annuller."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Høretelefoner med ledning"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 0def17e1c21a..cc3b2aa33695 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -90,7 +90,7 @@
<string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"Use for contact sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
- <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Access"</string>
+ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
<string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 0def17e1c21a..a9f039a6522c 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -90,7 +90,7 @@
<string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"Use for contact sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
- <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Access"</string>
+ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
<string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string>
@@ -147,14 +147,14 @@
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Portable hotspot"</string>
<string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth tethering"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Tethering"</string>
- <string name="tether_settings_title_all" msgid="8910259483383010470">"Tethering &amp; portable hotspot"</string>
+ <string name="tether_settings_title_all" msgid="8910259483383010470">"Tethering and portable hotspot"</string>
<string name="managed_user_title" msgid="449081789742645723">"All work apps"</string>
<string name="user_guest" msgid="6939192779649870792">"Guest"</string>
<string name="unknown" msgid="3544487229740637809">"Unknown"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"User: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"Some defaults set"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"No defaults set"</string>
- <string name="tts_settings" msgid="8130616705989351312">"Text-to-Speech settings"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"Text-to-speech settings"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"Text-to-speech output"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Speech rate"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"Speed at which the text is spoken"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 0def17e1c21a..cc3b2aa33695 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -90,7 +90,7 @@
<string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"Use for contact sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
- <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Access"</string>
+ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
<string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 0def17e1c21a..cc3b2aa33695 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -90,7 +90,7 @@
<string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"Use for contact sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
- <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Access"</string>
+ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
<string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index c27973f8bc86..287a1aca2fbd 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -22,7 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"No se pueden buscar las redes."</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ninguna"</string>
- <string name="wifi_remembered" msgid="3266709779723179188">"Guardada"</string>
+ <string name="wifi_remembered" msgid="3266709779723179188">"Guardado"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectado"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Inhabilitada"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Error de configuración IP"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index a0c00c33e466..9d3455766f6b 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -549,9 +549,9 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
- <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterm. en el dispositivo"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado por el dispositivo"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Es necesario reiniciar tu dispositivo para que se apliquen los cambios. Reiniciar ahora o cancelar."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Es necesario reiniciar tu dispositivo para que se apliquen los cambios. Reinicia ahora o cancela la acción."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculares con cable"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 376c2e796f77..0042321a3654 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -37,7 +37,7 @@
<string name="wifi_no_internet" msgid="1774198889176926299">"Ezin da konektatu Internetera"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s bidez automatikoki konektatuta"</string>
- <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikoki konektatuta sareen balorazioen hornitzailearen bidez"</string>
+ <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikoki konektatuta sare-balorazioen hornitzailearen bidez"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s bidez konektatuta"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> bidez konektatuta"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s bidez erabilgarri"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 33e6659d633b..3945e5542eb7 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -545,7 +545,7 @@
<string name="profile_info_settings_title" msgid="105699672534365099">"Profiilin tiedot"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"Ennen kuin voit luoda rajoitetun profiilin, määritä näytön lukitus, joka suojelee sovelluksiasi ja henkilökohtaisia tietojasi."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Aseta lukitus"</string>
- <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda tähän: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda tähän käyttäjään: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 09fe903baba2..1b1ae8ed55d4 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -549,7 +549,7 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
- <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Appareil par défaut"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Paramètre par défaut"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Vous devez redémarrer l\'appareil pour que cette modification soit appliquée. Redémarrez maintenant ou annulez l\'opération."</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 3e8b1c1b9cde..f9d57c453f69 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -37,7 +37,7 @@
<string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string>
- <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de rede"</string>
+ <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Wifi conectada a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dispoñible a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index bfc4a4339345..9b6a27ae8620 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -552,6 +552,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिवाइस की डिफ़ॉल्ट सेटिंग"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद है"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"बदली गई सेटिंग को लागू करने के लिए, अपने डिवाइस को फिर से चालू करें. डिवाइस को फिर से चालू करें या रद्द करें."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"बदली गई सेटिंग को लागू करने के लिए, डिवाइस को रीस्टार्ट करना होगा. अपने डिवाइस को रीस्टार्ट करें या रद्द करें."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"वायर वाला हेडफ़ोन"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 276ad88c43ef..b010b504b00c 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -116,7 +116,7 @@
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"Զուգակցել"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Չեղարկել"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Զուգակցում է մուտքի թույլտվությունը դեպի ձեր կոնտակտները և զանգերի պատմությունը, երբ միացված է:"</string>
- <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Չհաջողվեց զուգավորել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ:"</string>
+ <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Չհաջողվեց զուգակցել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ:"</string>
<string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Հնարավոր չեղավ զուգավորվել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ սխալ PIN-ի կամ անցաբառի պատճառով:."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Հնարավոր չէ կապ հաստատել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ:"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Զուգավորումը մերժվեց <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի կողմից:"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 24711ff92d05..50fdfc9a9136 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -549,9 +549,9 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string>
- <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predefinito dispositivo"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parametro predefinito"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Non attivo"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Devi riavviare il dispositivo per applicare questa modifica. Riavvia ora o annulla."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Per applicare questa modifica, devi riavviare il dispositivo. Riavvia ora o annulla."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Cuffie con cavo"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 3a45526290e0..fb7d00f866b1 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -555,5 +555,5 @@
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"מושבת"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"צריך להפעיל מחדש את המכשיר כדי להחיל את השינוי. יש להפעיל מחדש עכשיו או לבטל."</string>
- <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"אוזניות עם חוט"</string>
+ <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"אוזניות חוטיות"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index fd67e1391840..743017c82e3a 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -40,7 +40,7 @@
<item msgid="8339720953594087771">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>に接続中..."</item>
<item msgid="3028983857109369308">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>による認証中..."</item>
<item msgid="4287401332778341890">"IPアドレスを<xliff:g id="NETWORK_NAME">%1$s</xliff:g>から取得中..."</item>
- <item msgid="1043944043827424501">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>に接続しました"</item>
+ <item msgid="1043944043827424501">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> に接続済み"</item>
<item msgid="7445993821842009653">"保留中"</item>
<item msgid="1175040558087735707">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>から切断中..."</item>
<item msgid="699832486578171722">"切断されました"</item>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 0395770da819..3537ceaf8204 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -552,6 +552,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"デバイスのデフォルト"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"無効"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"この変更を適用するには、デバイスの再起動が必要です。今すぐ再起動してください。キャンセルすることもできます。"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"この変更を適用するには、デバイスの再起動が必要です。今すぐ再起動するか、キャンセルしてください。"</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"有線ヘッドフォン"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 1ee06b8353c9..279aca0731f7 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -545,7 +545,7 @@
<string name="profile_info_settings_title" msgid="105699672534365099">"Профильдік ақпарат"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"Шектелген профайл жасақтауға дейін қолданбалар мен жеке деректерді қорғау үшін экран бекітпесін тағайындау қажет."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Бекітпе тағайындау"</string>
- <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> атты пайдаланушыға ауысу"</string>
+ <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> пайдаланушысына ауысу"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 6c36d2f0f363..03065e896da2 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -552,6 +552,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"លំនាំដើម​របស់ឧបករណ៍"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"បានបិទ"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ត្រូវតែ​ចាប់ផ្ដើម​ឧបករណ៍​របស់អ្នក​ឡើងវិញ ទើប​ការផ្លាស់ប្ដូរ​នេះ​ត្រូវបានអនុវត្ត​។ ចាប់ផ្ដើមឡើងវិញ​ឥឡូវនេះ ឬ​បោះបង់​។"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ត្រូវតែ​ចាប់ផ្ដើម​ឧបករណ៍​របស់អ្នក​ឡើងវិញ ដើម្បីឱ្យ​ការផ្លាស់ប្ដូរ​នេះ​មានប្រសិទ្ធភាព។ ចាប់ផ្ដើមឡើងវិញ​ឥឡូវនេះ ឬ​បោះបង់​។"</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"កាស​មានខ្សែ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 6003e09f97bb..2702392c108f 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -552,6 +552,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Түзмөктүн демейки параметри"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өчүк"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бул өзгөртүүнү колдонуу үчүн түзмөктү өчүрүп күйгүзүңүз. Азыр өчүрүп күйгүзүңүз же жокко чыгарыңыз."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бул өзгөрүү күчүнө кириши үчүн, түзмөктү өчүрүп күйгүзүңүз. Азыр же кийинчерээк өчүрүп күйгүзсөңүз болот."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Зымдуу гарнитура"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index ac643c3a6e62..c95f8bf2fe39 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -553,5 +553,5 @@
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ഈ മാറ്റം ബാധകമാകുന്നതിന് നിങ്ങളുടെ ഉപകരണം റീബൂട്ട് ചെയ്യേണ്ടതുണ്ട്. ഇപ്പോൾ റീബൂട്ട് ചെയ്യുകയോ റദ്ദാക്കുകയോ ചെയ്യുക."</string>
- <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഹെഡ്ഫോൺ"</string>
+ <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"വയേർഡ് ഹെഡ്ഫോൺ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 0dd982b4cfc8..8407db6d3a08 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -211,8 +211,8 @@
<string name="adb_wireless_error" msgid="721958772149779856">"Алдаа"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Wireless debugging"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Боломжтой төхөөрөмжүүдийг харах болох ашиглахын тулд wireless debugging-г асаана уу"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Хурдан хариу үйлдлийн кодоор төхөөрөмжийг хослуул"</string>
- <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Хурдан хариу үйлдлийн кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR кодоор төхөөрөмжийг хослуул"</string>
+ <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"Хослуулах кодоор төхөөрөмжийг хослуулна уу"</string>
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Зургаан оронтой кодыг ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"Хослуулсан төхөөрөмжүүд"</string>
@@ -226,12 +226,12 @@
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi хослуулах код"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Хослуулалт амжилтгүй боллоо"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Төхөөрөмжийг ижил сүлжээнд холбосон эсэхийг шалгана уу."</string>
- <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
+ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Төхөөрөмжийг хослуулж байна…"</string>
- <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. Хурдан хариу үйлдлийн код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string>
+ <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. QR код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP хаяг ба порт"</string>
- <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Хурдан хариу үйлдлийн кодыг скан хийх"</string>
- <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
+ <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR кодыг скан хийх"</string>
+ <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Wi-Fi сүлжээнд холбогдоно уу"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, дебаг хийх, dev"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Алдаа мэдээлэх товчлол"</string>
@@ -552,6 +552,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Төхөөрөмжийн өгөгдмөл"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Идэвхгүй болгосон"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Энэ өөрчлөлтийг хэрэгжүүлэхийн тулд таны төхөөрөмжийг дахин асаах ёстой. Одоо дахин асаах эсвэл болино уу."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Энэ өөрчлөлтийг хэрэгжүүлэхийн тулд таны төхөөрөмжийг дахин асаах ёстой. Одоо дахин асаах эсвэл цуцлана уу."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Утастай чихэвч"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index b488c478fbc7..5ee6353f0962 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -238,7 +238,7 @@
</string-array>
<string-array name="show_non_rect_clip_entries">
<item msgid="2482978351289846212">"बन्द"</item>
- <item msgid="3405519300199774027">"गैर आयातकार क्षेत्र नीलो रङमा कोर्नुहोस्"</item>
+ <item msgid="3405519300199774027">"गैर आयातकार क्षेत्र निलो रङमा कोर्नुहोस्"</item>
<item msgid="1212561935004167943">"हाइलाइट परीक्षण चित्र कोर्ने आदेशहरू हरियोमा"</item>
</string-array>
<string-array name="track_frame_time_entries">
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index e33df4ad7d1c..32cc39ec5b4f 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -37,7 +37,7 @@
<string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string>
<string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string>
- <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via netwerkbeoordelaar"</string>
+ <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Beschikbaar via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index df9443057e28..15700130cf78 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -549,7 +549,7 @@
<string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string>
- <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਡੀਵਾਈਸ"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ਡੀਵਾਈਸ ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ਇਸ ਤਬਦੀਲੀ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨੂੰ ਰੀਬੂਟ ਕਰਨਾ ਲਾਜ਼ਮੀ ਹੈ। ਹੁਣੇ ਰੀਬੂਟ ਕਰੋ ਜਾਂ ਰੱਦ ਕਰੋ।"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 40022677aeb4..508cbfccffe9 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -140,8 +140,8 @@
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string>
- <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Aplicações removidas"</string>
- <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Aplicações e utilizadores removidos"</string>
+ <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Apps removidas"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Apps e utilizadores removidos"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"Atualizações do sistema"</string>
<string name="tether_settings_title_usb" msgid="3728686573430917722">"Ligação USB"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Hotspot portátil"</string>
@@ -365,7 +365,7 @@
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração de animação"</string>
<string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular apresentações secundárias"</string>
- <string name="debug_applications_category" msgid="5394089406638954196">"Aplicações"</string>
+ <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string>
<string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string>
<string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Destruir atividades assim que o utilizador sair"</string>
<string name="app_process_limit_title" msgid="8361367869453043007">"Limite do processo em 2º plano"</string>
@@ -396,7 +396,7 @@
<item msgid="4548987861791236754">"Cores naturais e realistas"</item>
<item msgid="1282170165150762976">"Cores otimizadas para conteúdos digitais"</item>
</string-array>
- <string name="inactive_apps_title" msgid="5372523625297212320">"Aplicações em espera"</string>
+ <string name="inactive_apps_title" msgid="5372523625297212320">"Apps em espera"</string>
<string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Toque para ativar/desativar."</string>
<string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Toque para ativar/desativar."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"Estado do Modo de espera das apps:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
@@ -458,7 +458,7 @@
<string name="disabled" msgid="8017887509554714950">"Desativada"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Autorizada"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Não autorizada"</string>
- <string name="install_other_apps" msgid="3232595082023199454">"Instalar aplicações desconhecidas"</string>
+ <string name="install_other_apps" msgid="3232595082023199454">"Instalar apps desconhecidas"</string>
<string name="home" msgid="973834627243661438">"Página inicial de definições"</string>
<string-array name="battery_labels">
<item msgid="7878690469765357158">"0%"</item>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index f43387955711..663d3f702ae5 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -546,7 +546,7 @@
<string name="profile_info_settings_title" msgid="105699672534365099">"Informații de profil"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"Înainte de a putea crea un profil cu permisiuni limitate, va trebui să configurați blocarea ecranului pentru a vă proteja aplicațiile și datele personale."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Configurați blocarea"</string>
- <string name="user_switch_to_user" msgid="6975428297154968543">"Comutați la <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="user_switch_to_user" msgid="6975428297154968543">"Treceți la <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index fc9a49ce0a5a..f39a741e3a4b 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -554,6 +554,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predvol. nastavenie zariadenia"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuté"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Táto zmena sa uplatní až po reštartovaní zariadenia. Zariadenie reštartujte alebo zmenu zrušte."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Zmena sa prejaví až po reštarte zariadenia. Môžete ho teraz reštartovať alebo akciu zrušiť."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Slúchadlá s káblom"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 41ccdeb29f3e..5c80627003cd 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -553,5 +553,5 @@
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Imezimwa"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Ni lazima uwashe tena kifaa chako ili mabadiliko haya yatekelezwe. Washa tena sasa au ughairi."</string>
- <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Vipokea sauti vyenye waya vinavyobanwa kichwani"</string>
+ <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Vipokea sauti vya waya"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index b113affdac71..e6d938047fcf 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -552,6 +552,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz varsayılanı"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Devre dışı"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu değişikliğin geçerli olması için cihazının yeniden başlatılması gerekir. Şimdi yeniden başlatın veya iptal edin."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu değişikliğin geçerli olması için cihazınızın yeniden başlatılması gerekir. Şimdi yeniden başlatın veya iptal edin."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Kablolu kulaklık"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 83329401f0bd..cf1fafd8f190 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -551,9 +551,9 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гість"</string>
- <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Пристрій за умовчанням"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"За умовчанням для пристрою"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Вимкнено"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Щоб застосувати ці зміни, перезапустіть пристрій. Перезапустіть пристрій або скасуйте зміни."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Щоб застосувати ці зміни, потрібний перезапуск. Перезапустіть пристрій або скасуйте зміни."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Дротові навушники"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 05cfeb67f86a..b7fbe6fad392 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -545,7 +545,7 @@
<string name="profile_info_settings_title" msgid="105699672534365099">"پروفائل کی معلومات"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"ایک محدود پروفائل بنانے سے پہلے، آپ کو اپنی ایپس اور ذاتی ڈیٹا کو محفوظ کرنے کیلئے ایک اسکرین لاک سیٹ اپ کرنا ہوگا۔"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"لاک سیٹ کریں"</string>
- <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> پر سوئچ کریں"</string>
+ <string name="user_switch_to_user" msgid="6975428297154968543">"‫<xliff:g id="USER_NAME">%s</xliff:g> پر سوئچ کریں"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string>
<string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 29a96371a2b1..f81731ab2723 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -552,6 +552,6 @@
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Qurilma standarti"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Yoqilmagan"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string>
- <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Oʻzgarishlar qurilma oʻchib yonganda bajariladi. Hoziroq oʻchib yoqish yoki bekor qilish."</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Oʻzgarishlar kuchga kirishi uchun qurilmani oʻchirib yoqing. Buni hozir yoki keyinroq bajarishingiz mumkin."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Simli quloqlik"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 7e1b476b7a0c..b7ccf8d85677 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -549,7 +549,7 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Khách"</string>
- <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cài đặt mặc định của thiết bị"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Theo giá trị mặc định của thiết bị"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Đã tắt"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bạn phải khởi động lại thiết bị để áp dụng sự thay đổi này. Hãy khởi động lại ngay hoặc hủy."</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 95e916b9871a..68f72896c251 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,6 +1,6 @@
package com.android.settingslib.bluetooth;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED;
+import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -238,7 +238,7 @@ public class BluetoothUtils {
final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
iconSize, false);
bitmap.recycle();
- return new AdaptiveOutlineDrawable(resources, resizedBitmap, TYPE_ADVANCED);
+ return new AdaptiveOutlineDrawable(resources, resizedBitmap, ICON_TYPE_ADVANCED);
}
return drawable;
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 231809bfece4..e5fd0ba5d9bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -32,12 +32,11 @@ import androidx.annotation.VisibleForTesting;
import androidx.core.text.BidiFormatter;
import androidx.core.text.TextDirectionHeuristicsCompat;
+import com.android.i18n.timezone.CountryTimeZones;
+import com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping;
+import com.android.i18n.timezone.TimeZoneFinder;
import com.android.settingslib.R;
-import libcore.timezone.CountryTimeZones;
-import libcore.timezone.CountryTimeZones.TimeZoneMapping;
-import libcore.timezone.TimeZoneFinder;
-
import org.xmlpull.v1.XmlPullParserException;
import java.util.ArrayList;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
index 278b57da0c28..e5ea4467517b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -41,7 +41,7 @@ public class CircleFramedDrawable extends Drawable {
private final Bitmap mBitmap;
private final int mSize;
- private final Paint mPaint;
+ private Paint mIconPaint;
private float mScale;
private Rect mSrcRect;
@@ -75,18 +75,18 @@ public class CircleFramedDrawable extends Drawable {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
// opaque circle matte
- mPaint = new Paint();
- mPaint.setAntiAlias(true);
- mPaint.setColor(Color.BLACK);
- mPaint.setStyle(Paint.Style.FILL);
- canvas.drawPath(fillPath, mPaint);
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setColor(Color.BLACK);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawPath(fillPath, paint);
// mask in the icon where the bitmap is opaque
- mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(icon, cropRect, circleRect, paint);
// prepare paint for frame drawing
- mPaint.setXfermode(null);
+ paint.setXfermode(null);
mScale = 1f;
@@ -100,7 +100,7 @@ public class CircleFramedDrawable extends Drawable {
final float pad = (mSize - inside) / 2f;
mDstRect.set(pad, pad, mSize - pad, mSize - pad);
- canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+ canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, mIconPaint);
}
public void setScale(float scale) {
@@ -122,8 +122,12 @@ public class CircleFramedDrawable extends Drawable {
@Override
public void setColorFilter(ColorFilter cf) {
+ if (mIconPaint == null) {
+ mIconPaint = new Paint();
+ }
+ mIconPaint.setColorFilter(cf);
}
-
+
@Override
public int getIntrinsicWidth() {
return mSize;
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index ac7a12187fd6..121f5492a5ab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -38,7 +38,10 @@ public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> {
"/odm/etc/NOTICE.xml.gz",
"/oem/etc/NOTICE.xml.gz",
"/product/etc/NOTICE.xml.gz",
- "/system_ext/etc/NOTICE.xml.gz"};
+ "/system_ext/etc/NOTICE.xml.gz",
+ "/vendor_dlkm/etc/NOTICE.xml.gz",
+ "/odm_dlkm/etc/NOTICE.xml.gz",
+ };
static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
private final Context mContext;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index ea71e52dc9c9..949b2456042c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib.media;
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK;
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK;
import static android.media.MediaRoute2Info.TYPE_GROUP;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
@@ -29,6 +31,8 @@ import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
+import java.util.List;
+
/**
* InfoMediaDevice extends MediaDevice to represents wifi device.
*/
@@ -62,7 +66,7 @@ public class InfoMediaDevice extends MediaDevice {
@Override
public Drawable getIconWithoutBackground() {
- return mContext.getDrawable(getDrawableResId());
+ return mContext.getDrawable(getDrawableResIdByFeature());
}
@VisibleForTesting
@@ -83,6 +87,21 @@ public class InfoMediaDevice extends MediaDevice {
return resId;
}
+ @VisibleForTesting
+ int getDrawableResIdByFeature() {
+ int resId;
+ final List<String> features = mRouteInfo.getFeatures();
+ if (features.contains(FEATURE_REMOTE_GROUP_PLAYBACK)) {
+ resId = R.drawable.ic_media_group_device;
+ } else if (features.contains(FEATURE_REMOTE_VIDEO_PLAYBACK)) {
+ resId = R.drawable.ic_media_display_device;
+ } else {
+ resId = R.drawable.ic_media_speaker_device;
+ }
+
+ return resId;
+ }
+
@Override
public String getId() {
return MediaDeviceUtils.getId(mRouteInfo);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 8968340b65f4..c5e66bef9653 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1117,14 +1117,16 @@ public class AccessPoint implements Comparable<AccessPoint> {
* Returns the display title for the AccessPoint, such as for an AccessPointPreference's title.
*/
public String getTitle() {
- if (isPasspoint()) {
+ if (isPasspoint() && !TextUtils.isEmpty(mConfig.providerFriendlyName)) {
return mConfig.providerFriendlyName;
- } else if (isPasspointConfig()) {
+ } else if (isPasspointConfig() && !TextUtils.isEmpty(mProviderFriendlyName)) {
return mProviderFriendlyName;
- } else if (isOsuProvider()) {
+ } else if (isOsuProvider() && !TextUtils.isEmpty(mOsuProvider.getFriendlyName())) {
return mOsuProvider.getFriendlyName();
- } else {
+ } else if (!TextUtils.isEmpty(getSsidStr())) {
return getSsidStr();
+ } else {
+ return "";
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 6269a717b333..fd986e5d13fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -90,7 +90,7 @@ public class AccessPointPreference extends Preference {
return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
}
- // Used for dummy pref.
+ // Used for fake pref.
public AccessPointPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mFrictionSld = null;
@@ -142,7 +142,7 @@ public class AccessPointPreference extends Preference {
public void onBindViewHolder(final PreferenceViewHolder view) {
super.onBindViewHolder(view);
if (mAccessPoint == null) {
- // Used for dummy pref.
+ // Used for fake pref.
return;
}
Drawable drawable = getIcon();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index d7e76a14c768..b7ae3dca5c16 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -214,6 +214,9 @@ public class WifiStatusTracker {
}
private void updateStatusLabel() {
+ if (mWifiManager == null) {
+ return;
+ }
NetworkCapabilities networkCapabilities;
final Network currentWifiNetwork = mWifiManager.getCurrentNetwork();
if (currentWifiNetwork != null && currentWifiNetwork.equals(mDefaultNetwork)) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 3015397ff1a3..bf5ab1c9951a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -256,7 +256,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
/**
- * Sanity warning: this wipes out mScoreCache, so use with extreme caution
+ * Validity warning: this wipes out mScoreCache, so use with extreme caution
* @param workThread substitute Handler thread, for testing purposes only
*/
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 0e6a60bf47c1..1ace0b4250b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -118,7 +118,7 @@ public class WifiUtils {
final int maxDisplayedScans = 4;
int num5 = 0; // number of scanned BSSID on 5GHz band
int num24 = 0; // number of scanned BSSID on 2.4Ghz band
- int numBlackListed = 0;
+ int numBlockListed = 0;
// TODO: sort list by RSSI or age
long nowMs = SystemClock.elapsedRealtime();
@@ -170,8 +170,8 @@ public class WifiUtils {
}
visibility.append(scans5GHz.toString());
}
- if (numBlackListed > 0) {
- visibility.append("!").append(numBlackListed);
+ if (numBlockListed > 0) {
+ visibility.append("!").append(numBlockListed);
}
visibility.append("]");
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index bcabec858487..46ecbd45a860 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -148,6 +148,17 @@ public class AccessPointTest {
}
@Test
+ public void testCompareTo_GivesNull() {
+ WifiConfiguration spyConfig = spy(new WifiConfiguration());
+
+ when(spyConfig.isPasspoint()).thenReturn(true);
+ spyConfig.providerFriendlyName = null;
+ AccessPoint passpointAp = new AccessPoint(mContext, spyConfig);
+
+ assertThat(passpointAp.getTitle()).isEqualTo("");
+ }
+
+ @Test
public void testCompareTo_GivesActiveBeforeInactive() {
AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build();
AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index 49b236a2188f..c45b7f333fa1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.media;
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK;
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK;
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK;
import static android.media.MediaRoute2Info.TYPE_GROUP;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
@@ -38,6 +41,8 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.ArrayList;
+
@RunWith(RobolectricTestRunner.class)
public class InfoMediaDeviceTest {
@@ -107,4 +112,28 @@ public class InfoMediaDeviceTest {
assertThat(mInfoMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_media_group_device);
}
+
+ @Test
+ public void getDrawableResIdByFeature_returnCorrectResId() {
+ final ArrayList<String> features = new ArrayList<>();
+ features.add(FEATURE_REMOTE_VIDEO_PLAYBACK);
+ when(mRouteInfo.getFeatures()).thenReturn(features);
+
+ assertThat(mInfoMediaDevice.getDrawableResIdByFeature()).isEqualTo(
+ R.drawable.ic_media_display_device);
+
+ features.clear();
+ features.add(FEATURE_REMOTE_AUDIO_PLAYBACK);
+ when(mRouteInfo.getFeatures()).thenReturn(features);
+
+ assertThat(mInfoMediaDevice.getDrawableResIdByFeature()).isEqualTo(
+ R.drawable.ic_media_speaker_device);
+
+ features.clear();
+ features.add(FEATURE_REMOTE_GROUP_PLAYBACK);
+ when(mRouteInfo.getFeatures()).thenReturn(features);
+
+ assertThat(mInfoMediaDevice.getDrawableResIdByFeature()).isEqualTo(
+ R.drawable.ic_media_group_device);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 94d95f06050d..68f162ec0226 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -27,7 +27,9 @@ import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,6 +38,7 @@ import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.RoutingSessionInfo;
+import android.media.session.MediaSessionManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -68,6 +71,8 @@ public class InfoMediaManagerTest {
private LocalBluetoothManager mLocalBluetoothManager;
@Mock
private MediaManager.MediaDeviceCallback mCallback;
+ @Mock
+ private MediaSessionManager mMediaSessionManager;
private InfoMediaManager mInfoMediaManager;
private Context mContext;
@@ -76,8 +81,10 @@ public class InfoMediaManagerTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = spy(RuntimeEnvironment.application);
+ doReturn(mMediaSessionManager).when(mContext).getSystemService(
+ Context.MEDIA_SESSION_SERVICE);
mInfoMediaManager =
new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null, mLocalBluetoothManager);
mShadowRouter2Manager = ShadowRouter2Manager.getShadow();
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2da84c28d163..18c2957b1adc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -172,5 +172,7 @@ public class SecureSettings {
Settings.Secure.ONE_HANDED_MODE_ENABLED,
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
Settings.Secure.TAPS_APP_TO_EXIT,
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+ Settings.Secure.PANIC_GESTURE_ENABLED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 3fd543dd4990..91f3f4af0566 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -259,5 +259,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.PANIC_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a946888bcf9b..ef3bdbc95809 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -866,9 +866,6 @@ class SettingsProtoDumpUtil {
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
GlobalSettingsProto.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
dumpSetting(s, p,
- Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS,
- GlobalSettingsProto.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);
- dumpSetting(s, p,
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
GlobalSettingsProto.KEEP_PROFILE_IN_BACKGROUND);
@@ -2026,6 +2023,13 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
SecureSettingsProto.EMERGENCY_ASSISTANCE_APPLICATION);
+
+ final long emergencyResponseToken = p.start(SecureSettingsProto.EMERGENCY_RESPONSE);
+ dumpSetting(s, p,
+ Settings.Secure.PANIC_GESTURE_ENABLED,
+ SecureSettingsProto.EmergencyResponse.PANIC_GESTURE_ENABLED);
+ p.end(emergencyResponseToken);
+
dumpSetting(s, p,
Settings.Secure.ENHANCED_VOICE_PRIVACY_ENABLED,
SecureSettingsProto.ENHANCED_VOICE_PRIVACY_ENABLED);
@@ -2573,6 +2577,10 @@ class SettingsProtoDumpUtil {
Settings.Secure.TAPS_APP_TO_EXIT,
SecureSettingsProto.OneHanded.TAPS_APP_TO_EXIT);
+ dumpSetting(s, p,
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+ SecureSettingsProto.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
+
// Please insert new settings using the same order as in SecureSettingsProto.
p.end(token);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b90b9c1208ae..6914f0a1e33a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -55,11 +55,11 @@ public class SettingsBackupTest {
"hybrid_sysui_battery_warning_flags";
/**
- * The following blacklists contain settings that should *not* be backed up and restored to
+ * The following denylists contain settings that should *not* be backed up and restored to
* another device. As a general rule, anything that is not user configurable should be
- * blacklisted (and conversely, things that *are* user configurable *should* be backed up)
+ * denied (and conversely, things that *are* user configurable *should* be backed up)
*/
- private static final Set<String> BACKUP_BLACKLISTED_SYSTEM_SETTINGS =
+ private static final Set<String> BACKUP_DENY_LIST_SYSTEM_SETTINGS =
newHashSet(
Settings.System.ADVANCED_SETTINGS, // candidate for backup?
Settings.System.ALARM_ALERT_CACHE, // internal cache
@@ -105,7 +105,7 @@ public class SettingsBackupTest {
Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
);
- private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
+ private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS =
newHashSet(
Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED,
@@ -311,7 +311,6 @@ public class SettingsBackupTest {
Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
Settings.Global.JOB_SCHEDULER_CONSTANTS,
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
- Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS,
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
Settings.Global.KERNEL_CPU_THREAD_READER,
Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
@@ -591,7 +590,7 @@ public class SettingsBackupTest {
Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT);
- private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
+ private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O.
@@ -746,42 +745,42 @@ public class SettingsBackupTest {
Settings.Secure.SUPPRESS_DOZE);
@Test
- public void systemSettingsBackedUpOrBlacklisted() {
- checkSettingsBackedUpOrBlacklisted(
+ public void systemSettingsBackedUpOrDenied() {
+ checkSettingsBackedUpOrDenied(
getCandidateSettings(Settings.System.class),
newHashSet(SystemSettings.SETTINGS_TO_BACKUP),
- BACKUP_BLACKLISTED_SYSTEM_SETTINGS);
+ BACKUP_DENY_LIST_SYSTEM_SETTINGS);
}
@Test
- public void globalSettingsBackedUpOrBlacklisted() {
- checkSettingsBackedUpOrBlacklisted(
+ public void globalSettingsBackedUpOrDenied() {
+ checkSettingsBackedUpOrDenied(
getCandidateSettings(Settings.Global.class),
newHashSet(GlobalSettings.SETTINGS_TO_BACKUP),
- BACKUP_BLACKLISTED_GLOBAL_SETTINGS);
+ BACKUP_DENY_LIST_GLOBAL_SETTINGS);
}
@Test
@Suppress //("b/148236308")
- public void secureSettingsBackedUpOrBlacklisted() {
+ public void secureSettingsBackedUpOrDenied() {
HashSet<String> keys = new HashSet<String>();
Collections.addAll(keys, SecureSettings.SETTINGS_TO_BACKUP);
Collections.addAll(keys, DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
- checkSettingsBackedUpOrBlacklisted(
+ checkSettingsBackedUpOrDenied(
getCandidateSettings(Settings.Secure.class),
keys,
- BACKUP_BLACKLISTED_SECURE_SETTINGS);
+ BACKUP_DENY_LIST_SECURE_SETTINGS);
}
- private static void checkSettingsBackedUpOrBlacklisted(
- Set<String> settings, Set<String> settingsToBackup, Set<String> blacklist) {
+ private static void checkSettingsBackedUpOrDenied(
+ Set<String> settings, Set<String> settingsToBackup, Set<String> denylist) {
Set<String> settingsNotBackedUp = difference(settings, settingsToBackup);
- Set<String> settingsNotBackedUpOrBlacklisted = difference(settingsNotBackedUp, blacklist);
- assertWithMessage("Settings not backed up or blacklisted")
- .that(settingsNotBackedUpOrBlacklisted).isEmpty();
+ Set<String> settingsNotBackedUpOrDenied = difference(settingsNotBackedUp, denylist);
+ assertWithMessage("Settings not backed up or denied")
+ .that(settingsNotBackedUpOrDenied).isEmpty();
- assertWithMessage("blacklisted settings backed up")
- .that(intersect(settingsToBackup, blacklist)).isEmpty();
+ assertWithMessage("denied settings backed up")
+ .that(intersect(settingsToBackup, denylist)).isEmpty();
}
private static Set<String> getCandidateSettings(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 9cecd52217a9..8253c5e642f3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -214,6 +214,9 @@
<!-- Permission needed to test tcp keepalive offload. -->
<uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
+ <!-- Permission needed for CTS test - UnsupportedErrorDialogTests -->
+ <uses-permission android:name="android.permission.RESET_APP_ERRORS" />
+
<!-- Permission needed to run keyguard manager tests in CTS -->
<uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
@@ -314,6 +317,14 @@
<!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
+ <!-- Permissions required for CTS test - AdbManagerTest -->
+ <uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
+
+ <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
+ <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" />
+ <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
+ <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/TEST_MAPPING b/packages/Shell/TEST_MAPPING
new file mode 100644
index 000000000000..a149b5cda3ea
--- /dev/null
+++ b/packages/Shell/TEST_MAPPING
@@ -0,0 +1,31 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsBugreportTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ },
+ {
+ "name": "ShellTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsBugreportTestCases"
+ },
+ {
+ "name": "ShellTests"
+ }
+ ]
+}
diff --git a/packages/Shell/res/values-bn/strings.xml b/packages/Shell/res/values-bn/strings.xml
index 39f62fe6a00a..ede125bce935 100644
--- a/packages/Shell/res/values-bn/strings.xml
+++ b/packages/Shell/res/values-bn/strings.xml
@@ -35,7 +35,7 @@
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"জিপ ফাইলে ত্রুটি প্রতিবেদনের বিশদ বিবরণ যোগ করা যায়নি"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"নামবিহীন"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"বিশদ বিবরণ"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"স্ক্রিনশট"</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"স্ক্রিনশট নিন"</string>
<string name="bugreport_screenshot_taken" msgid="5684211273096253120">"স্ক্রিনশট সফলভাবে নেওয়া হয়েছে৷"</string>
<string name="bugreport_screenshot_failed" msgid="5853049140806834601">"স্ক্রিনশট নেওয়া যায়নি৷"</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"ত্রুটির প্রতিবেদন <xliff:g id="ID">#%d</xliff:g> এর বিশদ বিবরণ"</string>
diff --git a/packages/SoundPicker/res/values-af/strings.xml b/packages/SoundPicker/res/values-af/strings.xml
index 3c0efe9704bb..fd857b1c3cc4 100644
--- a/packages/SoundPicker/res/values-af/strings.xml
+++ b/packages/SoundPicker/res/values-af/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Vee uit"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Kan nie gepasmaakte luitoon byvoeg nie"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Kan nie gepasmaakte luitoon uitvee nie"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Klanke"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-am/strings.xml b/packages/SoundPicker/res/values-am/strings.xml
index 953aaa74cf83..07aee8a646ec 100644
--- a/packages/SoundPicker/res/values-am/strings.xml
+++ b/packages/SoundPicker/res/values-am/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"ሰርዝ"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"ብጁ የጥሪ ቅላጼን ማከል አልተቻለም"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"ብጁ የጥሪ ቅላጼን መሰረዝ አልተቻለም"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ድምፆች"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ar/strings.xml b/packages/SoundPicker/res/values-ar/strings.xml
index 6110097240f0..a91795545269 100644
--- a/packages/SoundPicker/res/values-ar/strings.xml
+++ b/packages/SoundPicker/res/values-ar/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"حذف"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"يتعذر إضافة نغمة رنين مخصصة"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"يتعذر حذف نغمة الرنين المخصصة"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-as/strings.xml b/packages/SoundPicker/res/values-as/strings.xml
index 8bd1f9871ac7..5d6bc5d603b4 100644
--- a/packages/SoundPicker/res/values-as/strings.xml
+++ b/packages/SoundPicker/res/values-as/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"মচক"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"নিজৰ উপযোগিতা অনুযায়ী তৈয়াৰ কৰা ৰিংট\'ন যোগ কৰিব পৰা নগ\'ল"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"নিজৰ উপযোগিতা অনুযায়ী তৈয়াৰ কৰা ৰিংট\'ন মচিব পৰা নগ\'ল"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ধ্বনিসমূহ"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-az/strings.xml b/packages/SoundPicker/res/values-az/strings.xml
index 20a268071b5a..e32c3eb5d9e7 100644
--- a/packages/SoundPicker/res/values-az/strings.xml
+++ b/packages/SoundPicker/res/values-az/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Silin"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Fərdi zəng səsi əlavə etmək mümkün deyil"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Fərdi zəng səsini silmək mümkün deyil"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Səslər"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-b+sr+Latn/strings.xml b/packages/SoundPicker/res/values-b+sr+Latn/strings.xml
index eb53de69f27a..947c85c8ad91 100644
--- a/packages/SoundPicker/res/values-b+sr+Latn/strings.xml
+++ b/packages/SoundPicker/res/values-b+sr+Latn/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Izbriši"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Dodavanje prilagođene melodije zvona nije uspelo"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Brisanje prilagođene melodije zvona nije uspelo"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Zvukovi"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-be/strings.xml b/packages/SoundPicker/res/values-be/strings.xml
index 593a68f7491f..6f7fc6882d76 100644
--- a/packages/SoundPicker/res/values-be/strings.xml
+++ b/packages/SoundPicker/res/values-be/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Выдаліць"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Немагчыма дадаць карыстальніцкі рынгтон"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Немагчыма выдаліць карыстальніцкі рынгтон"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Гукі"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-bg/strings.xml b/packages/SoundPicker/res/values-bg/strings.xml
index 4bc8228a85fc..4277d2851e8c 100644
--- a/packages/SoundPicker/res/values-bg/strings.xml
+++ b/packages/SoundPicker/res/values-bg/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Изтриване"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Персонализираната мелодия не може да се добави"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Персонализираната мелодия не може да се изтрие"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-bn/strings.xml b/packages/SoundPicker/res/values-bn/strings.xml
index 000811887788..276594ad4c77 100644
--- a/packages/SoundPicker/res/values-bn/strings.xml
+++ b/packages/SoundPicker/res/values-bn/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"মুছুন"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"কাস্টম রিংটোন যোগ করা গেল না"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"কাস্টম রিংটোন মোছা গেল না"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-bs/strings.xml b/packages/SoundPicker/res/values-bs/strings.xml
index 893d832919f4..0c8d33f4187e 100644
--- a/packages/SoundPicker/res/values-bs/strings.xml
+++ b/packages/SoundPicker/res/values-bs/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Izbriši"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Nije moguće dodati prilagođenu melodiju zvona"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Nije moguće izbrisati prilagođenu melodiju zvona"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Zvukovi"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ca/strings.xml b/packages/SoundPicker/res/values-ca/strings.xml
index d14c87112546..ed96f70c7c01 100644
--- a/packages/SoundPicker/res/values-ca/strings.xml
+++ b/packages/SoundPicker/res/values-ca/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Suprimeix"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"No es pot afegir el so de trucada personalitzat"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"No es pot suprimir el so de trucada personalitzat"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sons"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-cs/strings.xml b/packages/SoundPicker/res/values-cs/strings.xml
index 7e3accf11f62..e8fc97e44174 100644
--- a/packages/SoundPicker/res/values-cs/strings.xml
+++ b/packages/SoundPicker/res/values-cs/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Smazat"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Vlastní vyzváněcí tón se nepodařilo přidat"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Vlastní vyzváněcí tón se nepodařilo smazat"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Zvuky"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-da/strings.xml b/packages/SoundPicker/res/values-da/strings.xml
index bccd4223c741..b4437dc805ac 100644
--- a/packages/SoundPicker/res/values-da/strings.xml
+++ b/packages/SoundPicker/res/values-da/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Slet"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Den tilpassede ringetone kunne ikke tilføjes"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Den tilpassede ringetone kunne ikke slettes"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Lyde"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-de/strings.xml b/packages/SoundPicker/res/values-de/strings.xml
index 1674f307afe8..8be3aaab7eb7 100644
--- a/packages/SoundPicker/res/values-de/strings.xml
+++ b/packages/SoundPicker/res/values-de/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Löschen"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Benutzerdefinierter Klingelton konnte nicht hinzugefügt werden"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Benutzerdefinierter Klingelton konnte nicht gelöscht werden"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Töne"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-el/strings.xml b/packages/SoundPicker/res/values-el/strings.xml
index d0d9d0d495a2..41e9b0ccf334 100644
--- a/packages/SoundPicker/res/values-el/strings.xml
+++ b/packages/SoundPicker/res/values-el/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Διαγραφή"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Δεν είναι δυνατή η προσθήκη προσαρμοσμένου ήχου κλήσης"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Δεν είναι δυνατή η διαγραφή προσαρμοσμένου ήχου κλήσης"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Ήχοι"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-en-rAU/strings.xml b/packages/SoundPicker/res/values-en-rAU/strings.xml
index 0ffb3fb1be86..4c237b993e07 100644
--- a/packages/SoundPicker/res/values-en-rAU/strings.xml
+++ b/packages/SoundPicker/res/values-en-rAU/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Delete"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Unable to add customised ringtone"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Unable to delete customised ringtone"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-en-rCA/strings.xml b/packages/SoundPicker/res/values-en-rCA/strings.xml
index 0ffb3fb1be86..4c237b993e07 100644
--- a/packages/SoundPicker/res/values-en-rCA/strings.xml
+++ b/packages/SoundPicker/res/values-en-rCA/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Delete"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Unable to add customised ringtone"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Unable to delete customised ringtone"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-en-rGB/strings.xml b/packages/SoundPicker/res/values-en-rGB/strings.xml
index 0ffb3fb1be86..4c237b993e07 100644
--- a/packages/SoundPicker/res/values-en-rGB/strings.xml
+++ b/packages/SoundPicker/res/values-en-rGB/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Delete"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Unable to add customised ringtone"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Unable to delete customised ringtone"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-en-rIN/strings.xml b/packages/SoundPicker/res/values-en-rIN/strings.xml
index 0ffb3fb1be86..4c237b993e07 100644
--- a/packages/SoundPicker/res/values-en-rIN/strings.xml
+++ b/packages/SoundPicker/res/values-en-rIN/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Delete"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Unable to add customised ringtone"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Unable to delete customised ringtone"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-en-rXC/strings.xml b/packages/SoundPicker/res/values-en-rXC/strings.xml
index 126effcbafdc..8397e0bb639a 100644
--- a/packages/SoundPicker/res/values-en-rXC/strings.xml
+++ b/packages/SoundPicker/res/values-en-rXC/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎Delete‎‏‎‎‏‎"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‏‏‎‏‏‏‏‏‎‎Unable to add custom ringtone‎‏‎‎‏‎"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‎‏‎‎‎‎Unable to delete custom ringtone‎‏‎‎‏‎"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎Sounds‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-es-rUS/strings.xml b/packages/SoundPicker/res/values-es-rUS/strings.xml
index 42b61c113fff..5bf73b2fe2b8 100644
--- a/packages/SoundPicker/res/values-es-rUS/strings.xml
+++ b/packages/SoundPicker/res/values-es-rUS/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Borrar"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"No se puede agregar el tono personalizado"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"No se puede borrar el tono personalizado"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sonidos"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-es/strings.xml b/packages/SoundPicker/res/values-es/strings.xml
index e9e73ba4ce65..a77f656f14e5 100644
--- a/packages/SoundPicker/res/values-es/strings.xml
+++ b/packages/SoundPicker/res/values-es/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Eliminar"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"No se ha podido añadir un tono de llamada personalizado"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"No se ha podido eliminar un tono de llamada personalizado"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sonidos"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-et/strings.xml b/packages/SoundPicker/res/values-et/strings.xml
index 988476375c38..fa680accddf5 100644
--- a/packages/SoundPicker/res/values-et/strings.xml
+++ b/packages/SoundPicker/res/values-et/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Kustuta"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Kohandatud helinat ei õnnestu lisada"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Kohandatud helinat ei õnnestu kustutada"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Helid"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-eu/strings.xml b/packages/SoundPicker/res/values-eu/strings.xml
index 0d64523aa3e9..e8e07fe00d47 100644
--- a/packages/SoundPicker/res/values-eu/strings.xml
+++ b/packages/SoundPicker/res/values-eu/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Ezabatu"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Ezin da gehitu tonu pertsonalizatua"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Ezin da ezabatu tonu pertsonalizatua"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Soinuak"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-fa/strings.xml b/packages/SoundPicker/res/values-fa/strings.xml
index 4e55cbbcb632..dc7c214977f2 100644
--- a/packages/SoundPicker/res/values-fa/strings.xml
+++ b/packages/SoundPicker/res/values-fa/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"حذف"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"افزودن آهنگ زنگ سفارشی ممکن نیست"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"حذف آهنگ زنگ سفارشی ممکن نیست"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"صداها"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-fi/strings.xml b/packages/SoundPicker/res/values-fi/strings.xml
index 86690bb04d6e..9f64f8379b43 100644
--- a/packages/SoundPicker/res/values-fi/strings.xml
+++ b/packages/SoundPicker/res/values-fi/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Poista"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Muokatun soittoäänen lisääminen epäonnistui."</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Muokatun soittoäänen poistaminen epäonnistui."</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Äänet"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-fr-rCA/strings.xml b/packages/SoundPicker/res/values-fr-rCA/strings.xml
index cbf05909470b..4d4545fd598c 100644
--- a/packages/SoundPicker/res/values-fr-rCA/strings.xml
+++ b/packages/SoundPicker/res/values-fr-rCA/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Supprimer"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Impossible d\'ajouter une sonnerie personnalisée"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Impossible de supprimer la sonnerie personnalisée"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sons"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-fr/strings.xml b/packages/SoundPicker/res/values-fr/strings.xml
index cbf05909470b..9452e70ff8df 100644
--- a/packages/SoundPicker/res/values-fr/strings.xml
+++ b/packages/SoundPicker/res/values-fr/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Supprimer"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Impossible d\'ajouter une sonnerie personnalisée"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Impossible de supprimer la sonnerie personnalisée"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-gl/strings.xml b/packages/SoundPicker/res/values-gl/strings.xml
index ac7283934084..59a9d066a7aa 100644
--- a/packages/SoundPicker/res/values-gl/strings.xml
+++ b/packages/SoundPicker/res/values-gl/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Eliminar"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Non se pode engadir un ton de chamada personalizado"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Non se pode eliminar un ton de chamada personalizado"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sons"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-gu/strings.xml b/packages/SoundPicker/res/values-gu/strings.xml
index 0e5cd45812d6..f50dc9a8d8fa 100644
--- a/packages/SoundPicker/res/values-gu/strings.xml
+++ b/packages/SoundPicker/res/values-gu/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"ડિલીટ કરો"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"કસ્ટમ રિંગટોન ઉમેરવામાં અસમર્થ"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"કસ્ટમ રિંગટોન કાઢી નાખવામાં અસમર્થ"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-hi/strings.xml b/packages/SoundPicker/res/values-hi/strings.xml
index 80c42a1074d8..ab3b7f802b0f 100644
--- a/packages/SoundPicker/res/values-hi/strings.xml
+++ b/packages/SoundPicker/res/values-hi/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"मिटाएं"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"आपके मुताबिक रिंगटोन नहीं जोड़ी जा सकी"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"आपके मुताबिक रिंगटोन नहीं हटाई जा सकी"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-hr/strings.xml b/packages/SoundPicker/res/values-hr/strings.xml
index bcb36d0393f5..f74c4ae69e80 100644
--- a/packages/SoundPicker/res/values-hr/strings.xml
+++ b/packages/SoundPicker/res/values-hr/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Izbriši"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Dodavanje prilagođene melodije zvona nije moguće"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Brisanje prilagođene melodije zvona nije moguće"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Zvukovi"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-hu/strings.xml b/packages/SoundPicker/res/values-hu/strings.xml
index c7a5cf00b0e2..32d4ba90eb9a 100644
--- a/packages/SoundPicker/res/values-hu/strings.xml
+++ b/packages/SoundPicker/res/values-hu/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Törlés"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Nem sikerült hozzáadni az egyéni csengőhangot"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Nem sikerült törölni az egyéni csengőhangot"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Hangok"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-hy/strings.xml b/packages/SoundPicker/res/values-hy/strings.xml
index f938c544d878..da8934f72265 100644
--- a/packages/SoundPicker/res/values-hy/strings.xml
+++ b/packages/SoundPicker/res/values-hy/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Ջնջել"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Հնարավոր չէ հատուկ զանգերանգ ավելացնել"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Հնարավոր չէ ջնջել հատուկ զանգերանգը"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Ձայներ"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-in/strings.xml b/packages/SoundPicker/res/values-in/strings.xml
index 98e974aa16d5..86dce643de37 100644
--- a/packages/SoundPicker/res/values-in/strings.xml
+++ b/packages/SoundPicker/res/values-in/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Hapus"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Tidak dapat menambahkan nada dering khusus"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Tidak dapat menghapus nada dering khusus"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-is/strings.xml b/packages/SoundPicker/res/values-is/strings.xml
index 3971e7030c46..d0fce78ff9d9 100644
--- a/packages/SoundPicker/res/values-is/strings.xml
+++ b/packages/SoundPicker/res/values-is/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Eyða"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Get ekki bætt sérsniðnum hringitóni við"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Get ekki eytt sérsniðnum hringitóni"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Hljóð"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-it/strings.xml b/packages/SoundPicker/res/values-it/strings.xml
index e080a7557ac5..20965d0cb152 100644
--- a/packages/SoundPicker/res/values-it/strings.xml
+++ b/packages/SoundPicker/res/values-it/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Elimina"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Impossibile aggiungere suoneria personalizzata"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Impossibile eliminare suoneria personalizzata"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-iw/strings.xml b/packages/SoundPicker/res/values-iw/strings.xml
index 23dbf485971b..dfdb36453590 100644
--- a/packages/SoundPicker/res/values-iw/strings.xml
+++ b/packages/SoundPicker/res/values-iw/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"מחיקה"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"לא ניתן להוסיף רינגטון מותאם אישית"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"לא ניתן למחוק רינגטון מותאם אישית"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"צלילים"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ja/strings.xml b/packages/SoundPicker/res/values-ja/strings.xml
index 43895c2c12c0..7c2aec60df75 100644
--- a/packages/SoundPicker/res/values-ja/strings.xml
+++ b/packages/SoundPicker/res/values-ja/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"削除"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"カスタム着信音を追加できません"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"カスタム着信音を削除できません"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"サウンド"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ka/strings.xml b/packages/SoundPicker/res/values-ka/strings.xml
index 5be1d954650a..1cfe2401a99b 100644
--- a/packages/SoundPicker/res/values-ka/strings.xml
+++ b/packages/SoundPicker/res/values-ka/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"წაშლა"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"მორგებული ზარის დამატება შეუძლებელია"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"მორგებული ზარის წაშლა შეუძლებელია"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ხმები"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-kk/strings.xml b/packages/SoundPicker/res/values-kk/strings.xml
index 59c787dbe4d4..810192f92ef9 100644
--- a/packages/SoundPicker/res/values-kk/strings.xml
+++ b/packages/SoundPicker/res/values-kk/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Жою"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Арнаулы рингтонды енгізу мүмкін емес"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Арнаулы рингтонды жою мүмкін емес"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Дыбыстар"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-km/strings.xml b/packages/SoundPicker/res/values-km/strings.xml
index 7f8720076f58..a334429e5f1f 100644
--- a/packages/SoundPicker/res/values-km/strings.xml
+++ b/packages/SoundPicker/res/values-km/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"លុប"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"មិន​អាច​បន្ថែម​សំឡេង​រោទ៍​ផ្ទាល់ខ្លួន​បាន"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"មិន​អាច​លុប​សំឡេង​រោទ៍​ផ្ទាល់ខ្លួន​បាន​ទេ"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"សំឡេង"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-kn/strings.xml b/packages/SoundPicker/res/values-kn/strings.xml
index 7786039eb84f..e6a05c2b4e2d 100644
--- a/packages/SoundPicker/res/values-kn/strings.xml
+++ b/packages/SoundPicker/res/values-kn/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"ಅಳಿಸಿ"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"ಕಸ್ಟಮ್ ರಿಂಗ್‌ಟೋನ್ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"ಕಸ್ಟಮ್ ರಿಂಗ್‌ಟೋನ್ ಅಳಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ಧ್ವನಿಗಳು"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ko/strings.xml b/packages/SoundPicker/res/values-ko/strings.xml
index 41711da2b4a6..70554d6e5a4d 100644
--- a/packages/SoundPicker/res/values-ko/strings.xml
+++ b/packages/SoundPicker/res/values-ko/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"삭제"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"맞춤 벨소리를 추가할 수 없습니다."</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"맞춤 벨소리를 삭제할 수 없습니다."</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"소리"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ky/strings.xml b/packages/SoundPicker/res/values-ky/strings.xml
index 6e8b052b6140..aeec7a8cd522 100644
--- a/packages/SoundPicker/res/values-ky/strings.xml
+++ b/packages/SoundPicker/res/values-ky/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Жок кылуу"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Жеке рингтон кошулбай жатат"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Жеке рингтон жок кылынбай жатат"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Үндөр"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-lo/strings.xml b/packages/SoundPicker/res/values-lo/strings.xml
index 8b9359e389f4..8bcae0df95d2 100644
--- a/packages/SoundPicker/res/values-lo/strings.xml
+++ b/packages/SoundPicker/res/values-lo/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"​ລຶບ"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Unable to add custom ringtone"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Unable to delete custom ringtone"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ສຽງ"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-lt/strings.xml b/packages/SoundPicker/res/values-lt/strings.xml
index 3323ebdc8ce6..c7ea3691a128 100644
--- a/packages/SoundPicker/res/values-lt/strings.xml
+++ b/packages/SoundPicker/res/values-lt/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Ištrinti"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Nepavyksta pridėti tinkinto skambėjimo tono"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Nepavyksta ištrinti tinkinto skambėjimo tono"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Garsai"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-lv/strings.xml b/packages/SoundPicker/res/values-lv/strings.xml
index 5254bc6ec54b..2a2628972e21 100644
--- a/packages/SoundPicker/res/values-lv/strings.xml
+++ b/packages/SoundPicker/res/values-lv/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Dzēst"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Nevar pievienot pielāgotu zvana signālu"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Nevar izdzēst pielāgotu zvana signālu"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Skaņas"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-mk/strings.xml b/packages/SoundPicker/res/values-mk/strings.xml
index 00caeac0be41..545d5ed2f8f2 100644
--- a/packages/SoundPicker/res/values-mk/strings.xml
+++ b/packages/SoundPicker/res/values-mk/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Избриши"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Не може да се додаде приспособена мелодија"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Не може да се избрише приспособена мелодија"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Звуци"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ml/strings.xml b/packages/SoundPicker/res/values-ml/strings.xml
index 8772d775d34a..21da8e86b6c5 100644
--- a/packages/SoundPicker/res/values-ml/strings.xml
+++ b/packages/SoundPicker/res/values-ml/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"ഇല്ലാതാക്കുക"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"ഇഷ്ടാനുസൃത റിംഗ്‌ടോൺ ചേർക്കാനാവില്ല"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"ഇഷ്ടാനുസൃത റിംഗ്‌ടോൺ ഇല്ലാതാക്കാനാവില്ല"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ശബ്‌ദങ്ങൾ"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-mn/strings.xml b/packages/SoundPicker/res/values-mn/strings.xml
index b8ce2388dc7a..15f7d12b42ca 100644
--- a/packages/SoundPicker/res/values-mn/strings.xml
+++ b/packages/SoundPicker/res/values-mn/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Устгах"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Захиалгат хонхны ая нэмэх боломжгүй"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Захиалгат хонхны ая устгах боломжгүй"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Дуу чимээ"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-mr/strings.xml b/packages/SoundPicker/res/values-mr/strings.xml
index 09d0d4c2040e..eb55fc706bad 100644
--- a/packages/SoundPicker/res/values-mr/strings.xml
+++ b/packages/SoundPicker/res/values-mr/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"हटवा"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"कस्टम रिंगटोन जोडण्यात अक्षम"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"कस्टम रिंगटोन हटविण्यात अक्षम"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"आवाज"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ms/strings.xml b/packages/SoundPicker/res/values-ms/strings.xml
index ac5e983eac3c..9d87d72858ea 100644
--- a/packages/SoundPicker/res/values-ms/strings.xml
+++ b/packages/SoundPicker/res/values-ms/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Padam"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Tidak dapat menambah nada dering tersuai"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Tidak dapat memadamkan nada dering tersuai"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Bunyi"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-my/strings.xml b/packages/SoundPicker/res/values-my/strings.xml
index e6f80c994ac2..62163e950475 100644
--- a/packages/SoundPicker/res/values-my/strings.xml
+++ b/packages/SoundPicker/res/values-my/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"ဖျက်ရန်"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"စိတ်ကြိုက်ဖုန်းမြည်သံကို ထည့်သွင်း၍မရပါ"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"စိတ်ကြိုက်ဖုန်းမြည်သံကို ဖျက်၍မရပါ"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"အသံများ"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-nb/strings.xml b/packages/SoundPicker/res/values-nb/strings.xml
index d8d1f201a5ae..e4e259af541d 100644
--- a/packages/SoundPicker/res/values-nb/strings.xml
+++ b/packages/SoundPicker/res/values-nb/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Slett"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Kan ikke legge til egendefinert ringelyd"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Kan ikke slette egendefinert ringelyd"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Lyder"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ne/strings.xml b/packages/SoundPicker/res/values-ne/strings.xml
index 955b9fa4aa16..7dc78935b424 100644
--- a/packages/SoundPicker/res/values-ne/strings.xml
+++ b/packages/SoundPicker/res/values-ne/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"मेट्नुहोस्"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"आफू अनुकूल रिङटोन थप्न सकिएन"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"आफू अनुकूल रिङटोनलाई मेट्न सकिएन"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ध्वनिहरू"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-nl/strings.xml b/packages/SoundPicker/res/values-nl/strings.xml
index 3a68ac1a7287..5b6fb70b2a82 100644
--- a/packages/SoundPicker/res/values-nl/strings.xml
+++ b/packages/SoundPicker/res/values-nl/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Verwijderen"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Toevoegen van aangepaste ringtone is mislukt"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Verwijderen van aangepaste ringtone is mislukt"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Geluiden"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-or/strings.xml b/packages/SoundPicker/res/values-or/strings.xml
index 767eae14163a..f4bf3cd3a3b8 100644
--- a/packages/SoundPicker/res/values-or/strings.xml
+++ b/packages/SoundPicker/res/values-or/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"ଡିଲିଟ୍‌ କରନ୍ତୁ"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"କଷ୍ଟମ୍‍ ରିଙ୍ଗଟୋନ୍‍ ଯୋଡ଼ିପାରିବ ନାହିଁ"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"କଷ୍ଟମ୍‍ ରିଙ୍ଗଟୋନ୍‍ ଡିଲିଟ୍‍ କରିପାରିବ ନାହିଁ"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ସାଉଣ୍ଡ"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-pa/strings.xml b/packages/SoundPicker/res/values-pa/strings.xml
index 145bcd73957b..2653c6442ae7 100644
--- a/packages/SoundPicker/res/values-pa/strings.xml
+++ b/packages/SoundPicker/res/values-pa/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"ਮਿਟਾਓ"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"ਵਿਉਂਤੀ ਰਿੰਗਟੋਨ ਨੂੰ ਸ਼ਾਮਲ ਕਰਨ ਦੇ ਅਯੋਗ"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"ਵਿਉਂਤੀ ਰਿੰਗਟੋਨ ਨੂੰ ਮਿਟਾਉਣ ਦੇ ਅਯੋਗ"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sounds"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-pl/strings.xml b/packages/SoundPicker/res/values-pl/strings.xml
index ba8ee403ef14..1b3b5c420048 100644
--- a/packages/SoundPicker/res/values-pl/strings.xml
+++ b/packages/SoundPicker/res/values-pl/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Usuń"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Nie można dodać dzwonka niestandardowego"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Nie można usunąć dzwonka niestandardowego"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Dźwięki"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-pt-rBR/strings.xml b/packages/SoundPicker/res/values-pt-rBR/strings.xml
index ab1d5da03ba3..7b545e15ccaa 100644
--- a/packages/SoundPicker/res/values-pt-rBR/strings.xml
+++ b/packages/SoundPicker/res/values-pt-rBR/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Excluir"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Não foi possível adicionar o toque personalizado"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Não foi possível excluir o toque personalizado"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sons"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-pt-rPT/strings.xml b/packages/SoundPicker/res/values-pt-rPT/strings.xml
index efb768883bc6..5d742f1d75c7 100644
--- a/packages/SoundPicker/res/values-pt-rPT/strings.xml
+++ b/packages/SoundPicker/res/values-pt-rPT/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Eliminar"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Não foi possível adicionar o toque personalizado"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Não foi possível eliminar o toque personalizado"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sons"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-pt/strings.xml b/packages/SoundPicker/res/values-pt/strings.xml
index ab1d5da03ba3..7b545e15ccaa 100644
--- a/packages/SoundPicker/res/values-pt/strings.xml
+++ b/packages/SoundPicker/res/values-pt/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Excluir"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Não foi possível adicionar o toque personalizado"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Não foi possível excluir o toque personalizado"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sons"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ro/strings.xml b/packages/SoundPicker/res/values-ro/strings.xml
index 337158e392c7..6190f7f8eac3 100644
--- a/packages/SoundPicker/res/values-ro/strings.xml
+++ b/packages/SoundPicker/res/values-ro/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Ștergeți"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Nu se poate adăuga tonul de sonerie personalizat"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Nu se poate șterge tonul de sonerie personalizat"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sunete"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ru/strings.xml b/packages/SoundPicker/res/values-ru/strings.xml
index 058bb6f16d63..0d48ac1e8785 100644
--- a/packages/SoundPicker/res/values-ru/strings.xml
+++ b/packages/SoundPicker/res/values-ru/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Удалить"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Не удалось добавить рингтон"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Не удалось удалить рингтон"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Звуки"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-si/strings.xml b/packages/SoundPicker/res/values-si/strings.xml
index 5df39906a909..1872b6b27f30 100644
--- a/packages/SoundPicker/res/values-si/strings.xml
+++ b/packages/SoundPicker/res/values-si/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"මකන්න"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"අභිරුචි නාද රිද්මය එක් කළ නොහැකිය"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"අභිරුචි නාද රිද්මය මැකිය නොහැකිය"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ශබ්ද"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-sk/strings.xml b/packages/SoundPicker/res/values-sk/strings.xml
index 78b0223fc549..e7d444cb07e5 100644
--- a/packages/SoundPicker/res/values-sk/strings.xml
+++ b/packages/SoundPicker/res/values-sk/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Odstrániť"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Nepodarilo sa pridať vlastný tón zvonenia"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Nepodarilo sa odstrániť vlastný tón zvonenia"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Zvuky"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-sl/strings.xml b/packages/SoundPicker/res/values-sl/strings.xml
index 024cbe895d99..77a2a2cef0e7 100644
--- a/packages/SoundPicker/res/values-sl/strings.xml
+++ b/packages/SoundPicker/res/values-sl/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Izbriši"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Tona zvonjenja po meri ni mogoče dodati"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Tona zvonjenja po meri ni mogoče izbrisati"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Zvoki"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-sq/strings.xml b/packages/SoundPicker/res/values-sq/strings.xml
index cf2667209866..e35dd71c3beb 100644
--- a/packages/SoundPicker/res/values-sq/strings.xml
+++ b/packages/SoundPicker/res/values-sq/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Fshi"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Nuk mund të shtojë ton zileje të personalizuar"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Nuk mund të fshijë ton zileje të personalizuar"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Tingujt"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-sr/strings.xml b/packages/SoundPicker/res/values-sr/strings.xml
index 4b37d614d2c6..bc573f5a2662 100644
--- a/packages/SoundPicker/res/values-sr/strings.xml
+++ b/packages/SoundPicker/res/values-sr/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Избриши"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Додавање прилагођене мелодије звона није успело"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Брисање прилагођене мелодије звона није успело"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Звукови"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-sv/strings.xml b/packages/SoundPicker/res/values-sv/strings.xml
index 4bec7319c721..c1dd1c24d52b 100644
--- a/packages/SoundPicker/res/values-sv/strings.xml
+++ b/packages/SoundPicker/res/values-sv/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Radera"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Det gick inte att lägga till en egen ringsignal"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Det gick inte att radera den egna ringsignalen"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Ljud"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-sw/strings.xml b/packages/SoundPicker/res/values-sw/strings.xml
index 14e8d75613ff..b0234500b64c 100644
--- a/packages/SoundPicker/res/values-sw/strings.xml
+++ b/packages/SoundPicker/res/values-sw/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Futa"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Imeshindwa kuongeza mlio maalum wa simu"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Imeshindwa kufuta mlio maalum wa simu"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sauti"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ta/strings.xml b/packages/SoundPicker/res/values-ta/strings.xml
index a3d2a7aa395a..38e45b705d22 100644
--- a/packages/SoundPicker/res/values-ta/strings.xml
+++ b/packages/SoundPicker/res/values-ta/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"நீக்கு"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"பிரத்தியேக ரிங்டோனைச் சேர்க்க முடியவில்லை"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"பிரத்தியேக ரிங்டோனை நீக்க முடியவில்லை"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ஒலிகள்"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-te/strings.xml b/packages/SoundPicker/res/values-te/strings.xml
index a39527c15b2a..6b8a62e58526 100644
--- a/packages/SoundPicker/res/values-te/strings.xml
+++ b/packages/SoundPicker/res/values-te/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"తొలగించు"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"అనుకూల రింగ్‌టోన్‌ను జోడించలేకపోయింది"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"అనుకూల రింగ్‌టోన్‌ను తొలగించలేకపోయింది"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"ధ్వనులు"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-th/strings.xml b/packages/SoundPicker/res/values-th/strings.xml
index c6b138a1e63e..cc2e43f98024 100644
--- a/packages/SoundPicker/res/values-th/strings.xml
+++ b/packages/SoundPicker/res/values-th/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"ลบ"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"ไม่สามารถเพิ่มเสียงเรียกเข้าที่กำหนดเอง"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"ไม่สามารถลบเสียงเรียกเข้าที่กำหนดเอง"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"เสียง"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-tl/strings.xml b/packages/SoundPicker/res/values-tl/strings.xml
index 01c33b3758c1..c0c17128a53f 100644
--- a/packages/SoundPicker/res/values-tl/strings.xml
+++ b/packages/SoundPicker/res/values-tl/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"I-delete"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Hindi maidagdag ang custom na ringtone"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Hindi ma-delete ang custom na ringtone"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Mga Tunog"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-tr/strings.xml b/packages/SoundPicker/res/values-tr/strings.xml
index 266edbb4a8a7..955c23f068bf 100644
--- a/packages/SoundPicker/res/values-tr/strings.xml
+++ b/packages/SoundPicker/res/values-tr/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Sil"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Özel zil sesi eklenemiyor"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Özel zil sesi silinemiyor"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Sesler"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-uk/strings.xml b/packages/SoundPicker/res/values-uk/strings.xml
index baba766798b7..42dbfb0ec494 100644
--- a/packages/SoundPicker/res/values-uk/strings.xml
+++ b/packages/SoundPicker/res/values-uk/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Видалити"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Не вдалося додати користувацький сигнал дзвінка"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Не вдалося видалити користувацький сигнал дзвінка"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Звуки"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ur/strings.xml b/packages/SoundPicker/res/values-ur/strings.xml
index e32288640e0c..58141d6e8aca 100644
--- a/packages/SoundPicker/res/values-ur/strings.xml
+++ b/packages/SoundPicker/res/values-ur/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"حذف کریں"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"حسب ضرورت رنگ ٹون شامل کرنے سے قاصر ہے"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"حسب ضرورت رنگ ٹون حذف کرنے سے قاصر ہے"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"آوازیں"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-uz/strings.xml b/packages/SoundPicker/res/values-uz/strings.xml
index c93bacf210c9..9018e660d8fc 100644
--- a/packages/SoundPicker/res/values-uz/strings.xml
+++ b/packages/SoundPicker/res/values-uz/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"O‘chirish"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Maxsus rington qo‘shib bo‘lmadi"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Maxsus ringtonni o‘chirib bo‘lmadi"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Tovushlar"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-vi/strings.xml b/packages/SoundPicker/res/values-vi/strings.xml
index a52c9958222e..bf5c33acb214 100644
--- a/packages/SoundPicker/res/values-vi/strings.xml
+++ b/packages/SoundPicker/res/values-vi/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Xóa"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Không thể thêm nhạc chuông tùy chỉnh"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Không thể xóa nhạc chuông tùy chỉnh"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Âm thanh"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-zh-rCN/strings.xml b/packages/SoundPicker/res/values-zh-rCN/strings.xml
index e2f3552ee4bd..864aaae51264 100644
--- a/packages/SoundPicker/res/values-zh-rCN/strings.xml
+++ b/packages/SoundPicker/res/values-zh-rCN/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"删除"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"无法添加自定义铃声"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"无法删除自定义铃声"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"声音"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-zh-rHK/strings.xml b/packages/SoundPicker/res/values-zh-rHK/strings.xml
index 1a628aa10491..4cde32d77a7b 100644
--- a/packages/SoundPicker/res/values-zh-rHK/strings.xml
+++ b/packages/SoundPicker/res/values-zh-rHK/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"刪除"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"無法加入自訂鈴聲"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"無法刪除自訂鈴聲"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"音效"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-zh-rTW/strings.xml b/packages/SoundPicker/res/values-zh-rTW/strings.xml
index b8a91e7a3257..df8a66ac382e 100644
--- a/packages/SoundPicker/res/values-zh-rTW/strings.xml
+++ b/packages/SoundPicker/res/values-zh-rTW/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"刪除"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"無法新增自訂鈴聲"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"無法刪除自訂鈴聲"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"音效"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-zu/strings.xml b/packages/SoundPicker/res/values-zu/strings.xml
index c66da42fe0a1..29a8ffe846b2 100644
--- a/packages/SoundPicker/res/values-zu/strings.xml
+++ b/packages/SoundPicker/res/values-zu/strings.xml
@@ -25,6 +25,5 @@
<string name="delete_ringtone_text" msgid="201443984070732499">"Susa"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Ayikwazi ukwengeza ithoni yokukhala yangokwezifiso"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Ayikwazi ukususa ithoni yokukhala yangokwezifiso"</string>
- <!-- no translation found for app_label (3091611356093417332) -->
- <skip />
+ <string name="app_label" msgid="3091611356093417332">"Imisindo"</string>
</resources>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index d2f168eb5e3e..d69f3d620d48 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -333,6 +333,9 @@ public final class RingtonePickerActivity extends AlertActivity implements
@Override
public void onDestroy() {
+ if (mHandler != null) {
+ mHandler.removeCallbacksAndMessages(null);
+ }
if (mCursor != null) {
mCursor.close();
mCursor = null;
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7a27676237a1..2fbd9ba05817 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -45,7 +45,7 @@ android_library {
"WindowManager-Shell",
"SystemUIPluginLib",
"SystemUISharedLib",
- "SystemUI-statsd",
+ "SystemUI-statsd",
"SettingsLib",
"androidx.viewpager2_viewpager2",
"androidx.legacy_legacy-support-v4",
@@ -68,14 +68,14 @@ android_library {
"iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
- "dagger2-2.19",
+ "dagger2",
"jsr330"
],
manifest: "AndroidManifest.xml",
kotlincflags: ["-Xjvm-default=enable"],
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
}
filegroup {
@@ -139,8 +139,9 @@ android_library {
"mockito-target-extended-minus-junit4",
"testables",
"truth-prebuilt",
- "dagger2-2.19",
- "jsr330"
+ "dagger2",
+ "jsr330",
+ "WindowManager-Shell",
],
libs: [
"android.test.runner",
@@ -151,7 +152,7 @@ android_library {
"--extra-packages",
"com.android.systemui",
],
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
}
android_app {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 84181c912383..98d3553287d1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -239,6 +239,7 @@
<!-- Listen app op changes -->
<uses-permission android:name="android.permission.WATCH_APPOPS" />
+ <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<!-- to read and change hvac values in a car -->
<uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
@@ -329,6 +330,7 @@
<activity android:name=".screenrecord.ScreenRecordDialog"
android:theme="@style/ScreenRecord"
+ android:showForAllUsers="true"
android:excludeFromRecents="true" />
<service android:name=".screenrecord.RecordingService" />
@@ -394,19 +396,15 @@
<!-- Springboard for launching the share and edit activity. This needs to be in the main
system ui process since we need to notify the status bar to dismiss the keyguard -->
- <receiver android:name=".screenshot.GlobalScreenshot$ActionProxyReceiver"
- android:exported="false" />
-
- <!-- Callback for dismissing screenshot notification after a share target is picked -->
- <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
+ <receiver android:name=".screenshot.ActionProxyReceiver"
android:exported="false" />
<!-- Callback for deleting screenshot notification -->
- <receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
+ <receiver android:name=".screenshot.DeleteScreenshotReceiver"
android:exported="false" />
<!-- Callback for invoking a smart action from the screenshot notification. -->
- <receiver android:name=".screenshot.GlobalScreenshot$SmartActionsReceiver"
+ <receiver android:name=".screenshot.SmartActionsReceiver"
android:exported="false"/>
<!-- started from UsbDeviceSettingsManager -->
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index c440fba10135..89170139e21c 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -41,8 +41,6 @@ public interface SystemUIRootComponent {
The root component is composed of root modules, which in turn provide the global singleton
dependencies across all of SystemUI.
-- `ContextHolder` is just a wrapper that provides a context.
-
- `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI
variants (like other form factors e.g. Car).
@@ -52,41 +50,8 @@ variants (like other form factors e.g. Car).
### Adding injection to a new SystemUI object
-Anything that depends on any `@Singleton` provider from SystemUIRootComponent
-should be declared as a `@Subcomponent` of the root component. This requires
-declaring your own interface for generating your own modules or just the
-object you need injected. The subcomponent also needs to be added to
-SystemUIRootComponent in SystemUIFactory so it can be acquired.
-
-```java
-public interface SystemUIRootComponent {
-+ @Singleton
-+ Dependency.DependencyInjector createDependency();
-}
-
-public class Dependency extends SystemUI {
- //...
-+ @Subcomponent
-+ public interface DependencyInjector {
-+ Dependency createSystemUI();
-+ }
-}
-```
-
-For objects which extend SystemUI and require injection, you can define an
-injector that creates the injected object for you. This other class should
-be referenced in [@string/config_systemUIServiceComponents](packages/SystemUI/res/values/config.xml).
-
-```java
-public static class DependencyCreator implements Injector {
- @Override
- public SystemUI apply(Context context) {
- return SystemUIFactory.getInstance().getRootComponent()
- .createDependency()
- .createSystemUI();
- }
-}
-```
+SystemUI object are made injectable by adding an entry in `SystemUIBinder`. SystemUIApplication uses
+information in that file to locate and construct an instance of the requested SystemUI class.
### Adding a new injectable object
@@ -147,7 +112,7 @@ whenever your fragment needs to be created.
```java
public interface FragmentCreator {
-+ NavigationBarFragment createNavigationBar();
+ NavigationBarFragment createNavigationBar();
}
```
@@ -160,57 +125,45 @@ FragmentHostManager.get(view).create(NavigationBarFragment.class);
### Using injection with Views
-Generally, you shouldn't need to inject for a view, as the view should
-be relatively self contained and logic that requires injection should be
-moved to a higher level construct such as a Fragment or a top-level SystemUI
-component, see above for how to do injection for both of which.
+DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED.
-Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a
-lot of code that could benefit from injection and will need to be migrated
-off from Dependency#get uses. Similar to how fragments are injected, the view
-needs to be added to the interface
-com.android.systemui.util.InjectionInflationController$ViewInstanceCreator.
+Needing to inject objects into your View's constructor generally implies you
+are doing more work in your presentation layer than is advisable.
+Instead, create an injected controller for you view, inject into the
+controller, and then attach the view to the controller after inflation.
-```java
-public interface ViewInstanceCreator {
-+ QuickStatusBarHeader createQsHeader();
-}
-```
+View injection generally causes headaches while testing, as inflating a view
+(which may in turn inflate other views) implicitly causes a Dagger graph to
+be stood up, which may or may not contain the appropriately
+faked/mocked/stubbed objects. It is a hard to control process.
-Presumably you need to inflate that view from XML (otherwise why do you
-need anything special? see earlier sections about generic injection). To obtain
-an inflater that supports injected objects, call InjectionInflationController#injectable,
-which will wrap the inflater it is passed in one that can create injected
-objects when needed.
-
-```java
-@Override
-public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
- Bundle savedInstanceState) {
- return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false);
-}
-```
-
-There is one other important thing to note about injecting with views. SysUI
-already has a Context in its global dagger component, so if you simply inject
-a Context, you will not get the one that the view should have with proper
-theming. Because of this, always ensure to tag views that have @Inject with
-the @Named view context.
+## Updating Dagger2
-```java
-public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs,
- OtherCustomDependency something) {
- //...
-}
-```
+We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates
+when that repository is updated.
-## Updating Dagger2
+*Deprecated:*
Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded
into
[/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/)
+The following commands should work, substituting in the version that you are looking for:
+
+````
+cd prebuilts/tools/common/m2/repository/com/google/dagger/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger/2.28.1/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-compiler/2.28.1/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-spi/2.28.1/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-producers/2.28.1/
+````
+Then update `prebuilts/tools/common/m2/Android.bp` to point at your new jars.
+
## TODO List
- Eliminate usages of Dependency#get
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 2a2ba1b0ccaa..14097b12e730 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -39,4 +39,6 @@
-keep public class * extends com.android.systemui.SystemUI {
public <init>(android.content.Context);
-} \ No newline at end of file
+}
+
+-keep class com.android.wm.shell.* \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
new file mode 100644
index 000000000000..cc2089f69287
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/wallpaperTextColorSecondary">
+ <item android:id="@android:id/background">
+ <shape
+ android:color="@android:color/transparent">
+ <stroke android:width="1dp" android:color="?attr/wallpaperTextColorSecondary"/>
+ <corners android:radius="24dp"/>
+ </shape>
+ </item>
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="?attr/wallpaperTextColorSecondary"/>
+ <corners android:radius="24dp"/>
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
index 3018a022e763..370576b43463 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
@@ -33,6 +33,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Keyguard.TextView"
+ android:layout_marginBottom="8dp"
android:singleLine="true"
android:ellipsize="marquee"
android:visibility="gone"
@@ -42,11 +43,9 @@
<com.android.keyguard.EmergencyButton
android:id="@+id/emergency_call_button"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginTop="@dimen/eca_overlap"
+ android:layout_height="32dp"
+ android:layout_marginBottom="12dp"
android:text="@*android:string/lockscreen_emergency_call"
- style="@style/Keyguard.TextView.EmergencyButton"
- android:textAllCaps="@bool/kg_use_all_caps" />
+ style="@style/Keyguard.TextView.EmergencyButton" />
</com.android.keyguard.EmergencyCarrierArea>
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 8a1f6de432f8..92dd9fd96111 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Geen diens nie."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Wissel invoermetode"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Vliegtuigmodus"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN word vereis om vir opdatering voor te berei"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Patroon word vereis om vir opdatering voor te berei"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Wagwoord word vereis om vir opdatering voor te berei"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Patroon word vereis nadat toestel herbegin het"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN word vereis nadat toestel herbegin het"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Wagwoord word vereis nadat toestel herbegin het"</string>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 0a4aee53b7be..f94c20f9ad01 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ከአገልግሎት መስጫ ክልል ውጪ።"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"የግቤት ስልት ቀይር"</string>
<string name="airplane_mode" msgid="2528005343938497866">"የአውሮፕላን ሁነታ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ለዝማኔ ለማዘጋጀት ፒን ያስፈልጋል"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ለዝማኔ ለማዘጋጀት ሥርዓተ ጥለት ያስፈልጋል"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ለዝማኔ ለማዘጋጀት የይለፍ ቃል ያስፈልጋል"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"መሣሪያ ዳግም ከጀመረ በኋላ ሥርዓተ ጥለት ያስፈልጋል"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"መሣሪያ ዳግም ከተነሳ በኋላ ፒን ያስፈልጋል"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"መሣሪያ ዳግም ከጀመረ በኋላ የይለፍ ቃል ያስፈልጋል"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 491dc39aa08d..6d86a78360d8 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -113,9 +113,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"لا تتوفر خدمة."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تبديل أسلوب الإدخال"</string>
<string name="airplane_mode" msgid="2528005343938497866">"وضع الطائرة"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"يجب إدخال رقم التعريف الشخصي للتحضير للتحديث."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"يجب رسم النقش للتحضير للتحديث."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"يجب إدخال كلمة المرور للتحضير للتحديث."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"يجب رسم النقش بعد إعادة تشغيل الجهاز"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"يجب إدخال رقم التعريف الشخصي بعد إعادة تشغيل الجهاز"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"يجب إدخال كلمة المرور بعد إعادة تشغيل الجهاز"</string>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index 4367efb8c679..3b51e480b7dd 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"কোনো সেৱা নাই।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ইনপুট পদ্ধতি সলনি কৰক"</string>
<string name="airplane_mode" msgid="2528005343938497866">"এয়াৰপ্লেন ম\'ড"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"আপডে\'টৰ বাবে সাজু হ\'বলৈ পিনৰ আৱশ্যক"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"আপডে\'টৰ বাবে সাজু হ\'বলৈ আর্হিৰ আৱশ্যক"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"আপডে\'টৰ বাবে সাজু হ\'বলৈ পাছৱৰ্ডৰ আৱশ্যক"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পিছত আৰ্হি দিয়াটো বাধ্যতামূলক"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পিছত পিন দিয়াটো বাধ্যতামূলক"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পিছত পাছৱৰ্ড দিয়াটো বাধ্যতামূলক"</string>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index d89bf6a8c0ae..ea07c3db4354 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Xidmət yoxdur."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Daxiletmə metoduna keçin"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Təyyarə rejimi"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Güncəlləməyə hazırlıq üçün PIN kod tələb olunur"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Güncəlləməyə hazırlıq üçün model tələb olunur"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Güncəlləməyə hazırlıq üçün parol tələb olunur"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Cihaz yenidən başladıqdan sonra model tələb olunur"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Cihaz yeniden başladıqdan sonra PIN tələb olunur"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Cihaz yeniden başladıqdan sonra parol tələb olunur"</string>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index 656e32301153..e206958d1e95 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Mreža nije dostupna."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promeni metod unosa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Režim rada u avionu"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN je obavezan radi pripreme za ažuriranje"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Šablon je obavezan radi pripreme za ažuriranje"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Lozinka je obavezna radi pripreme za ažuriranje"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Treba da unesete šablon kada se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Treba da unesete PIN kada se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Treba da unesete lozinku kada se uređaj ponovo pokrene"</string>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index 07b6f358dcfc..569e705fbcc2 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Не абслугоўваецца."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Пераключэнне рэжыму ўводу"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Рэжым палёту"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Для падрыхтоўкі да абнаўлення неабходна ўвесці PIN-код"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Для падрыхтоўкі да абнаўлення неабходна ўвесці ўзор разблакіроўкі"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Для падрыхтоўкі да абнаўлення неабходна ўвесці пароль"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Пасля перазапуску прылады патрабуецца ўзор"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Пасля перазапуску прылады патрабуецца PIN-код"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Пасля перазапуску прылады патрабуецца пароль"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index a8c64f5d9551..d015be320e20 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Няма покритие."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Превключване на метода на въвеждане"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Самолетен режим"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"За подготовката за актуализация се изисква ПИН код"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"За подготовката за актуализация се изисква фигура"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"За подготовката за актуализация се изисква парола"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"След рестартиране на устройството се изисква фигура"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"След рестартиране на устройството се изисква ПИН код"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"След рестартиране на устройството се изисква парола"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 479e83ab3954..8eae6e6e2e18 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"কোনো পরিষেবা নেই।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ইনপুট পদ্ধতি পরিবর্তন করুন"</string>
<string name="airplane_mode" msgid="2528005343938497866">"বিমান মোড"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"আপডেট প্রস্তুত করতে পিন দরকার"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"আপডেট প্রস্তুত করতে প্যাটার্ন দরকার"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"আপডেট প্রস্তুত করতে পাসওয়ার্ড দরকার"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ডিভাইসটি পুনরায় চালু হওয়ার পর প্যাটার্নের প্রয়োজন হবে"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ডিভাইসটি পুনরায় চালু হওয়ার পর পিন প্রয়োজন হবে"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ডিভাইসটি পুনরায় চালু হওয়ার পর পাসওয়ার্ডের প্রয়োজন হবে"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index ada4c134fce7..286b08be1c5e 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nema mreže."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promjena načina unosa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Način rada u avionu"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Za pripremu ažuriranja potreban je PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Za pripremu ažuriranja potreban je uzorak"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Za pripremu ažuriranja potrebna je lozinka"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Potreban je uzorak nakon što se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Potreban je PIN nakon što se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Potrebna je lozinka nakon što se uređaj ponovo pokrene"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 6f5b6829c3e0..cb7fa37b281d 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sense servei"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Canvia el mètode d\'introducció"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mode d\'avió"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Cal introduir el PIN per preparar l\'actualització"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Cal introduir el patró per preparar l\'actualització"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Cal introduir la contrasenya per preparar l\'actualització"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Cal introduir el patró quan es reinicia el dispositiu"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Cal introduir el PIN quan es reinicia el dispositiu"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Cal introduir la contrasenya quan es reinicia el dispositiu"</string>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index a2f79adff1db..4f0c0ffa4962 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Žádný signál"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Přepnout metodu zadávání"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Režim Letadlo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Příprava na aktualizaci vyžaduje PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Příprava na aktualizaci vyžaduje gesto"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Příprava na aktualizaci vyžaduje heslo"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po restartování zařízení je vyžadováno gesto"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po restartování zařízení je vyžadován kód PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po restartování zařízení je vyžadováno heslo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index ef06269b5630..e486fc625699 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ingen dækning."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Skift indtastningsmetode"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flytilstand"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Du skal angive din pinkode for at forberede opdateringen"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Du skal angive dit mønster for at forberede opdateringen"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Du skal angive din adgangskode for at forberede opdateringen"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Du skal angive et mønster, når du har genstartet enheden"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Der skal angives en pinkode efter genstart af enheden"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Der skal angives en adgangskode efter genstart af enheden"</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index fdfce1ffb338..06d012f4e84d 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Dienst nicht verfügbar"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Eingabemethode wechseln"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flugmodus"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Zur Vorbereitung auf das Update ist eine PIN erforderlich"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Zur Vorbereitung auf das Update ist ein Muster erforderlich"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Zur Vorbereitung auf das Update ist ein Passwort erforderlich"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Nach dem Neustart des Geräts ist die Eingabe des Musters erforderlich"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Nach dem Neustart des Geräts ist die Eingabe der PIN erforderlich"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Nach dem Neustart des Geräts ist die Eingabe des Passworts erforderlich"</string>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 8e4578f8445c..176428421476 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Καμία υπηρεσία."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Εναλλαγή μεθόδου εισαγωγής"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Λειτουργία πτήσης"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Απαιτείται PIN για την προετοιμασία για ενημέρωση"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Απαιτείται μοτίβο για την προετοιμασία για ενημέρωση"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Απαιτείται κωδικός πρόσβασης για την προετοιμασία για ενημέρωση"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Απαιτείται μοτίβο μετά από την επανεκκίνηση της συσκευής"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Απαιτείται PIN μετά από την επανεκκίνηση της συσκευής"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Απαιτείται κωδικός πρόσβασης μετά από την επανεκκίνηση της συσκευής"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index 21cfe48689ae..92a15949a8ab 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"No service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Aeroplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pattern required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password required to prepare for update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 921ba6b59af9..719f1a18a744 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"No service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Airplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pattern required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password required to prepare for update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index 21cfe48689ae..92a15949a8ab 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"No service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Aeroplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pattern required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password required to prepare for update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index 21cfe48689ae..92a15949a8ab 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"No service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Aeroplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pattern required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password required to prepare for update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index fc59d0dfcdc5..975b1f644ef8 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎No service.‎‏‎‎‏‎"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎Switch input method‎‏‎‎‏‎"</string>
<string name="airplane_mode" msgid="2528005343938497866">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎Airplane mode‎‏‎‎‏‎"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎PIN required to prepare for update‎‏‎‎‏‎"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‎‏‎‏‏‏‏‏‎Pattern required to prepare for update‎‏‎‎‏‎"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎Password required to prepare for update‎‏‎‎‏‎"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎Pattern required after device restarts‎‏‎‎‏‎"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‎PIN required after device restarts‎‏‎‎‏‎"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎Password required after device restarts‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index ab0c8f351562..25ab6159998e 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sin servicio"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambiar método de entrada"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo de avión"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Se requiere el PIN para actualizar el sistema"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Se requiere el patrón para actualizar el sistema"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Se requiere la contraseña para actualizar el sistema"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Se requiere el patrón después de reiniciar el dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Se requiere el PIN después de reiniciar el dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Se requiere la contraseña después de reiniciar el dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index 38451c7cfba5..0754681215cc 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sin servicio"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambiar método de introducción"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo avión"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Debes introducir el PIN para prepararte para la actualización"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Debes dibujar el patrón para prepararte para la actualización"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Debes introducir la contraseña para prepararte para la actualización"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Debes introducir el patrón después de reiniciar el dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Debes introducir el PIN después de reiniciar el dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Debes introducir la contraseña después de reiniciar el dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index f8ad18bba2f7..331a95c73c5e 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Teenus puudub."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Vaheta sisestusmeetodit"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Lennukirežiim"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Värskendamiseks ettevalmistuste tegemiseks tuleb sisestada PIN-kood"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Värskendamiseks ettevalmistuste tegemiseks tuleb sisestada muster"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Värskendamiseks ettevalmistuste tegemiseks tuleb sisestada parool"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pärast seadme taaskäivitamist tuleb sisestada muster"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pärast seadme taaskäivitamist tuleb sisestada PIN-kood"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pärast seadme taaskäivitamist tuleb sisestada parool"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 8510bee0711b..3ff224b9e55a 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ez dago konektatuta inongo saretara."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Aldatu idazketa-metodoa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Hegaldi modua"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN kodea behar da eguneratzea prestatzeko"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Eredua behar da eguneratzea prestatzeko"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Pasahitza behar da eguneratzea prestatzeko"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Eredua marraztu beharko duzu gailua berrabiarazten denean"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN kodea idatzi beharko duzu gailua berrabiarazten denean"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pasahitza idatzi beharko duzu gailua berrabiarazten denean"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 43d32140b153..5e696369634e 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"سرویسی وجود ندارد."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تغییر روش ورودی"</string>
<string name="airplane_mode" msgid="2528005343938497866">"حالت هواپیما"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"آماده‌سازی برای به‌روزرسانی به پین نیاز دارد"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"آماده‌سازی برای به‌روزرسانی به الگو نیاز دارد"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"آماده‌سازی برای به‌روزرسانی به گذرواژه نیاز دارد"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"بعد از بازنشانی دستگاه باید الگو وارد شود"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"بعد از بازنشانی دستگاه باید پین وارد شود"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"بعد از بازنشانی دستگاه باید گذرواژه وارد شود"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 7dc12c93e45e..54bc4d8cba58 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ei yhteyttä"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Vaihda syöttötapaa."</string>
<string name="airplane_mode" msgid="2528005343938497866">"Lentokonetila"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Päivitykseen valmistautuminen edellyttää PIN-koodia"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Päivitykseen valmistautuminen edellyttää kuviota"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Päivitykseen valmistautuminen edellyttää salasanaa"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Kuvio vaaditaan laitteen uudelleenkäynnistyksen jälkeen."</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN-koodi vaaditaan laitteen uudelleenkäynnistyksen jälkeen."</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Salasana vaaditaan laitteen uudelleenkäynnistyksen jälkeen."</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index f093c1745e76..3e858c2ecf12 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Aucun service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Changer de méthode d\'entrée"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mode Avion"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Le NIP est nécessaire pour préparer la mise à jour"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Le schéma est nécessaire pour préparer la mise à jour"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Le mot de passe est nécessaire pour préparer la mise à jour"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Le schéma est exigé après le redémarrage de l\'appareil"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Le NIP est exigé après le redémarrage de l\'appareil"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Le mot de passe est exigé après le redémarrage de l\'appareil"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index d6d5a32874c2..8551fab7281f 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Aucun service."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Changer le mode de saisie"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mode Avion"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Veuillez saisir le code pour lancer la préparation de la mise à jour"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Veuillez saisir le schéma pour lancer la préparation de la mise à jour"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Veuillez saisir le mot de passe pour lancer la préparation de la mise à jour"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Veuillez dessiner le schéma après le redémarrage de l\'appareil"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Veuillez saisir le code après le redémarrage de l\'appareil"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Veuillez saisir le mot de passe après le redémarrage de l\'appareil"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index f5d5bb4d13d4..46079810aee4 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Non hai servizo."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambia o método de introdución"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo avión"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Necesítase o PIN para preparar o dispositivo co fin de actualizalo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Necesítase o padrón para preparar o dispositivo co fin de actualizalo"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Necesítase o contrasinal para preparar o dispositivo co fin de actualizalo"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"É necesario o padrón despois do reinicio do dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"É necesario o PIN despois do reinicio do dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"É necesario o contrasinal despois do reinicio do dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 29e2fe040082..b02d3d97b39f 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"કોઈ સેવા નથી."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ઇનપુટ પદ્ધતિ સ્વિચ કરો"</string>
<string name="airplane_mode" msgid="2528005343938497866">"એરપ્લેન મોડ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"અપડેટ માટે તૈયાર કરવા માટે પિન જરુરી છે"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"અપડેટ માટે તૈયાર કરવા માટે પૅટર્ન જરુરી છે"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"અપડેટ માટે તૈયાર કરવા માટે પાસવર્ડ જરુરી છે"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ઉપકરણનો પુનઃપ્રારંભ થાય તે પછી પૅટર્ન જરૂરી છે"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ઉપકરણનો પુનઃપ્રારંભ થાય તે પછી પિન જરૂરી છે"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ઉપકરણનો પુનઃપ્રારંભ થાય તે પછી પાસવર્ડ જરૂરી છે"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index d26c79f9c420..f6b15de0e97e 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"कोई सेवा नहीं."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"इनपुट का तरीका बदलें"</string>
<string name="airplane_mode" msgid="2528005343938497866">"हवाई जहाज़ मोड"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"अपडेट के लिए पिन डालना ज़रूरी है"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"अपडेट के लिए पैटर्न डालना ज़रूरी है"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"अपडेट के लिए पासवर्ड डालना ज़रूरी है"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"डिवाइस फिर से चालू होने के बाद पैटर्न ज़रूरी है"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"डिवाइस फिर से चालू होने के बाद पिन ज़रूरी है"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"डिवाइस फिर से चालू होने के बाद पासवर्ड ज़रूरी है"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index c8dd9b0a0932..49db3f88669a 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nema usluge."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promjena načina unosa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Način rada u zrakoplovu"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Za pripremu ažuriranja potreban je PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Za pripremu ažuriranja potreban je uzorak"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Za pripremu ažuriranja potrebna je zaporka"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Nakon ponovnog pokretanja uređaja morate unijeti uzorak"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Nakon ponovnog pokretanja uređaja morate unijeti PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Nakon ponovnog pokretanja uređaja morate unijeti zaporku"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index f0023d20e6bd..c26998f01ff0 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nincs szolgáltatás."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Beviteli módszer váltása"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Repülős üzemmód"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"A frissítésre való felkészüléshez meg kell adni a PIN-kódot"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"A frissítésre való felkészüléshez meg kell adni a mintát"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"A frissítésre való felkészüléshez meg kell adni a jelszót"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Az eszköz újraindítását követően meg kell adni a mintát"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Az eszköz újraindítását követően meg kell adni a PIN-kódot"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Az eszköz újraindítását követően meg kell adni a jelszót"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 42247052014f..ad949d48fe0c 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ծառայությունն անհասանելի է։"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Փոխել ներածման եղանակը"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Ավիառեժիմ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Թարմացմանը պատրաստվելու համար անհրաժեշտ է մուտքագրել PIN-ը"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Թարմացմանը պատրաստվելու համար անհրաժեշտ է մուտքագրել նախշը"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Թարմացմանը պատրաստվելու համար անհրաժեշտ է մուտքագրել գաղտնաբառը"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել նախշը"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել PIN կոդը"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել գաղտնաբառը"</string>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index d62795ba1b14..85b2a4726fa2 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Tidak ada layanan."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Beralih metode masukan"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mode pesawat"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN diwajibkan untuk menyiapkan update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pola diwajibkan untuk menyiapkan update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Sandi diwajibkan untuk menyiapkan update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pola diperlukan setelah perangkat dimulai ulang"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN diperlukan setelah perangkat dimulai ulang"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Sandi diperlukan setelah perangkat dimulai ulang"</string>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 5e3765571c02..e40cdca33034 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ekkert símasamband."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Skipta um innsláttaraðferð"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flugstilling"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Slá þarf inn PIN-númer til að undirbúa uppfærsluna"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Teikna þarf mynstur til að undirbúa uppfærsluna"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Gefa þarf upp aðgangsorð til að undirbúa uppfærsluna"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Mynsturs er krafist þegar tækið er endurræst"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN-númers er krafist þegar tækið er endurræst"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Aðgangsorðs er krafist þegar tækið er endurræst"</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index fe460e38dc0b..e1c9ee8b7d34 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nessun servizio."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambia metodo di immissione"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modalità aereo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN obbligatorio per la preparazione all\'aggiornamento"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Sequenza obbligatoria per la preparazione all\'aggiornamento"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password obbligatoria per la preparazione all\'aggiornamento"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Sequenza obbligatoria dopo il riavvio del dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN obbligatorio dopo il riavvio del dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password obbligatoria dopo il riavvio del dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index 71f3048ba11c..e054f629836e 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"אין שירות."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"החלפת שיטת קלט"</string>
<string name="airplane_mode" msgid="2528005343938497866">"מצב טיסה"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"נדרש קוד אימות להכנת העדכון"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"נדרש קו ביטול נעילה להכנת העדכון"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"נדרשת סיסמה להכנת העדכון"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"יש להזין את קו ביטול הנעילה לאחר הפעלה מחדש של המכשיר"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"יש להזין קוד גישה לאחר הפעלה מחדש של המכשיר"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"יש להזין סיסמה לאחר הפעלה מחדש של המכשיר"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 23ff82c6f2a7..957d78a8b440 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"通信サービスはありません。"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"入力方法の切り替え"</string>
<string name="airplane_mode" msgid="2528005343938497866">"機内モード"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"更新の準備には PIN の入力が必要です"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"更新の準備にはパターンの入力が必要です"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"更新の準備にはパスワードが必要です"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"デバイスの再起動後はパターンの入力が必要となります"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"デバイスの再起動後は PIN の入力が必要となります"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"デバイスの再起動後はパスワードの入力が必要となります"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index 25b9b1b22c1c..d0d15fec7172 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"სერვისი არ არის."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"შეყვანის მეთოდის გადართვა"</string>
<string name="airplane_mode" msgid="2528005343938497866">"თვითმფრინავის რეჟიმი"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"განახლების მოსამზადებლად საჭიროა PIN-კოდი"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"განახლების მოსამზადებლად საჭიროა ნიმუში"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"განახლების მოსამზადებლად საჭიროა პაროლი"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"მოწყობილობის გადატვირთვის შემდეგ საჭიროა ნიმუშის დახატვა"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"მოწყობილობის გადატვირთვის შემდეგ საჭიროა PIN-კოდის შეყვანა"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"მოწყობილობის გადატვირთვის შემდეგ საჭიროა პაროლის შეყვანა"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 456088181d7f..62afd1e45df8 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Қызмет көрсетілмейді."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Енгізу әдісін ауыстыру"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Ұшақ режимі"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Жаңа нұсқа орнатуға дайындау үшін PIN кодын енгізу қажет."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Жаңа нұсқа орнатуға дайындау үшін өрнек енгізу қажет."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Жаңа нұсқа орнатуға дайындау үшін құпия сөз енгізу қажет."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Құрылғы қайта іске қосылғаннан кейін, өрнекті енгізу қажет"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Құрылғы қайта іске қосылғаннан кейін, PIN кодын енгізу қажет"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Құрылғы қайта іске қосылғаннан кейін, құпия сөзді енгізу қажет"</string>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index e5ea9ea105dd..24b5c23a6732 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"គ្មាន​សេវា​ទេ។"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ប្ដូរ​វិធី​បញ្ចូល"</string>
<string name="airplane_mode" msgid="2528005343938497866">"មុខងារ​ពេល​ជិះ​យន្តហោះ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"តម្រូវ​ឱ្យ​មាន​កូដ PIN ដើម្បី​រៀបចំ​ធ្វើបច្ចុប្បន្នភាព"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"តម្រូវ​ឱ្យ​មាន​លំនាំ ដើម្បី​រៀបចំ​ធ្វើបច្ចុប្បន្នភាព"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"តម្រូវ​ឱ្យ​មាន​ពាក្យ​សម្ងាត់ ដើម្បី​រៀបចំ​ធ្វើបច្ចុប្បន្នភាព"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"តម្រូវឲ្យប្រើលំនាំ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"តម្រូវឲ្យបញ្ចូលកូដ PIN បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"តម្រូវឲ្យបញ្ចូលពាក្យសម្ងាត់ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 8173ca047fe3..785ca4338326 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ಸೇವೆ ಇಲ್ಲ."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ಇನ್‌ಪುಟ್‌‌ ವಿಧಾನ ಬದಲಿಸಿ"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ಅಪ್‌ಡೇಟ್‌ಗಾಗಿ ಸಿದ್ಧಗೊಳಿಸಲು, ಪಿನ್‌‌ ಅಗತ್ಯವಿದೆ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ಅಪ್‌ಡೇಟ್‌ಗಾಗಿ ಸಿದ್ಧಗೊಳಿಸಲು, ಪ್ಯಾಟರ್ನ್ ಅಗತ್ಯವಿದೆ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ಅಪ್‌ಡೇಟ್‌ಗಾಗಿ ಸಿದ್ಧಗೊಳಿಸಲು, ಪಾಸ್‌ವರ್ಡ್ ಅಗತ್ಯವಿದೆ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ಸಾಧನ ಮರುಪ್ರಾರಂಭಗೊಂಡ ನಂತರ ಪ್ಯಾಟರ್ನ್ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ಸಾಧನ ಮರುಪ್ರಾರಂಭಗೊಂಡ ನಂತರ ಪಿನ್ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ಸಾಧನ ಮರುಪ್ರಾರಂಭಗೊಂಡ ನಂತರ ಪಾಸ್‌ವರ್ಡ್ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index 606bb5047245..848490ebb9b8 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"서비스 불가"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"입력 방법 전환"</string>
<string name="airplane_mode" msgid="2528005343938497866">"비행기 모드"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"업데이트를 준비하려면 PIN이 필요합니다."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"업데이트를 준비하려면 패턴이 필요합니다."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"업데이트를 준비하려면 비밀번호가 필요합니다."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"기기가 다시 시작되면 패턴이 필요합니다."</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"기기가 다시 시작되면 PIN이 필요합니다."</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"기기가 다시 시작되면 비밀번호가 필요합니다."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 72f95dbfa59f..d868788a3eca 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Интернет жок."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Киргизүү ыкмасын өзгөртүү"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Учак режими"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Жаңыртууга даярдоо үчүн PIN код талап кылынат"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Жаңыртууга даярдоо үчүн графикалык ачкыч талап кылынат"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Жаңыртууга даярдоо үчүн сырсөз талап кылынат"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Түзмөк кайра күйгүзүлгөндөн кийин графикалык ачкычты тартуу талап кылынат"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Түзмөк кайра күйгүзүлгөндөн кийин PIN-кодду киргизүү талап кылынат"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Түзмөк кайра күйгүзүлгөндөн кийин сырсөздү киргизүү талап кылынат"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index 25e36c1b3115..ebaffb13d5b4 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ບໍ່ມີບໍລິການ"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ສະລັບຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ໂໝດໃນຍົນ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ຕ້ອງໃຊ້ PIN ເພື່ອກະກຽມອັບເດດ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ຕ້ອງໃຊ້ຮູບແບບເພື່ອກະກຽມອັບເດດ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ຕ້ອງໃຊ້ລະຫັດຜ່ານເພື່ອກະກຽມອັບເດດ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ຈຳເປັນຕ້ອງມີແບບຮູບປົດລັອກຫຼັງຈາກອຸປະກອນເລີ່ມລະບົບໃໝ່"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ຈຳເປັນຕ້ອງມີ PIN ຫຼັງຈາກອຸປະກອນເລີ່ມລະບົບໃໝ່"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ຈຳເປັນຕ້ອງມີລະຫັດຜ່ານຫຼັງຈາກອຸປະກອນເລີ່ມລະບົບໃໝ່"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 158efe2feabb..4d598f6bfb6d 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nėra paslaugos."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Perjungti įvesties metodą"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Lėktuvo režimas"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Kad būtų pasiruošta atnaujinti, reikia įvesti PIN kodą"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Kad būtų pasiruošta atnaujinti, reikia įvesti atrakinimo piešinį"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Kad būtų pasiruošta atnaujinti, reikia įvesti slaptažodį"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Iš naujo paleidus įrenginį būtinas atrakinimo piešinys"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Iš naujo paleidus įrenginį būtinas PIN kodas"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Iš naujo paleidus įrenginį būtinas slaptažodis"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index c4a4e7f45b38..fad67d536e58 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nav pakalpojuma."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Pārslēgt ievades metodi"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Lidojuma režīms"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Lai sagatavotos atjauninājumam, nepieciešams PIN."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Lai sagatavotos atjauninājumam, nepieciešama kombinācija."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Lai sagatavotos atjauninājumam, nepieciešama parole."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pēc ierīces restartēšanas ir jāievada atbloķēšanas kombinācija."</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pēc ierīces restartēšanas ir jāievada PIN kods."</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pēc ierīces restartēšanas ir jāievada parole."</string>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index de4a83b97021..1397f467e116 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Нема услуга."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Префрли метод за внесување"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Авионски режим"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Потребен е PIN за да се подготви за ажурирање"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Потребна е шема за да се подготви за ажурирање"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Потребна е лозинка за да се подготви за ажурирање"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Потребна е шема по рестартирање на уредот"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Потребен е PIN-код по рестартирање на уредот"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Потребна е лозинка по рестартирање на уредот"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index da26ba788932..f82f822ca26c 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"സേവനമില്ല"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ഇൻപുട്ട് രീതി മാറുക"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ഫ്ലൈറ്റ് മോഡ്"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"അപ്‌ഡേറ്റിനായി തയ്യാറെടുക്കാൻ പിൻ ആവശ്യമാണ്"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"അപ്‌ഡേറ്റിനായി തയ്യാറെടുക്കാൻ പാറ്റേൺ ആവശ്യമാണ്"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"അപ്‌ഡേറ്റിനായി തയ്യാറെടുക്കാൻ പാസ്‌വേഡ് ആവശ്യമാണ്"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ഉപകരണം റീസ്റ്റാർട്ടായശേഷം ‌പാറ്റേൺ വരയ്‌ക്കേണ്ടതുണ്ട്"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ഉപകരണം റീസ്റ്റാർട്ടായശേഷം ‌പിൻ നൽകേണ്ടതുണ്ട്"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ഉപകരണം റീസ്റ്റാർട്ടായശേഷം ‌പാസ്‌വേഡ് നൽകേണ്ടതുണ്ട്"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index fb032f15df7f..462017af1922 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Үйлчилгээ алга."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Оруулах аргыг сэлгэх"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Нислэгийн горим"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Шинэчлэхэд бэлтгэхийн тулд ПИН шаардлагатай"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Шинэчлэхэд бэлтгэхийн тулд хээ шаардлагатай"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Шинэчлэхэд бэлтгэхийн тулд нууц үг шаардлагатай"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Төхөөрөмжийг дахин эхлүүлсний дараа загвар оруулах шаардлагатай"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Төхөөрөмжийг дахин эхлүүлсний дараа ПИН оруулах шаардлагатай"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Төхөөрөмжийг дахин эхлүүлсний дараа нууц үг оруулах шаардлагатай"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index e79e5c5d9234..0166791dc3ba 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"सेवा नाही."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"इनपुट पद्धत स्विच करा"</string>
<string name="airplane_mode" msgid="2528005343938497866">"विमान मोड"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"अपडेटसाठी तयार करण्याकरिता पिन आवश्यक आहे"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"अपडेटसाठी तयार करण्याकरिता पॅटर्न आवश्यक आहे"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"अपडेटसाठी तयार करण्याकरिता पासवर्ड आवश्यक आहे"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"डिव्हाइस रीस्टार्ट झाल्यावर पॅटर्न आवश्यक आहे"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"डिव्हाइस रीस्टार्ट झाल्यावर पिन आवश्यक आहे"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"डिव्हाइस रीस्टार्ट झाल्यावर पासवर्ड आवश्यक आहे"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 5bc5df4d7c26..67500866f3d4 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Tiada perkhidmatan."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Tukar kaedah masukan"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mod Pesawat"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN diperlukan untuk menyediakan kemas kini"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Corak diperlukan untuk menyediakan kemas kini"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Kata laluan diperlukan untuk menyediakan kemas kini"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Corak diperlukan setelah peranti dimulakan semula"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN diperlukan setelah peranti dimulakan semula"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Kata laluan diperlukan setelah peranti dimulakan semula"</string>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 43732d8ff260..3b32f06e19cd 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ဝန်ဆောင်မှု မရှိပါ။"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"စာရိုက်စနစ်ပြောင်းရန်"</string>
<string name="airplane_mode" msgid="2528005343938497866">"လေယာဉ်ပျံမုဒ်"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"အပ်ဒိတ်အတွက် ပြင်ဆင်ရန် ပင်နံပါတ် လိုပါသည်"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"အပ်ဒိတ်အတွက် ပြင်ဆင်ရန် ပုံစံလိုပါသည်"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"အပ်ဒိတ်အတွက် ပြင်ဆင်ရန် စကားဝှက် လိုပါသည်"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"စက်ပစ္စည်းကို ပိတ်ပြီးပြန်ဖွင့်လိုက်သည့်အခါတွင် ပုံစံ လိုအပ်ပါသည်"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"စက်ပစ္စည်းကို ပိတ်ပြီးပြန်ဖွင့်လိုက်သည့်အခါတွင် ပင်နံပါတ် လိုအပ်ပါသည်"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"စက်ပစ္စည်းကို ပိတ်ပြီးပြန်ဖွင့်လိုက်သည့်အခါတွင် စကားဝှက် လိုအပ်ပါသည်"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 6dd3b2ad6e72..ebd8f2922ba2 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ingen tilkobling."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Bytt inndatametode"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flymodus"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN-koden kreves for å klargjøre for oppdateringen"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Mønsteret kreves for å klargjøre for oppdateringen"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Passordet kreves for å klargjøre for oppdateringen"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Du må tegne mønsteret etter at enheten har startet på nytt"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Du må skrive inn PIN-koden etter at enheten har startet på nytt"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Du må skrive inn passordet etter at enheten har startet på nytt"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index a1e6d62234e9..ce05e38dca10 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"सेवा उपलब्ध छैन।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"इनपुट विधिलाई स्विच गर्नुहोस्"</string>
<string name="airplane_mode" msgid="2528005343938497866">"हवाइजहाज मोड"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"अद्यावधिक गर्ने कार्यका लागि तयार पार्न PIN चाहिन्छ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"अद्यावधिक गर्ने कार्यका लागि तयार पार्न प्याटर्न चाहिन्छ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"अद्यावधिक गर्ने कार्यका लागि तयार पार्न पासवर्ड चाहिन्छ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"यन्त्र पुनः सुरु भएपछि ढाँचा आवश्यक पर्दछ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"यन्त्र पुनः सुरु भएपछि PIN आवश्यक पर्दछ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"यन्त्र पुनः सुरु भएपछि पासवर्ड आवश्यक पर्दछ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index f3c35a405bd6..aa783e84b892 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Geen service."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Invoermethode wijzigen"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Vliegtuigmodus"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Pincode vereist voor voorbereiding op update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Patroon vereist voor voorbereiding op update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Wachtwoord vereist voor voorbereiding op update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Patroon vereist nadat het apparaat opnieuw is opgestart"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pincode vereist nadat het apparaat opnieuw is opgestart"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Wachtwoord vereist nadat het apparaat opnieuw is opgestart"</string>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index e92dc42680e1..8bbdcf1e9eb6 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"କୌଣସି ସେବା ନାହିଁ।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ଇନପୁଟ୍‌ ପଦ୍ଧତି ବଦଳାନ୍ତୁ"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ଅପଡେଟ୍ ପାଇଁ ପ୍ରସ୍ତୁତ ହେବାକୁ PIN ଆବଶ୍ୟକ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ଅପଡେଟ୍ ପାଇଁ ପ୍ରସ୍ତୁତ ହେବାକୁ ପାଟର୍ନ ଆବଶ୍ୟକ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ଅପଡେଟ୍ ପାଇଁ ପ୍ରସ୍ତୁତ ହେବାକୁ ପାସୱାର୍ଡ ଆବଶ୍ୟକ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ଡିଭାଇସ୍‍ ରିଷ୍ଟାର୍ଟ ହେବା ପରେ ପାଟର୍ନ ଆବଶ୍ୟକ ଅଟେ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ଡିଭାଇସ୍‍ ରିଷ୍ଟାର୍ଟ ହେବାପରେ ପାସ୍‌ୱର୍ଡ ଆବଶ୍ୟକ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ଡିଭାଇସ୍‍ ରିଷ୍ଟାର୍ଟ ହେବା ପରେ ପାସୱର୍ଡ ଆବଶ୍ୟକ ଅଟେ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 5c83ab8a354c..78e066526840 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ਇਨਪੁੱਟ ਵਿਧੀ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ਅੱਪਡੇਟ ਨੂੰ ਤਿਆਰ ਕਰਨ ਲਈ ਪਿੰਨ ਲੋੜੀਂਦਾ ਹੈ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ਅੱਪਡੇਟ ਨੂੰ ਤਿਆਰ ਕਰਨ ਲਈ ਪੈਟਰਨ ਲੋੜੀਂਦਾ ਹੈ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ਅੱਪਡੇਟ ਨੂੰ ਤਿਆਰ ਕਰਨ ਲਈ ਪਾਸਵਰਡ ਲੋੜੀਂਦਾ ਹੈ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ਡੀਵਾਈਸ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ \'ਤੇ ਪੈਟਰਨ ਦੀ ਲੋੜ ਹੈ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ਡੀਵਾਈਸ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ \'ਤੇ ਪਿੰਨ ਦੀ ਲੋੜ ਹੈ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ਡੀਵਾਈਸ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ \'ਤੇ ਪਾਸਵਰਡ ਦੀ ਲੋੜ ਹੈ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 5f1df3e109b1..5094cf9983a1 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Brak usługi."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Przełączanie metody wprowadzania"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Tryb samolotowy"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Aby przygotować się do aktualizacji, wymagany jest kod PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Aby przygotować się do aktualizacji, wymagany jest wzór"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Aby przygotować się do aktualizacji, wymagane jest hasło"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po ponownym uruchomieniu urządzenia wymagany jest wzór"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po ponownym uruchomieniu urządzenia wymagany jest kod PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po ponownym uruchomieniu urządzenia wymagane jest hasło"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 1e47efa2cc4f..5bfc3dbc900d 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sem serviço."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Alterar o método de entrada"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo avião"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Insira o PIN para se preparar para a atualização"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Insira o padrão para se preparar para a atualização"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Insira a senha para se preparar para a atualização"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"O padrão é exigido após a reinicialização do dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"O PIN é exigido após a reinicialização do dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"A senha é exigida após a reinicialização do dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index 09cfcf1da02f..5af8bc09a05b 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sem serviço."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Alternar o método de introdução"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo de avião"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"É necessário introduzir o PIN para a preparação para a atualização."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"É necessário introduzir o padrão para a preparação para a atualização."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"É necessário introduzir a palavra-passe para a preparação para a atualização."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"É necessário um padrão após reiniciar o dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"É necessário um PIN após reiniciar o dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"É necessária uma palavra-passe após reiniciar o dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 1e47efa2cc4f..5bfc3dbc900d 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sem serviço."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Alterar o método de entrada"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo avião"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Insira o PIN para se preparar para a atualização"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Insira o padrão para se preparar para a atualização"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Insira a senha para se preparar para a atualização"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"O padrão é exigido após a reinicialização do dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"O PIN é exigido após a reinicialização do dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"A senha é exigida após a reinicialização do dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 7df2db6f03f1..8122241e6613 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Fără serviciu."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Comutați metoda de introducere"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mod Avion"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Pentru a vă pregăti pentru actualizare este necesar codul PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pentru a vă pregăti pentru actualizare este necesar modelul"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Pentru a vă pregăti pentru actualizare este necesară parola"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Modelul este necesar după repornirea dispozitivului"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Codul PIN este necesar după repornirea dispozitivului"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Parola este necesară după repornirea dispozitivului"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index ccd3c968cd51..b80b479ca29a 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Нет сигнала."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Сменить способ ввода"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Режим полета"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Для подготовки к обновлению необходимо ввести PIN-код."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Для подготовки к обновлению необходимо ввести графический ключ."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Для подготовки к обновлению необходимо ввести пароль."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"После перезагрузки устройства необходимо ввести графический ключ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"После перезагрузки устройства необходимо ввести PIN-код"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"После перезагрузки устройства необходимо ввести пароль"</string>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 3dfc282ccae5..1cd876f15d47 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"සේවාව නැත."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ආදාන ක්‍රමය මාරු කිරීම"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ගුවන් යානා ප්‍රකාරය"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"යාවත්කාලීනය සඳහා සුදානම් කිරීමට PIN අවශ්‍යය"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"යාවත්කාලීනය සඳහා සුදානම් කිරීමට රටාව අවශ්‍යය"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"යාවත්කාලීනය සඳහා සුදානම් කිරීමට මුරපදය අවශ්‍යය"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"උපාංගය නැවත ආරම්භ වූ පසු රටාව අවශ්‍යයි"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"උපාංගය නැවත ආරම්භ වූ පසු PIN අංකය අවශ්‍යයි"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"උපාංගය නැවත ආරම්භ වූ පසු මුරපදය අවශ්‍යයි"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index 8568e14fecc3..801a7dbccf6d 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Žiadny signál."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Prepnúť metódu vstupu"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Režim v lietadle"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Príprava na aktualizáciu vyžaduje zadanie kódu PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Príprava na aktualizáciu vyžaduje zadanie vzoru"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Príprava na aktualizáciu vyžaduje zadanie hesla"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po reštartovaní zariadenia musíte zadať bezpečnostný vzor"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po reštartovaní zariadenia musíte zadať kód PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po reštartovaní zariadenia musíte zadať heslo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 168158d9d602..967255cb50e7 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ni storitve."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Preklop načina vnosa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Način za letalo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Za pripravo na posodobitev morate vnesti kodo PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Za pripravo na posodobitev morate vnesti vzorec"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Za pripravo na posodobitev morate vnesti geslo"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po vnovičnem zagonu naprave je treba vnesti vzorec"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po vnovičnem zagonu naprave je treba vnesti kodo PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po vnovičnem zagonu naprave je treba vnesti geslo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index 14973f82dc4d..382a4dcafed7 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nuk ka shërbim."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Ndërro metodën e hyrjes"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modaliteti i aeroplanit"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Kërkohet kodi PIN për t\'u përgatitur për përditësimin"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Kërkohet motivi për t\'u përgatitur për përditësimin"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Kërkohet fjalëkalimi për t\'u përgatitur për përditësimin"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Kërkohet motivi pas rinisjes së pajisjes"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Kërkohet kodi PIN pas rinisjes së pajisjes"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Kërkohet fjalëkalimi pas rinisjes së pajisjes"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 24a1125f9a7d..f83df3f8925e 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Мрежа није доступна."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Промени метод уноса"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Режим рада у авиону"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN је обавезан ради припреме за ажурирање"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Шаблон је обавезан ради припреме за ажурирање"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Лозинка је обавезна ради припреме за ажурирање"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Треба да унесете шаблон када се уређај поново покрене"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Треба да унесете PIN када се уређај поново покрене"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Треба да унесете лозинку када се уређај поново покрене"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index a37c4809ccdb..a037bffa4da2 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ingen tjänst."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Byt inmatningsmetod"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flygplansläge"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Pinkod krävs för att förbereda för uppdatering"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Grafiskt lösenord krävs för att förbereda för uppdatering"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Lösenord krävs för att förbereda för uppdatering"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Du måste ange grafiskt lösenord när du har startat om enheten"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Du måste ange pinkod när du har startat om enheten"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Du måste ange lösenord när du har startat om enheten"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index c4a9a1445ceb..efa5ecfd44fd 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Hakuna mtandao."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Kubadili mbinu ya kuingiza data"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Hali ya ndegeni"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Inahitaji PIN ili kujiandaa kwa ajili ya sasisho"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Inahitaji mchoro ili kujiandaa kwa ajili ya sasisho"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Inahitaji nenosiri ili kujiandaa kwa ajili ya sasisho"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Unafaa kuchora mchoro baada ya kuwasha kifaa upya"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Unafaa kuweka PIN baada ya kuwasha kifaa upya"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Unafaa kuweka nenosiri baada ya kuwasha kifaa upya"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index a4dc0be3496f..96dbbb0314d6 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"சேவை இல்லை."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"உள்ளீட்டு முறையை மாற்றும்"</string>
<string name="airplane_mode" msgid="2528005343938497866">"விமானப் பயன்முறை"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"புதுப்பிப்பிற்குத் தயார்செய்ய பின் தேவை"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"புதுப்பிப்பிற்குத் தயார்செய்ய பேட்டர்ன் தேவை"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"புதுப்பிப்பிற்குத் தயார்செய்ய கடவுச்சொல் தேவை"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"சாதனத்தை மீண்டும் தொடங்கியதும், பேட்டர்னை வரைய வேண்டும்"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"சாதனத்தை மீண்டும் தொடங்கியதும், பின்னை உள்ளிட வேண்டும்"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"சாதனத்தை மீண்டும் தொடங்கியதும், கடவுச்சொல்லை உள்ளிட வேண்டும்"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 3e27ce8ba702..74386bc7a487 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"సేవ లేదు."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ఇన్‌పుట్ పద్ధతిని మార్చు"</string>
<string name="airplane_mode" msgid="2528005343938497866">"విమానం మోడ్"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"అప్‌డేట్‌కు సిద్ధం చేయడానికి పిన్ అవసరం"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"అప్‌డేట్‌కు సిద్ధం చేయడానికి ఆకృతి అవసరం"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"అప్‌డేట్‌కు సిద్ధం చేయడానికి పాస్‌వర్డ్ అవసరం"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత నమూనాను గీయాలి"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"డివైజ్‌ను పునఃప్రారంభించిన తర్వాత పిన్ నమోదు చేయాలి"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత పాస్‌వర్డ్‌ను నమోదు చేయాలి"</string>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index 8f94c636d01f..e157be4ac18e 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ไม่มีบริการ"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"สลับวิธีการป้อนข้อมูล"</string>
<string name="airplane_mode" msgid="2528005343938497866">"โหมดบนเครื่องบิน"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ต้องใช้ PIN เพื่อเตรียมรับการอัปเดต"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ต้องใช้รูปแบบเพื่อเตรียมรับการอัปเดต"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ต้องใช้รหัสผ่านเพื่อเตรียมรับการอัปเดต"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ต้องวาดรูปแบบหลังจากอุปกรณ์รีสตาร์ท"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ต้องระบุ PIN หลังจากอุปกรณ์รีสตาร์ท"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ต้องป้อนรหัสผ่านหลังจากอุปกรณ์รีสตาร์ท"</string>
@@ -138,6 +135,6 @@
<item quantity="one">ซิมถูกปิดใช้งานในขณะนี้ โปรดป้อนรหัส PUK เพื่อทำต่อ คุณพยายามได้อีก <xliff:g id="_NUMBER_0">%d</xliff:g> ครั้งก่อนที่ซิมจะไม่สามารถใช้งานได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ</item>
</plurals>
<string name="clock_title_default" msgid="6342735240617459864">"ค่าเริ่มต้น"</string>
- <string name="clock_title_bubble" msgid="2204559396790593213">"ลูกโป่ง"</string>
+ <string name="clock_title_bubble" msgid="2204559396790593213">"บับเบิล"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"แอนะล็อก"</string>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index bd87b20a2ccb..7b7e17dc8dcb 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Walang serbisyo."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Magpalit ng pamamaraan ng pag-input"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Airplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Kinakailangan ang PIN para makapaghanda sa pag-update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Kinakailangan ang pattern para makapaghanda sa pag-update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Kinakailangan ang password para makapaghanda sa pag-update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Kailangan ng pattern pagkatapos mag-restart ng device"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Kailangan ng PIN pagkatapos mag-restart ng device"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Kailangan ng password pagkatapos mag-restart ng device"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index cf37451a9ed8..8c0caead0bdb 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Hizmet yok."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Giriş yöntemini değiştir"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Uçak modu"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Güncellemenin hazırlanması için PIN gerekli"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Güncellemenin hazırlanması için desen gerekli"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Güncellemenin hazırlanması için şifre gerekli"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Cihaz yeniden başladıktan sonra desen gerekir"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Cihaz yeniden başladıktan sonra PIN gerekir"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Cihaz yeniden başladıktan sonra şifre gerekir"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 0ccd012c5221..6e5ce0f142dc 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Зв’язку немає."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Змінити метод введення"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Режим польоту"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Щоб підготуватися до оновлення, введіть PIN-код"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Щоб підготуватися до оновлення, введіть ключ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Щоб підготуватися до оновлення, введіть пароль"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Після перезавантаження пристрою потрібно ввести ключ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Після перезавантаження пристрою потрібно ввести PIN-код"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Після перезавантаження пристрою потрібно ввести пароль"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 22a477e3d63f..0fd5e17c953e 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"کوئی سروس نہیں ہے۔"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"اندراج کا طریقہ سوئچ کریں"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ہوائی جہاز وضع"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"‏اپ ڈیٹ تیار کرنے کے لیے PIN درکار ہے"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"اپ ڈیٹ تیار کرنے کے لیے پیٹرن درکار ہے"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"اپ ڈیٹ تیار کرنے کے لیے پاس ورڈ درکار ہے"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"آلہ دوبارہ چالو ہونے کے بعد پیٹرن درکار ہوتا ہے"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"‏آلہ دوبارہ چالو ہونے کے بعد PIN درکار ہوتا ہے"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"آلہ دوبارہ چالو ہونے کے بعد پاسورڈ درکار ہوتا ہے"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 72d4fae575b8..323fea5a608e 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Aloqa yo‘q."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Matn kiritish usulini almashtirish"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Parvoz rejimi"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Yangilashga tayyorlash uchun PIN kod talab etiladi"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Yangilashga tayyorlash uchun grafik kalit talab etiladi"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Yangilashga tayyorlash uchun parol talab etiladi"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Qurilma o‘chirib yoqilgandan keyin grafik kalit talab qilinadi"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Qurilma o‘chirib yoqilgandan keyin PIN kod talab qilinadi"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Qurilma o‘chirib yoqilgandan keyin parol talab qilinadi"</string>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index de642f3cc27c..2ba5089c7ed9 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Không có dịch vụ."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Chuyển phương thức nhập"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Chế độ trên máy bay"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Cần nhập mã PIN để chuẩn bị cập nhật"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Cần nhập hình mở khóa để chuẩn bị cập nhật"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Cần nhập mật khẩu để chuẩn bị cập nhật"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Yêu cầu hình mở khóa sau khi thiết bị khởi động lại"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Yêu cầu mã PIN sau khi thiết bị khởi động lại"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Yêu cầu mật khẩu sau khi thiết bị khởi động lại"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index be162b0b5790..b4bff5fab6d6 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"无服务。"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"切换输入法"</string>
<string name="airplane_mode" msgid="2528005343938497866">"飞行模式"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"需要输入 PIN 码才能让设备做好更新准备"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"需要绘制解锁图案才能让设备做好更新准备"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"需要输入密码才能让设备做好更新准备"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"重启设备后需要绘制解锁图案"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"重启设备后需要输入 PIN 码"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"重启设备后需要输入密码"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index 33e5b44ac31e..b3d387706fa5 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"沒有服務。"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"轉換輸入方法"</string>
<string name="airplane_mode" msgid="2528005343938497866">"飛行模式"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"必須提供 PIN 碼,才能準備進行更新"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"必須畫出上鎖圖案,才能準備進行更新"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"必須輸入密碼,才能準備進行更新"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"裝置重新啟動後,必須畫出上鎖圖案才能使用"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"裝置重新啟動後,必須輸入 PIN 碼才能使用"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"裝置重新啟動後,必須輸入密碼才能使用"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index 763233cc2e15..03dec4852771 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"沒有服務。"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"切換輸入法"</string>
<string name="airplane_mode" msgid="2528005343938497866">"飛航模式"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"請輸入 PIN 碼,以便為更新作業進行準備"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"請畫出解鎖圖案,以便為更新作業進行準備"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"請輸入密碼,以便為更新作業進行準備"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"裝置重新啟動後需要畫出解鎖圖案"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"裝置重新啟動後需要輸入 PIN 碼"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"裝置重新啟動後需要輸入密碼"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 397c868e6547..5ab567f706c3 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ayikho isevisi"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Shintsha indlela yokufaka"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Imodi yendiza"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Iphinikhodi iyadingeka ukuze kulungiselelwe isibuyekezo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Iphethini iyadingeka ukuze kulungiselelwe isibuyekezo"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Iphasiwedi iyadingeka ukuze kulungiselelwe isibuyekezo"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Iphethini iyadingeka ngemuva kokuqala kabusha kwedivayisi"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Iphinikhodi iyadingeka ngemuva kokuqala kabusha kwedivayisi"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Iphasiwedi iyadingeka ngemuva kokuqala kabusha kwedivayisi"</string>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 4d184d5758d3..f7e9fedd5f66 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -240,15 +240,6 @@
<!-- Description of airplane mode -->
<string name="airplane_mode">Airplane mode</string>
- <!-- An explanation text that the PIN needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_prepare_for_update_pin">PIN required to prepare for update</string>
-
- <!-- An explanation text that the pattern needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_prepare_for_update_pattern">Pattern required to prepare for update</string>
-
- <!-- An explanation text that the password needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_prepare_for_update_password">Password required to prepare for update</string>
-
<!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
<string name="kg_prompt_reason_restart_pattern">Pattern required after device restarts</string>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 5f2a946a1b6d..53eb2343d26a 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -23,10 +23,12 @@
<item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
</style>
- <style name="Keyguard.TextView.EmergencyButton" parent="@android:style/DeviceDefault.ButtonBar">
+ <style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
<item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
- <item name="android:textSize">@dimen/kg_status_line_font_size</item>
- <item name="android:background">@null</item>
+ <item name="android:textSize">14dp</item>
+ <item name="android:background">@drawable/kg_emergency_button_background</item>
+ <item name="android:paddingLeft">12dp</item>
+ <item name="android:paddingRight">12dp</item>
</style>
<style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView">
<item name="android:singleLine">true</item>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_0.xml
index 3304c19b1ed8..ff5cb9ef6b67 100644
--- a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_0.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2017 The Android Open Source Project
+ Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@
android:viewportHeight="28.0">
<!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
<group android:name="icon" android:pivotX="14" android:pivotY="14"
- android:scaleX="?attr/rotateButtonScaleX">
+ android:scaleX="1">
<!-- Tint color to be set directly -->
<path android:fillColor="#FFFFFFFF"
android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
@@ -107,8 +107,8 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="100"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -118,14 +118,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -135,14 +135,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -152,14 +152,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -169,14 +169,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml
new file mode 100644
index 000000000000..90fedb17ecf1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:name="root"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="28.0"
+ android:viewportHeight="28.0">
+ <!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
+ <group android:name="icon" android:pivotX="14" android:pivotY="14"
+ android:scaleX="1">
+ <!-- Tint color to be set directly -->
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
+ </group>
+ </vector>
+ </aapt:attr>
+
+ <!-- Repeat all animations 5 times but don't fade out at the end -->
+ <target android:name="root">
+ <aapt:attr name="android:animation">
+ <set android:ordering="sequentially">
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="icon">
+ <aapt:attr name="android:animation">
+ <set android:ordering="sequentially">
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="100"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="90"
+ android:valueTo="90"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="90"
+ android:valueTo="90"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="90"
+ android:valueTo="90"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="90"
+ android:valueTo="90"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml
new file mode 100644
index 000000000000..a89e7a34ad26
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:name="root"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="28.0"
+ android:viewportHeight="28.0">
+ <!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
+ <group android:name="icon" android:pivotX="14" android:pivotY="14"
+ android:scaleX="-1">
+ <!-- Tint color to be set directly -->
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
+ </group>
+ </vector>
+ </aapt:attr>
+
+ <!-- Repeat all animations 5 times but don't fade out at the end -->
+ <target android:name="root">
+ <aapt:attr name="android:animation">
+ <set android:ordering="sequentially">
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="icon">
+ <aapt:attr name="android:animation">
+ <set android:ordering="sequentially">
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="100"
+ android:duration="600"
+ android:valueFrom="0"
+ android:valueTo="90">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="0"
+ android:valueTo="0"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="0"
+ android:valueTo="90">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="0"
+ android:valueTo="0"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="0"
+ android:valueTo="90">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="0"
+ android:valueTo="0"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="0"
+ android:valueTo="90">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="0"
+ android:valueTo="0"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="0"
+ android:valueTo="90">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml
new file mode 100644
index 000000000000..0dc67b0d22af
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:name="root"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="28.0"
+ android:viewportHeight="28.0">
+ <!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
+ <group android:name="icon" android:pivotX="14" android:pivotY="14"
+ android:scaleX="-1">
+ <!-- Tint color to be set directly -->
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
+ </group>
+ </vector>
+ </aapt:attr>
+
+ <!-- Repeat all animations 5 times but don't fade out at the end -->
+ <target android:name="root">
+ <aapt:attr name="android:animation">
+ <set android:ordering="sequentially">
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ <!-- Linear fade out -->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="1700"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:anim/linear_interpolator"/>
+ <!-- Linear fade in-->
+ <objectAnimator android:propertyName="alpha"
+ android:duration="100"
+ android:startOffset="100"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:anim/linear_interpolator" />
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="icon">
+ <aapt:attr name="android:animation">
+ <set android:ordering="sequentially">
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="100"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="180">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="90"
+ android:valueTo="90"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="180">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="90"
+ android:valueTo="90"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="180">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="90"
+ android:valueTo="90"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="180">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+
+ <!-- Reset rotation position for fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:startOffset="1300"
+ android:duration="100"
+ android:valueFrom="90"
+ android:valueTo="90"/>
+
+ <!-- Icon rotation with start timing offset after fade in -->
+ <objectAnimator android:propertyName="rotation"
+ android:duration="600"
+ android:valueFrom="90"
+ android:valueTo="180">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index 0db1a57a0973..827cf4a9d3b6 100644
--- a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+ Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,5 +15,9 @@
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="#9AFFFFFF" android:radius="17dp" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#242424" /> <!-- 14% of white -->
+ <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
+ android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
+ <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 87dd58e4f0ed..213bb923db65 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.bubbles.BubbleManageEducationView
+<com.android.systemui.bubbles.ManageEducationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -87,4 +87,4 @@
/>
</LinearLayout>
</LinearLayout>
-</com.android.systemui.bubbles.BubbleManageEducationView>
+</com.android.systemui.bubbles.ManageEducationView>
diff --git a/packages/SystemUI/res/layout/global_actions_power_item.xml b/packages/SystemUI/res/layout/global_actions_power_item.xml
index 0d060b63486f..3bf58944423b 100644
--- a/packages/SystemUI/res/layout/global_actions_power_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_power_item.xml
@@ -26,7 +26,7 @@
android:id="@*android:id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
- android:layout_marginBottom="45dp"
+ android:layout_marginBottom="@dimen/global_actions_power_dialog_item_bottom_margin"
android:scaleType="centerInside"
android:tint="@color/control_primary_text" />
<TextView
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index ee1173be0db9..8a47a22ff985 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -48,5 +48,6 @@
android:layout_height="48dp"
android:layout_marginBottom="4dp"
android:tint="@color/media_primary_text"
+ android:forceHasOverlappingRendering="false"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 07bbb8f40eb8..3c641afea0d6 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -24,8 +24,17 @@
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center_horizontal|fill_vertical"
+ android:forceHasOverlappingRendering="false"
android:background="@drawable/qs_media_background">
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/center_vertical_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_percent="0.5"
+ />
+
<!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
<FrameLayout
android:id="@+id/notification_media_progress_time"
@@ -56,25 +65,16 @@
<!-- Actions must be ordered left-to-right even in RTL layout. However, they appear in a chain
with the album art and the title, and must as a group appear at the end of that chain. This is
- accomplished by having the guidebox (an invisible view that is positioned around all 5 actions)
- in the chain with the album art and the title. The actions are in a LTR chain bounded by that
- guidebox, and the ambiguity of how wide the guidebox should be is resolved by using a barrier
- which forces it's starting edge to be as far to the end as possible while fitting the actions.
- -->
+ accomplished by having all actions appear in a LTR chain within the parent, and then biasing it
+ to the right side, then this barrier is used to bound the text views. -->
<androidx.constraintlayout.widget.Barrier
android:id="@+id/media_action_barrier"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
+ app:layout_constraintTop_toTopOf="parent"
app:barrierDirection="start"
- />
-
- <View
- android:id="@+id/media_action_guidebox"
- android:layout_width="0dp"
- android:layout_height="48dp"
- android:layout_marginTop="16dp"
- android:visibility="invisible"
+ app:constraint_referenced_ids="action0,action1,action2,action3,action4"
/>
<ImageButton
@@ -132,6 +132,7 @@
android:layout_width="@dimen/qs_seamless_icon_size"
android:layout_height="@dimen/qs_seamless_icon_size"
android:layout_marginEnd="8dp"
+ android:layout_gravity="center_vertical"
android:tint="@color/media_primary_text"
android:src="@*android:drawable/ic_media_seamless" />
@@ -139,6 +140,7 @@
android:id="@+id/media_seamless_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:text="@*android:string/ext_media_seamless_action"
@@ -155,15 +157,6 @@
android:src="@drawable/ic_cast_connected"
android:forceHasOverlappingRendering="false" />
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/media_seamless_barrier"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:barrierDirection="start"
- app:constraint_referenced_ids="media_seamless,media_seamless_fallback"
- app:barrierAllowsGoneWidgets="false"
- />
-
<!-- Seek Bar -->
<!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
<SeekBar
@@ -199,7 +192,6 @@
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:singleLine="true"
android:textColor="@color/media_primary_text"
- android:textDirection="locale"
android:textSize="16sp" />
<!-- Artist name -->
@@ -210,14 +202,13 @@
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:textColor="@color/media_secondary_text"
- android:textDirection="locale"
android:textSize="14sp" />
<com.android.internal.widget.CachingIconView
android:id="@+id/icon"
android:tint="@color/media_primary_text"
- android:layout_width="20dp"
- android:layout_height="20dp" />
+ android:layout_width="@dimen/qs_media_icon_size"
+ android:layout_height="@dimen/qs_media_icon_size" />
<!-- Buttons to remove this view when no longer needed -->
<include
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
new file mode 100644
index 000000000000..3c306322d21f
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+
+<com.android.systemui.privacy.OngoingPrivacyChip
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/privacy_chip"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ android:focusable="true" >
+
+ <FrameLayout
+ android:id="@+id/background"
+ android:layout_height="@dimen/ongoing_appops_chip_height"
+ android:layout_width="wrap_content"
+ android:minWidth="48dp"
+ android:layout_gravity="center_vertical">
+ <LinearLayout
+ android:id="@+id/icons_container"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="center_vertical"
+ />
+ </FrameLayout>
+</com.android.systemui.privacy.OngoingPrivacyChip> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 5c00af5705e9..436188a83d4f 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -62,7 +62,7 @@
android:gravity="center_vertical"
android:focusable="true"
android:singleLine="true"
- android:ellipsize="end"
+ android:ellipsize="marquee"
android:textAppearance="@style/TextAppearance.QS.Status"
android:layout_marginEnd="4dp"
android:visibility="gone"/>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 597644bf3295..4527c6c793d5 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -53,6 +53,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
+ android:importantForAccessibility="no"
android:layout_weight="1">
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index be86e5f5abc5..3c7480181877 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -14,7 +14,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
-->
-<FrameLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/quick_status_bar_system_icons"
@@ -27,6 +27,13 @@
android:clickable="true"
android:paddingTop="@dimen/status_bar_padding_top" >
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|start" >
+
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
@@ -38,5 +45,23 @@
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
systemui:showDark="false" />
+ </LinearLayout>
+
+ <android.widget.Space
+ android:id="@+id/space"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|end" >
+
+ <include layout="@layout/ongoing_privacy_chip" />
-</FrameLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
deleted file mode 100644
index 35f2af4f7526..000000000000
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:paddingTop="350dp"
- android:background="#CC000000"
- android:gravity="top|center_horizontal"
- android:clipChildren="false">
-
- <com.android.systemui.pip.tv.PipControlsView
- android:id="@+id/pip_controls"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:alpha="0" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
new file mode 100644
index 000000000000..732758a2ded2
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.systemui.biometrics.UdfpsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/udfps_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ systemui:sensorRadius="140px"
+ systemui:sensorMarginBottom="630px"
+ systemui:sensorTouchAreaCoefficient="0.5"/>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 256364665f43..e48fe656d187 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tik weer om oop te maak"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swiep op om oop te maak"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Swiep op om weer te probeer"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Hierdie toestel behoort aan jou organisasie"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Hierdie toestel behoort aan <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Swiep vanaf ikoon vir foon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Swiep vanaf ikoon vir stembystand"</string>
<string name="camera_hint" msgid="4519495795000658637">"Swiep vanaf ikoon vir kamera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profiel kan gemonitor word"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Netwerk kan dalk gemonitor word"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Netwerk kan dalk gemonitor word"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Jou organisasie besit hierdie toestel en kan netwerkverkeer monitor"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> besit hierdie toestel en kan netwerkverkeer monitor"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Hierdie toestel behoort aan jou organisasie en is gekoppel aan <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Hierdie toestel behoort aan <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> en is gekoppel aan <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Hierdie toestel behoort aan jou organisasie"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Hierdie toestel behoort aan <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Hierdie toestel behoort aan jou organisasie en is gekoppel aan VPN\'e"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Hierdie toestel behoort aan <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> en is gekoppel aan VPN\'e"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Jou organisasie kan netwerkverkeer in jou werkprofiel monitor"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kan netwerkverkeer in jou werkprofiel monitor"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Netwerk kan gemonitor word"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Hierdie toestel is gekoppel aan VPN\'e"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Jou werkprofiel is gekoppel aan <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Jou persoonlike profiel is gekoppel aan <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Hierdie toestel is gekoppel aan <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Toestelbestuur"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profielmonitering"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Netwerkmonitering"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Deaktiveer VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Ontkoppel VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Bekyk beleide"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Hierdie toestel behoort aan <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nJou IT-admin kan instellings, korporatiewe toegang, programme, data wat met jou toestel geassosieer word, en jou toestel se ligginginligting monitor en bestuur.\n\nKontak jou IT-admin vir meer inligting."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Hierdie toestel behoort aan jou organisasie.\n\nJou IT-admin kan instellings, korporatiewe toegang, programme, data wat met jou toestel geassosieer word, en jou toestel se ligginginligting monitor en bestuur.\n\nKontak jou IT-admin vir meer inligting."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Jou organisasie het \'n sertifikaatoutoriteit op hierdie toestel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jou organisasie het \'n sertifikaatoutoriteit in jou werkprofiel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"\'n Sertifikaatoutoriteit is op hierdie toestel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Stil"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Verstek"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Borrel"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Outomaties"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen klank of vibrasie nie"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen klank of vibrasie nie en verskyn laer in gespreksafdeling"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan lui of vibreer op grond van fooninstellings"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan lui of vibreer op grond van fooninstellings. Gesprekke van <xliff:g id="APP_NAME">%1$s</xliff:g> af verskyn by verstek in \'n borrel."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Hou jou aandag met \'n swewende kortpad na hierdie inhoud toe."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laat die stelsel bepaal of hierdie kennisgewing \'n klank moet maak of vibreer"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wys boaan die gespreksafdeling, verskyn as \'n swewende borrel, wys profielfoto op sluitskerm"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Instellings"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Hierdie program wys tans bo-oor ander programme op jou skerm en gebruik die mikrofoon en kamera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Instellings"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Die stelsel het hierdie kennisgewing stilgemaak."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Die stelsel het hierdie kennisgewing bevorder."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Die stelsel het hierdie kennisgewing gedegradeer."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Is dit korrek?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Dankie vir jou terugvoer!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kennisgewingkontroles vir <xliff:g id="APP_NAME">%1$s</xliff:g> is oopgemaak"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Kennisgewingkontroles vir <xliff:g id="APP_NAME">%1$s</xliff:g> is toegemaak"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Laat kennisgewings van hierdie kanaal af toe"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Wysig volgorde van instellings."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Bladsy <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Sluitskerm"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Vou uit"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimeer"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Maak toe"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Instellings"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Swiep af om toe te maak"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Kieslys"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> is in beeld-in-beeld"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"As jy nie wil hê dat <xliff:g id="NAME">%s</xliff:g> hierdie kenmerk moet gebruik nie, tik om instellings oop te maak en skakel dit af."</string>
- <string name="pip_play" msgid="333995977693142810">"Speel"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Laat wag"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Slaan oor na volgende"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Slaan oor na vorige"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Foon afgeskakel weens hitte"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Jou foon werk nou normaal"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Jou foon werk nou normaal.\nTik vir meer inligting"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Jou foon was te warm en dit het afgeskakel om af te koel. Jou foon werk nou normaal.\n\nJou foon kan dalk te warm word as jy:\n • Hulpbron-intensiewe programme (soos dobbel-, video- of navigasieprogramme) gebruik\n • Groot lêers af- of oplaai\n • Jou foon in hoë temperature gebruik"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Sien versorgingstappe"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Foon raak warm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Sommige kenmerke is beperk terwyl foon afkoel"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Sommige kenmerke is beperk terwyl foon afkoel.\nTik vir meer inligting"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Jou foon sal outomaties probeer om af te koel. Jy kan steeds jou foon gebruik, maar dit sal dalk stadiger wees.\n\nJou foon sal normaalweg werk nadat dit afgekoel het."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Sien versorgingstappe"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Trek laaier uit"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Kan nie hierdie toestel laai nie. Trek die kragprop uit, en wees versigtig, want die kabel kan warm wees."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Sien versorgingstappe"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Hou en sleep om kontroles te herrangskik"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Alle kontroles is verwyder"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Veranderinge is nie gestoor nie"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Sien ander programme"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontroles kon nie gelaai word nie. Gaan die <xliff:g id="APP">%s</xliff:g>-program na om seker te maak dat die programinstellings nie verander het nie."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Versoenbare kontroles is nie beskikbaar nie"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Ander"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Bevestig verandering vir <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Swiep om meer te sien"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laai tans aanbevelings"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Maak hierdie mediasessie toe"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Versteek die huidige sessie."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Versteek"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Hervat"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellings"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan program na"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Fout, probeer tans weer …"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nie gekry nie"</string>
diff --git a/packages/SystemUI/res/values-af/strings_tv.xml b/packages/SystemUI/res/values-af/strings_tv.xml
index 2ea6c60ab1b8..f277529397f6 100644
--- a/packages/SystemUI/res/values-af/strings_tv.xml
+++ b/packages/SystemUI/res/values-af/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Beeld-in-beeld"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Titellose program)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Maak PIP toe"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Volskerm"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofoon aktief"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s het toegang tot jou mikrofoon"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index f896f24010f6..300ca0e5b141 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ለመክፈት ዳግም መታ ያድርጉ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ለመክፈት በጣት ወደ ላይ ጠረግ ያድርጉ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"እንደገና ለመሞከር ወደ ላይ ይጥረጉ"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ይህ መሣሪያ የድርጅትዎ ነው"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ይህ መሳሪያ ንብረትነቱ የ<xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ነው"</string>
<string name="phone_hint" msgid="6682125338461375925">"ለስልክ ከአዶ ላይ ጠረግ ያድርጉ"</string>
<string name="voice_hint" msgid="7476017460191291417">"ለድምጽ ረዳት ከአዶ ጠረግ ያድርጉ"</string>
<string name="camera_hint" msgid="4519495795000658637">"ለካሜራ ከአዶ ላይ ጠረግ ያድርጉ"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"መገለጫ ክትትል ሊደረግበት ይችላል"</string>
<string name="vpn_footer" msgid="3457155078010607471">"አውታረ መረብ በክትትል እየተደረገበት ሊሆን ይችላል"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"አውታረ መረብ ክትትል የሚደረግበት ሊሆን ይችላል"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"የእርስዎ ድርጅት የዚህ መሣሪያ ባለቤት ነው፣ እና የአውታረ መረብ ትራፊክን ሊከታተል ይችላል"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> የዚህ መሣሪያ ባለቤት ነው፣ እና የአውታረ መረብ ትራፊክን ሊከታተል ይችላል"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ይህ መሣሪያ የድርጅትዎ ሲሆን ከ<xliff:g id="VPN_APP">%1$s</xliff:g> ጋር ተገናኝቷል"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ይህ መሳሪያ ንብረትነቱ የ<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ሲሆን ከ<xliff:g id="VPN_APP">%2$s</xliff:g> ጋር ተገናኝቷል"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ይህ መሣሪያ የድርጅትዎ ነው"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ይህ መሳሪያ ንብረትነቱ የ<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ነው"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ይህ መሣሪያ የድርጅትዎ ሲሆን ከቪፒኤን ጋር ተገናኝቷል"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ይህ መሳሪያ ንብረትነቱ የ<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ሲሆን ከቪፒኤን ጋር ተገናኝቷል"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"የእርስዎ ድርጅት በእርስዎ የሥራ መገለጫ ያለን የአውታረ መረብ ትራፊክን ሊቆጣጠር ይችል ይሆናል"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> በእርስዎ የሥራ መገለጫ ውስጥ የአውታረ መረብ ትራፊክ ላይ ክትትል ሊያደርግ ይችላል"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"አውታረ መረብ ክትትል የሚደረግበት ሊሆን ይችላል"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ይህ መሳሪያ ከቪፒኤን ጋር ተገናኝቷል"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"የእርስዎ የሥራ መገለጫ ከ<xliff:g id="VPN_APP">%1$s</xliff:g> ጋር ተገናኝቷል።"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"የእርስዎ የግል መገለጫ ከ<xliff:g id="VPN_APP">%1$s</xliff:g> ጋር ተገናኝቷል"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ይህ መሳሪያ ከ<xliff:g id="VPN_APP">%1$s</xliff:g> ጋር ተገናኝቷል"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"የመሣሪያ አስተዳደር"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"መገለጫን መከታተል"</string>
<string name="monitoring_title" msgid="4063890083735924568">"የአውታረ መረብ ክትትል"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN አሰናክል"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"የVPN ግንኙነት አቋርጥ"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"መመሪያዎችን ይመልከቱ"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ይህ መሣሪያ የ<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ነው።\n\nየእርስዎ የአይቲ አስተዳዳሪ ቅንብሮችን፣ የኮርፖሬት መዳረሻን፣ መተግበሪያዎችን፣ ከመሣሪያዎ ጋር የተጎዳኘ ውሂብን እና የመሣሪያዎ አካባቢ መረጃን መከታተል እና ማቀናበር ይችላል።\n\nተጨማሪ መረጃ የአይቲ አስተዳዳሪዎን ያነጋግሩ።"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ይህ መሣሪያ የድርጅትዎ ነው።\n\nየእርስዎ የአይቲ አስተዳዳሪ ቅንብሮችን፣ የኮርፖሬት መዳረሻን፣ መተግበሪያዎችን፣ ከመሣሪያዎ ጋር የተጎዳኘ ውሂብን እና የመሣሪያዎ አካባቢ መረጃን መከታተል እና ማቀናበር ይችላል።\n\nለተጨማሪ መረጃ የአይቲ አስተዳዳሪዎን ያነጋግሩ።"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"የእርስዎ ድርጅት የእውቅና ማረጋገጫ ሰጪ ባለሥልጣን በዚህ መሣሪያ ላይ ጭኗል። የእርስዎ ደኅንነቱ የተጠበቀ አውታረ መረብ ትራፊክ ክትትል ሊደረግበት እና ሊሻሻል ይችላል።"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"የእርስዎ ድርጅት የእውቅና ማረጋገጫ ሰጪ ባለሥልጣን በእርስዎ የሥራ መገለጫ ላይ ጭኗል። የእርስዎ ደኅንነቱ የተጠበቀ አውታረ መረብ ትራፊክ ክትትል ሊደረግበት እና ሊሻሻል ይችላል።"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"የእውቅና ማረጋገጫ ሰጪ ባለሥልጣን በዚህ መሣሪያ ላይ ተጭኗል። የእርስዎ ደኅንነቱ የተጠበቀ አውታረ መረብ ትራፊክ ክትትል ሊደረግበት እና ሊሻሻል ይችላል።"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"ፀጥ ያለ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ነባሪ"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"አረፋ"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ራስ-ሰር"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ምንም ድምጽ ወይም ንዝረት የለም"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ምንም ድምጽ ወይም ንዝረት የለም እና በውይይት ክፍል ላይ አይታይም"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"በእርስዎ የስልክ ቅንብሮች የሚወሰን ሆኖ ሊደውል ወይም ሊነዝር ይችላል"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"በእርስዎ የስልክ ቅንብሮች የሚወሰን ሆኖ ሊደውል ወይም ሊነዝር ይችላል። የ<xliff:g id="APP_NAME">%1$s</xliff:g> አረፋ ውይይቶች በነባሪነት።"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ለዚህ ይዞታ ከተንሳፋፊ አቋራጭ ጋር የእርስዎን ትኩረት ያቆያል።"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ይህ ማሳወቂያ ድምጽ ወይም ንዝረት መደረግ ካለበት ስርዓቱ እንዲወሰን ያድርጉት"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"በውይይት ክፍል አናት ላይ ያሳያል፣ እንደ ተንሳፋፊ አረፋ ብቅ ይላል፣ በቆልፍ ማያ ገጽ ላይ የመገለጫ ሥዕልን ያሳያል"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ቅንብሮች"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ቅድሚያ"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ይህ መተግበሪያ በማያ ገጽዎ ላይ በሌሎች መተግበሪያዎች ላይ እያሳየ እና ማይክሮፎኑንና ካሜራውን እየተጠቀመ ነው።"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ቅንብሮች"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"እሺ"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ይህ ማስታወቂያ በስርዓቱ ጸጥ እንዲል ተደርጓል።"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ይህ ማስታወቂያ በስርዓቱ ከፍ ተደርጓል።"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ይህ ማስታወቂያ በስርዓቱ ዝቅ ተደርጓል።"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"ይህ ትክክል ነበር?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"ለግብረመልስዎ እናመሰግናለን!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"እሺ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ መቆጣጠሪያዎች ተከፍተዋል"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ መቆጣጠሪያዎች ተዘግተዋል"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"ከዚህ ሰርጥ የመጡ ሁሉንም ማሳወቂያች ፍቀድ"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"የቅንብሮድ ቅደም-ተከተል አርትዕ።"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ገጽ <xliff:g id="ID_1">%1$d</xliff:g> ከ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ማያ ገጽ ቁልፍ"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"ዘርጋ"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"አሳንስ"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"ዝጋ"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ቅንብሮች"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"ለማሰናበት ወደ ታች ይጎትቱ"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"ምናሌ"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> በስዕል-ላይ-ስዕል ውስጥ ነው"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> ይህን ባህሪ እንዲጠቀም ካልፈለጉ ቅንብሮችን ለመክፈት መታ ያድርጉና ያጥፉት።"</string>
- <string name="pip_play" msgid="333995977693142810">"አጫውት"</string>
- <string name="pip_pause" msgid="1139598607050555845">"ባለበት አቁም"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"ወደ ቀጣይ ዝለል"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"ወደ ቀዳሚ ዝለል"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"ስልክ በሙቀት ምክንያት ጠፍቷል"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"የእርስዎ ስልክ አሁን በመደበኝነት እያሄደ ነው"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"የእርስዎ ስልክ በመደበኛ ሁኔታ እየሠራ ነው።\nለተጨማሪ መረጃ መታ ያድርጉ"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"የእርስዎ ስልክ በጣም ግሎ ነበር፣ ስለዚህ እንዲቀዘቅዝ ጠፍቷል። የእርስዎ ስልክ አሁን በመደበኝነት እያሄደ ነው።\n\nየሚከተሉትን ካደረጉ የእርስዎ በጣም ሊግል ይችላል፦\n • ኃይል በጣም የሚጠቀሙ መተግበሪያዎችን (እንደ ጨዋታ፣ ቪዲዮ ወይም የአሰሳ መተግበሪያዎች ያሉ) ከተጠቀሙ\n • ትላልቅ ፋይሎችን ካወረዱ ወይም ከሰቀሉ\n • ስልክዎን በከፍተኛ ሙቀት ውስጥ ከተጠቀሙ"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"የእንክብካቤ ደረጃዎችን ይመልከቱ"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ስልኩ እየሞቀ ነው"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ስልኩ እየቀዘቀዘ ሳለ አንዳንድ ባህሪዎች ይገደባሉ"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"አንዳንድ ባሕሪያት ስልኩ እየቀዘቀዘ እያለ ውስን ይሆናሉ።\nለተጨማሪ መረጃ መታ ያድርጉ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"የእርስዎ ስልክ በራስ-ሰር ለመቀዝቀዝ ይሞክራል። አሁንም ስልክዎን መጠቀም ይችላሉ፣ ነገር ግን ሊንቀራፈፍ ይችላል።\n\nአንዴ ስልክዎ ከቀዘቀዘ በኋላ በመደበኝነት ያሄዳል።"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"የእንክብካቤ ደረጃዎችን ይመልከቱ"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ኃይል መሙያን ይንቀሉ"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"የዚህን መሣሪያ ባትሪ መሙላት ላይ ችግር አለ። የኃይል አስማሚውን ይንቀሉትና ሊግል ስለሚችል ገመዱን ይጠብቁት።"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"የእንክብካቤ ደረጃዎችን ይመልከቱ"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"መቆጣጠሪያዎችን ዳግም ለማስተካከል ይያዙ እና ይጎትቱ"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"ሁሉም መቆጣጠሪያዎች ተወግደዋል"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ለውጦች አልተቀመጡም"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ሌሎች መተግበሪያዎች ይመልከቱ"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"መቆጣጠሪያዎች ሊጫኑ አልቻሉም። የመተግበሪያው ቅንብሮች እንዳልተቀየሩ ለማረጋገጥ <xliff:g id="APP">%s</xliff:g> መተግበሪያን ይፈትሹ።"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"ተኳዃኝ መቆጣጠሪያዎች አይገኙም"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ሌላ"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"ለ<xliff:g id="DEVICE">%s</xliff:g> ለውጥን ያረጋግጡ"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"ተጨማሪ ለማየት ያንሸራትቱ"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ምክሮችን በመጫን ላይ"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ይህን የሚዲያ ክፍለ-ጊዜ ዝጋ"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"ሚዲያ"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"የአሁኑን ክፍለ-ጊዜ ደብቅ።"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ደብቅ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ከቆመበት ቀጥል"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ቅንብሮች"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ንቁ ያልኾነ፣ መተግበሪያን ይፈትሹ"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"ስህተት፣ እንደገና በመሞከር ላይ…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"አልተገኘም"</string>
diff --git a/packages/SystemUI/res/values-am/strings_tv.xml b/packages/SystemUI/res/values-am/strings_tv.xml
index 83e3fd9f539f..d9ec459d06f7 100644
--- a/packages/SystemUI/res/values-am/strings_tv.xml
+++ b/packages/SystemUI/res/values-am/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ስዕል-ላይ-ስዕል"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(ርዕስ የሌለው ፕሮግራም)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIPን ዝጋ"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"ሙሉ ማያ ገጽ"</string>
<string name="mic_active" msgid="5766614241012047024">"ማይክራፎን ንቁ ነው"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s የእርስዎን ማይክራፎን ደርሶበታል"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 03f0fd5fc453..7f2a405efed2 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -462,10 +462,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"انقر مرة أخرى للفتح"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"يمكنك الفتح بالتمرير سريعًا لأعلى."</string>
<string name="keyguard_retry" msgid="886802522584053523">"مرِّر سريعًا للأعلى لإعادة المحاولة."</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"هذا الجهاز يخص مؤسستك."</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
<string name="phone_hint" msgid="6682125338461375925">"يمكنك التمرير سريعًا من الرمز لتشغيل الهاتف"</string>
<string name="voice_hint" msgid="7476017460191291417">"يمكنك التمرير سريعًا من الرمز لتشغيل المساعد الصوتي"</string>
<string name="camera_hint" msgid="4519495795000658637">"يمكنك التمرير سريعًا من الرمز لتشغيل الكاميرا"</string>
@@ -535,33 +533,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ربما تتم مراقبة الملف الشخصي"</string>
<string name="vpn_footer" msgid="3457155078010607471">"قد تكون الشبكة خاضعة للمراقبة"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"قد تكون الشبكة خاضعة للمراقبة"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"تملك مؤسستك هذا الجهاز ويمكنها تتبّع حركة بيانات الشبكة."</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"تملك مؤسسة <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> هذا الجهاز ويمكنها تتبّع حركة بيانات الشبكة"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"هذا الجهاز يخص مؤسستك وتم ربطه بشبكة <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> وتم ربطه بشبكة <xliff:g id="VPN_APP">%2$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"هذا الجهاز يخص مؤسستك."</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"‏هذا الجهاز يخص مؤسستك وتم ربطه بشبكات افتراضية خاصة (VPN)."</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"‏هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> وتم ربطه بشبكات افتراضية خاصة (VPN)."</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"يمكن لمؤسستك مراقبة حركة بيانات الشبكة في الملف الشخصي للعمل"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"يمكن لـ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> مراقبة حركة بيانات الشبكة في ملفك الشخصي للعمل"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"قد تكون الشبكة خاضعة للمراقبة"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"‏تم ربط هذا الجهاز بشبكات افتراضية خاصة (VPN)."</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"تم ربط الملف الشخصي للعمل بشبكة <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"تم ربط ملفك الشخصي بشبكة <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"تم ربط هذا الجهاز بشبكة <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"إدارة الأجهزة"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"مراقبة الملف الشخصي"</string>
<string name="monitoring_title" msgid="4063890083735924568">"مراقبة الشبكات"</string>
@@ -571,10 +557,8 @@
<string name="disable_vpn" msgid="482685974985502922">"إيقاف الشبكة الافتراضية الخاصة"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"‏قطع الاتصال بشبكة VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"عرض السياسات"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nيمكن لمشرف تكنولوجيا المعلومات تتبّع وإدارة الإعدادات والتطبيقات والبيانات المرتبطة بجهازك ومعلومات الموقع الجغرافي للجهاز وعمليات الدخول إلى نظام المؤسسة.\n\nللحصول على المزيد من المعلومات، يمكنك التواصل مع مشرف تكنولوجيا المعلومات."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"هذا الجهاز يخص مؤسستك.\n\nيمكن لمشرف تكنولوجيا المعلومات في مؤسستك تتبّع وإدارة الإعدادات والتطبيقات والبيانات المرتبطة بجهازك ومعلومات الموقع الجغرافي للجهاز وعمليات الدخول إلى نظام المؤسسة.\n\nللحصول على المزيد من المعلومات، يمكنك التواصل مع مشرف تكنولوجيا المعلومات."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ثبّتت مؤسستك مرجعًا مصدّقًا على هذا الجهاز. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ثبّتت مؤسستك مرجعًا مصدّقًا في ملفك الشخصي للعمل. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"تم تثبيت مرجع مصدّق على هذا الجهاز. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string>
@@ -736,18 +720,16 @@
<string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"متابعة إرسال التنبيهات"</string>
<string name="inline_turn_off_notifications" msgid="8543989584403106071">"إيقاف الإشعارات"</string>
<string name="inline_keep_showing_app" msgid="4393429060390649757">"هل تريد الاستمرار في تلقي إشعارات من هذا التطبيق؟"</string>
- <string name="notification_silence_title" msgid="8608090968400832335">"إشعار صامت"</string>
- <string name="notification_alert_title" msgid="3656229781017543655">"تلقائي"</string>
+ <string name="notification_silence_title" msgid="8608090968400832335">"صامتة"</string>
+ <string name="notification_alert_title" msgid="3656229781017543655">"تلقائية"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"فقاعة"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"تلقائي"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"بدون صوت أو اهتزاز"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"بدون صوت أو اهتزاز وتظهر في موضع أسفل في قسم المحادثات"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"يمكن إصدار رنين أو اهتزاز بناءً على إعدادات الهاتف"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"يمكن إصدار رنين أو اهتزاز بناءً على إعدادات الهاتف. تظهر المحادثات من <xliff:g id="APP_NAME">%1$s</xliff:g> كفقاعات تلقائيًا."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"يلفِت هذا الإشعار انتباهك لهذا المحتوى باستخدام اختصار عائم."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"السماح للنظام بتحديد ما إذا يجب اهتزاز الجهاز أو إصدار رنين عند تلقّي هذا الإشعار"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"تظهر في أعلى قسم المحادثات وتظهر كفقاعة عائمة وتعرض صورة الملف الشخصي على شاشة القفل"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"الإعدادات"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"الأولوية"</string>
@@ -768,18 +750,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"يتم عرض هذا التطبيق فوق التطبيقات الأخرى على شاشتك ويستخدم الميكروفون والكاميرا."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"الإعدادات"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"حسنًا"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"كتم النظام صوت هذا الإشعار."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"رفع النظام ترتيب هذا الإشعار."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"خفض النظام ترتيب هذا الإشعار."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"هل كان هذا صحيحًا؟"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"شكرًا على تعليقك"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"حسنًا"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"تم فتح عناصر التحكم في الإشعارات لتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"تم إغلاق عناصر التحكم في الإشعارات لتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"السماح بالإشعارات من هذه القناة"</string>
@@ -960,26 +936,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"تعديل ترتيب الإعدادات."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"الصفحة <xliff:g id="ID_1">%1$d</xliff:g> من <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"شاشة القفل"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"توسيع"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"تصغير"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"إغلاق"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"الإعدادات"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"اسحب لأسفل للإلغاء"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"القائمة"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> يظهر في صورة داخل صورة"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"إذا كنت لا تريد أن يستخدم <xliff:g id="NAME">%s</xliff:g> هذه الميزة، فانقر لفتح الإعدادات، ثم أوقِف تفعيل هذه الميزة."</string>
- <string name="pip_play" msgid="333995977693142810">"تشغيل"</string>
- <string name="pip_pause" msgid="1139598607050555845">"إيقاف مؤقت"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"التخطي إلى التالي"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"التخطي إلى السابق"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"تم إيقاف الهاتف بسبب الحرارة"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"يعمل هاتفك الآن بشكل طبيعي"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"يعمل هاتفك الآن بشكل طبيعي.\nانقر للحصول على مزيد من المعلومات."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"ارتفعت درجة حرارة هاتفك بشدة، لذا تم إيقاف تشغيله لخفض درجة حرارته. يعمل هاتفك الآن بشكل طبيعي.\n\nقد ترتفع بشدة درجة حرارة هاتفك إذا:\n • استخدمت تطبيقات كثيفة الاستخدام لموارد الجهاز (مثل الألعاب أو الفيديو أو تطبيقات التنقل)\n • نزَّلت أو حمَّلت ملفات كبيرة الحجم\n • استخدمت هاتفك وسط أجواء مرتفعة الحرارة"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"الاطّلاع على خطوات العناية"</string>
<string name="high_temp_title" msgid="2218333576838496100">"تزداد درجة حرارة الهاتف"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"يتم تقييد عمل بعض الميزات إلى أن تنخفض درجة حرارة الهاتف"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"يتم تقييد عمل بعض الميزات إلى أن تنخفض درجة حرارة الهاتف.\nانقر للحصول على مزيد من المعلومات."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"سيحاول الهاتف تخفيض درجة حرارته تلقائيًا. سيظل بإمكانك استخدام هاتفك، ولكن قد يعمل بشكل أبطأ.\n\nبعد أن تنخفض درجة حرارة الهاتف، سيستعيد سرعته المعتادة."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"الاطّلاع على خطوات العناية"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"فصل الشاحن"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"هناك مشكلة في شحن هذا الجهاز. يُرجى فصل محوِّل الطاقة بحرص لأن الكابل قد يكون ساخنًا."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"الاطّلاع على خطوات العناية"</string>
@@ -1077,7 +1041,7 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"الإعدادات"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"نافذة التكبير"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"عناصر التحكم في نافذة التكبير"</string>
- <string name="quick_controls_title" msgid="6839108006171302273">"أدوات التحكم بالجهاز"</string>
+ <string name="quick_controls_title" msgid="6839108006171302273">"أدوات التحكم بالأجهزة"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"إضافة عناصر تحكّم لأجهزتك المتصلة"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"إعداد أدوات التحكم بالجهاز"</string>
<string name="quick_controls_setup_subtitle" msgid="1681506617879773824">"اضغط مع الاستمرار على زر التشغيل للوصول إلى عناصر التحكّم"</string>
@@ -1102,6 +1066,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"اضغط مع الاستمرار واسحب لإعادة ترتيب عناصر التحكّم."</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"تمت إزالة كل عناصر التحكّم."</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"لم يتم حفظ التغييرات."</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"عرض التطبيقات الأخرى"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"تعذَّر تحميل عناصر التحكّم. تحقّق من تطبيق <xliff:g id="APP">%s</xliff:g> للتأكّد من أنه لم يتم تغيير إعدادات التطبيق."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"عناصر التحكّم المتوافقة غير متوفّرة"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"غير ذلك"</string>
@@ -1119,8 +1084,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"تأكيد التغيير لـ <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"مرّر سريعًا لرؤية المزيد."</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"جارٍ تحميل الاقتراحات"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"إغلاق جلسة تشغيل الوسائط هذه"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"الوسائط"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"إخفاء الجلسة الحالية"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"إخفاء"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"استئناف التشغيل"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"الإعدادات"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"غير نشط، تحقّق من التطبيق."</string>
<string name="controls_error_retryable" msgid="864025882878378470">"حدث خطأ، جارٍ إعادة المحاولة…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"لم يتم العثور عليه."</string>
diff --git a/packages/SystemUI/res/values-ar/strings_tv.xml b/packages/SystemUI/res/values-ar/strings_tv.xml
index 76403ab1a4ee..c29d804f841f 100644
--- a/packages/SystemUI/res/values-ar/strings_tv.xml
+++ b/packages/SystemUI/res/values-ar/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"نافذة ضمن النافذة"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(ليس هناك عنوان للبرنامج)"</string>
- <string name="pip_close" msgid="5775212044472849930">"‏إغلاق PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"ملء الشاشة"</string>
<string name="mic_active" msgid="5766614241012047024">"الميكروفون نشط"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"‏تمكن %1$s من الوصول إلى الميكروفون الخاص بك."</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index d19565092cba..0345af07cd91 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"খুলিবলৈ পুনৰাই টিপক"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"খুলিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
<string name="keyguard_retry" msgid="886802522584053523">"পুনৰ চেষ্টা কৰিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>ৰ"</string>
<string name="phone_hint" msgid="6682125338461375925">"ফ\'নৰ বাবে আইকনৰপৰা ছোৱাইপ কৰক"</string>
<string name="voice_hint" msgid="7476017460191291417">"কণ্ঠধ্বনিৰে সহায়ৰ বাবে আইকনৰ পৰা ছোৱাইপ কৰক"</string>
<string name="camera_hint" msgid="4519495795000658637">"কেমেৰা খুলিবলৈ আইকনৰপৰা ছোৱাইপ কৰক"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"প্ৰ\'ফাইল নিৰীক্ষণ কৰা হ\'ব পাৰে"</string>
<string name="vpn_footer" msgid="3457155078010607471">"নেটৱৰ্ক নিৰীক্ষণ কৰা হ\'ব পাৰে"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"নেটৱৰ্ক নিৰীক্ষণ কৰা হ\'ব পাৰে"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"এই ডিভাইচটোৰ গৰাকী আপোনাৰ প্ৰতিষ্ঠান আৰু ই নেটৱৰ্কৰ ট্ৰেফিক নিৰীক্ষণ কৰিব পাৰে"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"এই ডিভাইচটোৰ গৰাকী <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> আৰু এইটোৱে নেটৱৰ্কৰ ট্ৰেফিক নিৰীক্ষণ কৰিব পাৰে"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ আৰু এইটো <xliff:g id="VPN_APP">%1$s</xliff:g>ৰ সৈতে সংযুক্ত হৈ আছে"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ৰ আৰু এইটো <xliff:g id="VPN_APP">%2$s</xliff:g>ৰ সৈতে সংযুক্ত হৈ আছে"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ৰ"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ আৰু এইটো VPNসমূহৰ সৈতে সংযুক্ত হৈ আছে"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ৰ আৰু এইটো VPNসমূহৰ সৈতে সংযুক্ত হৈ আছে"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"আপোনাৰ প্ৰতিষ্ঠানে আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ কৰিব পাৰে"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>এ আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ কৰিব পাৰে"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"নেটৱৰ্ক নিৰীক্ষণ কৰা হ\'ব পাৰে"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"এই ডিভাইচটো VPNসমূহৰ সৈতে সংযুক্ত হৈ আছে"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"আপোনাৰ কৰ্মস্থানৰ প্ৰ’ফাইলটো <xliff:g id="VPN_APP">%1$s</xliff:g>ৰ সৈতে সংযুক্ত হৈ আছে"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"আপোনাৰ ব্যক্তিগত প্ৰ’ফাইলটো <xliff:g id="VPN_APP">%1$s</xliff:g>ৰ সৈতে সংযুক্ত হৈ আছে"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"এই ডিভাইচটো <xliff:g id="VPN_APP">%1$s</xliff:g>ৰ সৈতে সংযুক্ত হৈ আছে"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ডিভাইচৰ পৰিচালনা"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"প্ৰ\'ফাইল নিৰীক্ষণ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"নেটৱৰ্ক নিৰীক্ষণ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"ভিপিএন অক্ষম কৰক"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"ভিপিএন সংযোগ বিচ্ছিন্ন কৰক"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"নীতিসমূহ চাওক"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিংসমূহ, কৰ্পৰে’টৰ এক্সেছ, এপ্‌সমূহ, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিংসমূহ, কৰ্পৰে’টৰ এক্সেছ, এপ্‌সমূহ, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"আপোনাৰ প্ৰতিষ্ঠানে এই ডিভাইচটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰিছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"আপোনাৰ প্ৰতিষ্ঠানে আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰিছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"এই ডিভাইচটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰা হৈছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"নীৰৱ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ডিফ’ল্ট"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"বাবল"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"স্বয়ংক্ৰিয়"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"কোনো ধ্বনি অথবা কম্পন নাই"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"কোনো ধ্বনি অথবা কম্পন নাই আৰু বাৰ্তালাপ শাখাটোৰ তলৰ অংশত দেখা পোৱা যায়"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ফ’নৰ ছেটিঙৰ ওপৰত নিৰ্ভৰ কৰি ৰিং কৰিব অথবা কম্পন হ’ব পাৰে"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ফ’নৰ ছেটিঙৰ ওপৰত নিৰ্ভৰ কৰি ৰিং কৰিব অথবা কম্পন হ’ব পাৰে। <xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাৰ্তালাপ ডিফ’ল্ট হিচাপে বাবল হয়।"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"উপঙি থকা এটা শ্বৰ্টকাটৰ জৰিয়তে এই সমলখিনিৰ প্ৰতি আপোনাক মনোযোগী কৰি ৰাখে।"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"এই জাননীটোৱে ধ্বনি নে কম্পন সৃষ্টি কৰিব সেয়া ছিষ্টেমটোক নিৰ্ধাৰণ কৰিবলৈ দিয়ক"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"বাৰ্তালাপ শাখাটোৰ শীৰ্ষত দেখুৱায়, ওপঙা বাবল হিচাপে দেখা পোৱা যায়, লক স্ক্ৰীনত প্ৰ’ফাইলৰ চিত্ৰ প্ৰদৰ্শন কৰে"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ছেটিংসমূহ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"অগ্ৰাধিকাৰ"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"এই এপে আপোনাৰ স্ক্ৰীণত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ মাইক্ৰ\'ফ\'ন আৰু কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ছেটিংসমূহ"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ঠিক আছে"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ছিষ্টেমটোৱে এই জাননীক নীৰৱ কৰিছে।"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ছিষ্টেমটোৱে এই জাননীটোৰ ক্ষেত্ৰত দিয়া গুৰুত্ব বৃদ্ধি কৰিছে।"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ছিষ্টেমটোৱে এই জাননীটোৰ ক্ষেত্ৰত দিয়া গুৰুত্ব হ্ৰাস কৰিছে।"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"এইটো শুদ্ধ আছিলনে?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"আপোনাৰ মতামতৰ বাবে ধন্যবাদ!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ঠিক আছে"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ জাননী নিয়ন্ত্ৰণসমূহ খোলা অৱস্থাত আছে"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ জাননী নিয়ন্ত্ৰণসমূহ বন্ধ অৱস্থাত আছে"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"এই চ্চেনেলৰ পৰা জাননী দিবলৈ অনুমতি দিয়ক"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ছেটিংসমূহৰ ক্ৰম সম্পাদনা কৰক।"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>ৰ পৃষ্ঠা <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্ৰীণ"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"বিস্তাৰ কৰক"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"সৰু কৰক"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"বন্ধ কৰক"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ছেটিংসমূহ"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"অগ্ৰাহ্য কৰিবলৈ তললৈ টানক"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"মেনু"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> চিত্ৰৰ ভিতৰৰ চিত্ৰত আছে"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g> সুবিধাটো ব্যৱহাৰ কৰিব নোখোজে, তেন্তে ছেটিংসমূহ খুলিবলৈ টিপক আৰু তালৈ গৈ ইয়াক অফ কৰক।"</string>
- <string name="pip_play" msgid="333995977693142810">"প্লে কৰক"</string>
- <string name="pip_pause" msgid="1139598607050555845">"পজ কৰক"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"আগৰটো মিডিয়ালৈ যাওক"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"আপোনাৰ ফ\'নটো গৰম হোৱাৰ কাৰণে অফ কৰা হৈছিল"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"আপোনাৰ ফ\'নটো এতিয়া স্বাভাৱিকভাৱে চলি আছে"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"আপোনাৰ ফ’নটো এতিয়া স্বাভাৱিকভাৱে চলি আছে।\nঅধিক তথ্যৰ বাবে টিপক"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"আপোনাৰ ফ\'নটো অত্যধিক গৰম হোৱাৰ বাবে ইয়াক ঠাণ্ডা কৰিবলৈ অফ কৰা হৈছিল। আপোনাৰ ফ\'নটো এতিয়া স্বাভাৱিকভাৱে চলি আছে।\n\nআপোনাৰ ফ\'নটো গৰম হ\'ব পাৰে, যদিহে আপুনি:\n • ফ\'নটোৰ হাৰ্ডৱেৰ অত্যধিক মাত্ৰাত ব্যৱহাৰ কৰা এপসমূহ চলালে (যেনে, ভিডিঅ\' গেইম, ভিডিঅ\', দিক্-নিৰ্দেশনা এপসমূহ)\n • খুউব ডাঙৰ আকাৰৰ ফাইল আপল\'ড বা ডাউনল’ড কৰিলে\n • আপোনাৰ ফ\'নটো উচ্চ তাপমাত্ৰাৰ পৰিৱেশত ব্যৱহাৰ কৰিলে"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"যত্ন লোৱাৰ পদক্ষেপসমূহ চাওক"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ফ\'নটো গৰম হ\'বলৈ ধৰিছে"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ফ\'নটো ঠাণ্ডা হৈ থকা সময়ত কিছুমান সুবিধা উপলব্ধ নহ’ব"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ফ’নটো ঠাণ্ডা হৈ থকাৰ সময়ত কিছুমান সুবিধা উপলব্ধ নহয়।\nঅধিক তথ্যৰ বাবে টিপক"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"আপোনাৰ ফ\'নটোৱে নিজে নিজে ঠাণ্ডা হ\'বলৈ স্বয়ংক্ৰিয়ভাৱে চেষ্টা কৰিব। আপুনি ফ\'নটো ব্যৱহাৰ কৰি থাকিব পাৰে কিন্তু ই লাহে লাহে চলিব পাৰে।\n\nফ\'নটো সম্পূৰ্ণভাৱে ঠাণ্ডা হোৱাৰ পিছত ই আগৰ নিচিনাকৈয়েই চলিব।"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"যত্ন লোৱাৰ পদক্ষেপসমূহ চাওক"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"চ্চার্জাৰ আনপ্লাগ কৰক"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"এই ডিভাইচটো চ্চার্জ কৰোঁতে কিবা সমস্যা হৈছে। পাৱাৰ এডাপ্টাৰটো আনপ্লাগ কৰক, কেব’লডাল গৰম হ’ব পাৰে গতিকে সাবধান হ’ব।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"যত্ন লোৱাৰ পদক্ষেপসমূহ চাওক"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"নিয়ন্ত্ৰণসমূহ পুনৰ সজাবলৈ ধৰি ৰাখক আৰু টানি আনি এৰক"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"সকলো নিয়ন্ত্ৰণ আঁতৰোৱা হৈছে"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"সালসলনিসমূহ ছেভ নহ’ল"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"অন্য এপ্‌সমূহ চাওক"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"নিয়ন্ত্ৰণসমূহ ল’ড কৰিবপৰা নগ’ল। এপ্‌টোৰ ছেটিংসমূহ সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APP">%s</xliff:g> এপ্‌টো পৰীক্ষা কৰক।"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"সমিল নিয়ন্ত্ৰণসমূহ উপলব্ধ নহয়"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"অন্য"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g>ৰ বাবে সলনি কৰাটো নিশ্চিত কৰক"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"অধিক চাবলৈ ছোৱাইপ কৰক"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"চুপাৰিছসমূহ ল’ড কৰি থকা হৈছে"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"এই মিডিয়া ছেশ্বনটো বন্ধ কৰক"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"বৰ্তমানৰ ছেশ্বনটো লুকুৱাওক।"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"লুকুৱাওক"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"সক্ৰিয় নহয়, এপ্‌টো পৰীক্ষা কৰক"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"আসোঁৱাহ, পুনৰ চেষ্টা কৰি আছে…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"বিচাৰি পোৱা নগ’ল"</string>
diff --git a/packages/SystemUI/res/values-as/strings_tv.xml b/packages/SystemUI/res/values-as/strings_tv.xml
index 2076c99bf901..1db8f2297cc3 100644
--- a/packages/SystemUI/res/values-as/strings_tv.xml
+++ b/packages/SystemUI/res/values-as/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"চিত্ৰৰ ভিতৰত চিত্ৰ"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(শিৰোনামবিহীন কাৰ্যক্ৰম)"</string>
- <string name="pip_close" msgid="5775212044472849930">"পিপ বন্ধ কৰক"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"সম্পূৰ্ণ স্ক্ৰীণ"</string>
<string name="mic_active" msgid="5766614241012047024">"মাইক্ৰ’ফ’ন সক্ৰিয় কৰা আছে"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$sএ আপোনাৰ মাইক্ৰ’ফ’ন এক্সেছ কৰিছে"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 4b66e727aa56..d0025306d203 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Açmaq üçün yenidən tıklayın"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Açmaq üçün yuxarı sürüşdürün"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Yenidən cəhd etmək üçün yuxarı sürüşdürün"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Bu cihaz təşkilatınıza məxsusdur"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> təşkilatına məxsusdur"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefon üçün ikonadan sürüşdürün"</string>
<string name="voice_hint" msgid="7476017460191291417">"Səs yardımçısı üçün ikonadan sürüşdürün"</string>
<string name="camera_hint" msgid="4519495795000658637">"Kamera üçün ikonadan sürüşdürün"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil izlənə bilər"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Şəbəkə nəzərdən keçirilə bilər"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Şəbəkə nəzərdən keçirilə bilər"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Təşkilatınız bu cihazın sahibidir və şəbəkə trafikinə nəzarət edə bilər"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> bu cihazın sahibidir və şəbəkə trafikinə nəzarət edə bilər"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Bu cihaz təşkilatınıza məxsusdur və <xliff:g id="VPN_APP">%1$s</xliff:g> şəbəkəsinə qoşulub"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> təşkilatına məxsusdur və <xliff:g id="VPN_APP">%2$s</xliff:g> şəbəkəsinə qoşulub"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Bu cihaz təşkilatınıza məxsusdur"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> təşkilatına məxsusdur"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Bu cihaz təşkilatınıza məxsusdur və VPN şəbəkəsinə qoşulub"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> təşkilatına məxsusdur və VPN şəbəkəsinə qoşulub"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Təşkilat iş profilində şəbəkə ötürülməsinə nəzarət edə bilər"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> iş profilində şəbəkə ötürülməsinə nəzarət edə bilər"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Şəbəkəyə nəzarət edilə bilər"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Bu cihaz VPN şəbəkəsinə qoşulub"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"İş profiliniz <xliff:g id="VPN_APP">%1$s</xliff:g> şəbəkəsinə qoşulub"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Şəxsi profiliniz <xliff:g id="VPN_APP">%1$s</xliff:g> şəbəkəsinə qoşulub"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Bu cihaz <xliff:g id="VPN_APP">%1$s</xliff:g> şəbəkəsinə qoşulub"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Cihaz idarəetməsi"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profil izlənməsi"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Şəbəkə monitorinqi"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN-i deaktiv edin"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN-i bağlantıdan ayırın"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Siyasətlərə Baxın"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> təşkilatına məxsusdur.\n\nIT admininiz cihaz və cihaz məkan məlumatı ilə əlaqəli ayarlara, korporativ girişə, tətbiqə və dataya nəzarət edə və idarə edə bilər.\n\nƏtraflı məlumat üçün IT admini ilə əlaqə saxlayın."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Bu cihaz təşkilatınıza məxsusdur.\n\nIT admininiz cihaz və cihaz məkan məlumatı ilə əlaqəli ayarlara, korporativ girişə, tətbiqə və dataya nəzarət edə və idarə edə bilər.\n\nƏtraflı məlumat üçün IT admini ilə əlaqə saxlayın."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Təşkilat bu cihazda sertifikat səlahiyyəti quraşdırdı. Təhlükəsiz şəbəkə ötürülməsinə nəzarət edilə və ya dəyişdirilə bilər."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Təşkilat iş profilində sertifikat səlahiyyəti quraşdırdı. Təhlükəsiz şəbəkə ötürülməsinə nəzarət edilə və ya dəyişdirilə bilər."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Bu cihazda sertifikat səlahiyyəti quraşdırıldı. Təhlükəsiz şəbəkə ötürülməsinə nəzarət edilə və ya dəyişdirilə bilər."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Səssiz"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Defolt"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Qabarcıq"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Səs və ya vibrasiya yoxdur"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Səs və ya vibrasiya yoxdur və söhbət bölməsinin aşağısında görünür"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Telefon ayarlarına əsasən zəng çala və ya vibrasiya edə bilər"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Telefon ayarlarına əsasən zəng çala və ya vibrasiya edə bilər. <xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqindən söhbətlərdə defolt olaraq qabarcıq çıxır."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Bu məzmuna üzən qısayol ilə diqqətinizi cəlb edir."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirişin səs çıxarması və ya vibrasiya etməsi sistem tərəfindən təyin edilsin"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Söhbət bölməsinin yuxarısında göstərilir, üzən qabarcıq kimi görünür, kilid ekranında profil şəkli göstərir"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ayarlar"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Bu tətbiq ekranda digər tətbiqlərin üzərində göstərilir və mikrofon ilə kameradan istifadə edir."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Ayarlar"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Bu bildiriş sistem tərəfindən səssiz edilib."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Bu bildiriş sistem tərəfindən irəli çəkilib."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Bu bildiriş sistem tərəfindən geri çəkilib."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Bu, doğru oldu?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Rəyiniz üçün təşəkkür edirik!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün bildiriş kontrolları açıqdır"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün bildiriş kontrolları bağlıdır"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Bu kanaldan gələn bildirişlərə icazə verin"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Ayarların sıralanmasını redaktə edin."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> səhifədən <xliff:g id="ID_1">%1$d</xliff:g> səhifə"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ekran kilidi"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Genişləndirin"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Kiçildin"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Bağlayın"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Ayarlar"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Rədd etmək üçün aşağı çəkin"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menyu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> şəkil içində şəkildədir"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> tətbiqinin bu funksiyadan istifadə etməyini istəmirsinizsə, ayarları açmaq və deaktiv etmək üçün klikləyin."</string>
- <string name="pip_play" msgid="333995977693142810">"Oxudun"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Fasilə verin"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Növbətiyə keçin"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Əvvəlkinə keçin"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"İstiliyə görə telefon söndü"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefon indi normal işləyir"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefonunuz indi normal işləyir.\nƏtraflı məlumat üçün toxunun"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon çox isti idi və soyumaq üçün söndü. Hazırda telefon normal işləyir.\n\n Telefon bu hallarda çox isti ola bilər:\n • Çox resurslu tətbiq istifadə etsəniz (oyun, video və ya naviqasiya tətbiqi kimi)\n • Böyük həcmli fayl endirsəniz və ya yükləsəniz\n • Telefonu yüksək temperaturda istifadə etsəniz"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ehtiyat tədbiri mərhələlərinə baxın"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon qızmağa başlayır"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Telefon soyuyana kimi bəzi funksiyalar məhdudlaşdırılır"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Telefon soyuyana kimi bəzi funksiyalar məhdudlaşdırılır.\nƏtraflı məlumat üçün toxunun"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonunuz avtomatik olaraq soyumağa başlayacaq. Telefon istifadəsinə davam edə bilərsiniz, lakin sürəti yavaşlaya bilər.\n\nTelefonunuz soyuduqdan sonra normal işləyəcək."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ehtiyat tədbiri mərhələlərinə baxın"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Adapteri cərəyandan ayırın"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Bu cihazın batareya yığmasında problem var. Adapteri cərəyandan ayırın. Ehtiyatlı olun, kabel isti ola bilər."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ehtiyat tədbiri mərhələlərinə baxın"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Nizamlayıcıları yenidən tənzimləmək üçün tutub sürüşdürün"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Bütün nizamlayıcılar silindi"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Dəyişikliklər yadda saxlanmadı"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Digər tətbiqlərə baxın"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Nizamlayıcıları yükləmək mümkün olmadı. <xliff:g id="APP">%s</xliff:g> tətbiqinə toxunaraq tətbiq ayarlarının dəyişmədiyinə əmin olun."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Uyğun nizamlayıcılar əlçatan deyil"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Digər"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> üzrə dəyişikliyi təsdiq edin"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Digərlərini görmək üçün sürüşdürün"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tövsiyələr yüklənir"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Bu media sessiyasını bağlayın"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Cari sessiyanı gizlədin."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Gizlədin"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Davam edin"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Aktiv deyil, tətbiqi yoxlayın"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Xəta, yenidən cəhd edilir…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tapılmadı"</string>
diff --git a/packages/SystemUI/res/values-az/strings_tv.xml b/packages/SystemUI/res/values-az/strings_tv.xml
index e4e8880df847..d690c642488f 100644
--- a/packages/SystemUI/res/values-az/strings_tv.xml
+++ b/packages/SystemUI/res/values-az/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Şəkil-içində-Şəkil"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Başlıqsız proqram)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP bağlayın"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Tam ekran"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon Aktivdir"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s mikrofona daxil olub"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 94120ae3055f..5cca958bbe96 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -456,10 +456,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Dodirnite ponovo da biste otvorili"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Prevucite nagore da biste otvorili"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Prevucite nagore da biste probali ponovo"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada organizaciji"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Prevucite od ikone za telefon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Prevucite od ikone za glasovnu pomoć"</string>
<string name="camera_hint" msgid="4519495795000658637">"Prevucite od ikone za kameru"</string>
@@ -526,33 +524,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil se možda nadgleda"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Mreža se možda nadgleda"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Mreža se možda nadgleda"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organizacija je vlasnik uređaja i može da nadgleda mrežni saobraćaj"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> je vlasnik ovog uređaja i može da nadgleda mrežni saobraćaj"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Ovaj uređaj pripada organizaciji i povezan je sa aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i povezan je sa aplikacijom <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Ovaj uređaj pripada organizaciji"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Ovaj uređaj pripada organizaciji i povezan je sa VPN-ovima"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i povezan je sa VPN-ovima"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Organizacija može da prati mrežni saobraćaj na poslovnom profilu"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> može da nadgleda mrežni saobraćaj na poslovnom profilu"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Mreža se možda nadgleda"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Ovaj uređaj je povezan sa VPN-ovima"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Poslovni profil je povezan sa aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Vaš lični profil je povezan sa aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Ovaj uređaj je povezan sa aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Upravljanje uređajima"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Nadgledanje profila"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Nadgledanje mreže"</string>
@@ -562,10 +548,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Onemogući VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Prekini vezu sa VPN-om"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Prikaži smernice"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nIT administrator može da nadgleda podešavanja, korporativni pristup, aplikacije, podatke povezane sa uređajem i informacije o lokaciji uređaja, kao i da upravlja njima.\n\nViše informacija potražite od IT administratora."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Ovaj uređaj pripada organizaciji.\n\nIT administrator može da nadgleda podešavanja, korporativni pristup, aplikacije, podatke povezane sa uređajem i informacije o lokaciji uređaja, kao i da upravlja njima.\n\nViše informacija potražite od IT administratora."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizacija je na ovom uređaju instalirala autoritet za izdavanje sertifikata. Bezbedni mrežni saobraćaj može da se prati ili menja."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizacija je na poslovnom profilu instalirala autoritet za izdavanje sertifikata. Bezbedni mrežni saobraćaj može da se prati ili menja."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Na ovom uređaju je instaliran autoritet za izdavanje sertifikata. Bezbedni mrežni saobraćaj može da se prati ili menja."</string>
@@ -730,15 +714,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Nečujno"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Podrazumevano"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Oblačić"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatska"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka i vibriranja"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Bez zvuka i vibriranja i prikazuje se u nastavku odeljka za konverzacije"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Može da zvoni ili vibrira u zavisnosti od podešavanja telefona"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Može da zvoni ili vibrira u zavisnosti od podešavanja telefona. Konverzacije iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> se podrazumevano prikazuju u oblačićima."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Privlači vam pažnju pomoću plutajuće prečice do ovog sadržaja."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sistem utvrdi da li ovo obaveštenje treba da emituje zvuk ili da vibrira"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Prikazuje se u vrhu odeljka za konverzacije kao plutajući oblačić, prikazuje sliku profila na zaključanom ekranu"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Podešavanja"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
@@ -759,18 +741,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ova aplikacija se prikazuje preko drugih aplikacija na ekranu i koristi mikrofon i kameru."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Podešavanja"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Potvrdi"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Sistem je isključio ovo obaveštenje."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Sistem je promovisao ovo obaveštenje."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Sistem je postavio ovo obaveštenje na dno."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Da li je to tačno?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Hvala vam na povratnim informacijama!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Potvrdi"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrole obaveštenja za otvaranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Kontrole obaveštenja za zatvaranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Dozvoli obaveštenja sa ovog kanala"</string>
@@ -945,26 +921,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Izmeni redosled podešavanja."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. strana od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključan ekran"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Proširi"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Umanji"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Zatvori"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Podešavanja"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Prevucite nadole da biste odbili"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Meni"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> je slika u slici"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ako ne želite da <xliff:g id="NAME">%s</xliff:g> koristi ovu funkciju, dodirnite da biste otvorili podešavanja i isključili je."</string>
- <string name="pip_play" msgid="333995977693142810">"Pusti"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pauziraj"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Pređi na sledeće"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Pređi na prethodno"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefon se isključio zbog toplote"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefon sada normalno radi"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefon sada normalno radi.\nDodirnite za više informacija"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon je bio prevruć, pa se isključio da se ohladi. Sada radi normalno.\n\nTelefon može previše da se ugreje ako:\n • Koristite aplikacije koje zahtevaju puno resursa (npr. video igre, video ili aplikacije za navigaciju)\n • Preuzimate/otpremate velike datoteke\n • Koristite telefon na visokoj temperaturi"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Pogledajte upozorenja"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon se zagrejao"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Neke funkcije su ograničene dok se telefon ne ohladi"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Neke funkcije su ograničene dok se telefon ne ohladi.\nDodirnite za više informacija"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon će automatski pokušati da se ohladi. I dalje ćete moći da koristite telefon, ali će sporije reagovati.\n\nKada se telefon ohladi, normalno će raditi."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pogledajte upozorenja"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Isključite punjač iz napajanja"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Došlo je do problema sa punjenjem ovog uređaja. Isključite adapter iz napajanja i budite pažljivi jer kabl može da bude topao."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Pogledajte upozorenja"</string>
@@ -1084,6 +1048,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Zadržite i prevucite da biste promenili raspored kontrola"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Sve kontrole su uklonjene"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Promene nisu sačuvane"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Pogledajte druge aplikacije"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Učitavanje kontrola nije uspelo. Pogledajte aplikaciju <xliff:g id="APP">%s</xliff:g> da biste se uverili da se podešavanja aplikacije nisu promenila."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kompatibilne kontrole nisu dostupne"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Drugo"</string>
@@ -1101,8 +1066,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Potvrdite promenu za: <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Prevucite da biste videli još"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavaju se preporuke"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Zatvorite ovu sesiju medija"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte aktuelnu sesiju."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sakrij"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Podešavanja"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno. Vidite aplikaciju"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Greška, pokušava se ponovo…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
index 4e2d610da4c5..d045ceaa4fca 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Slika u slici"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program bez naslova)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Zatvori PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Ceo ekran"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon je aktivan"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Aplikacija %1$s je pristupila mikrofonu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 8b4c58e0b003..956ef477c927 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -458,10 +458,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Дакраніцеся яшчэ раз, каб адкрыць"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Каб адкрыць, прагарніце ўверх"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Прагартайце ўверх, каб паўтарыць спробу"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Гэта прылада належыць вашай арганізацыі"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Гэта прылада належыць арганізацыі \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string>
<string name="phone_hint" msgid="6682125338461375925">"Тэлефон: правядзіце пальцам ад значка"</string>
<string name="voice_hint" msgid="7476017460191291417">"Галасавая дапамога: правядзіце пальцам ад значка"</string>
<string name="camera_hint" msgid="4519495795000658637">"Камера: правядзіце пальцам ад значка"</string>
@@ -529,33 +527,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"За профілем могуць назіраць"</string>
<string name="vpn_footer" msgid="3457155078010607471">"За сеткай могуць назіраць"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"За сеткай могуць назіраць"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ваша арганізацыя валодае гэтай прыладай і можа кантраляваць сеткавы трафік"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> валодае гэтай прыладай і можа кантраляваць сеткавы трафік"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Гэта прылада належыць вашай арганізацыі і падключана да праграмы \"<xliff:g id="VPN_APP">%1$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Гэта прылада належыць арганізацыі \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\" і падключана да праграмы \"<xliff:g id="VPN_APP">%2$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Гэта прылада належыць вашай арганізацыі"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Гэта прылада належыць арганізацыі \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Гэта прылада належыць вашай арганізацыі і падключана да VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Гэта прылада належыць арганізацыі \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\" і падключана да VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Ваша арганізацыя можа сачыць за сеткавым трафікам у вашым працоўным профілі"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> можа сачыць за сеткавым трафікам у вашым працоўным профілі"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"За сеткай могуць сачыць"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Гэта прылада падключана да VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Ваш працоўны профіль падключаны да праграмы \"<xliff:g id="VPN_APP">%1$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Ваш асабісты профіль падключаны да праграмы \"<xliff:g id="VPN_APP">%1$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Гэта прылада падключана да праграмы \"<xliff:g id="VPN_APP">%1$s</xliff:g>\""</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Кіраванне прыладай"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Маніторынг профіляў"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Маніторынг сеткі"</string>
@@ -565,10 +551,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Адключыць VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Адлучыць VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Праглядзець палітыку"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Гэта прылада належыць арганізацыі \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\".\n\nВаш ІТ-адміністратар можа адсочваць налады, карпаратыўны доступ, праграмы, даныя, звязаныя з вашай прыладай, і звесткі пра яе месцазнаходжанне, а таксама кіраваць імі.\n\nПа дадатковую інфармацыю звярніцеся да ІТ-адміністратара."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Гэта прылада належыць вашай арганізацыі.\n\nВаш ІТ-адміністратар можа адсочваць налады, карпаратыўны доступ, праграмы, даныя, звязаныя з вашай прыладай, і звесткі пра яе месцазнаходжанне, а таксама кіраваць імі.\n\nПа дадатковую інфармацыю звярніцеся да ІТ-адміністратара."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Ваша арганізацыя ўсталявала на гэтай прыладзе цэнтр сертыфікацыі. Ваш абаронены сеткавы трафік могуць праглядваць ці змяняць."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ваша арганізацыя ўсталявала ў вашым працоўным профілі цэнтр сертыфікацыі. Ваш абаронены сеткавы трафік могуць праглядваць ці змяняць."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На гэтай прыладзе ўсталяваны цэнтр сертыфікацыі. Ваш абаронены сеткавы трафік могуць праглядваць ці змяняць."</string>
@@ -733,15 +717,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Бязгучны рэжым"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Стандартна"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Усплывальнае апавяшчэнне"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Аўтаматычна"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без гуку ці вібрацыі"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Паказваецца без гуку ці вібрацыі ў раздзеле размоў"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя. Размовы ў праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" стандартна паяўляюцца ў выглядзе ўсплывальных апавяшчэнняў."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Прыцягвае ўвагу да гэтага змесціва ўсплывальнай кнопкай."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Сістэма сама будзе вызначаць, ці трэба для гэтага апавяшчэння ўключаць гук або вібрацыю"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Паказваецца ўверсе раздзела размоў у выглядзе ўсплывальнага апавяшчэння, а на экране блакіроўкі – у выглядзе відарыса профілю"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Налады"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Прыярытэт"</string>
@@ -762,18 +744,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Гэта праграма паказваецца на экране паверх іншых праграм. Яна выкарыстоўвае мікрафон і камеру."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Налады"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ОК"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Гук гэтага апавяшчэння выключаны сістэмай."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Прыярытэт гэтага апавяшчэння павышаны сістэмай."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Прыярытэт гэтага апавяшчэння паніжаны сістэмай."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Усё правільна?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Дзякуй за водгук!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ОК"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Кіраванне апавяшчэннямі для <xliff:g id="APP_NAME">%1$s</xliff:g> адкрыта"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Кіраванне апавяшчэннямі для <xliff:g id="APP_NAME">%1$s</xliff:g> закрыта"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Дазволіць апавяшчэнні з гэтага канала"</string>
@@ -950,26 +926,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Змяніць парадак налад."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Старонка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Экран блакіроўкі"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Разгарнуць"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Згарнуць"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Закрыць"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Налады"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Перацягніце ўніз, каб адхіліць"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Меню"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> з’яўляецца відарысам у відарысе"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Калі вы не хочаце, каб праграма <xliff:g id="NAME">%s</xliff:g> выкарыстоўвала гэту функцыю, дакраніцеся, каб адкрыць налады і адключыць яе."</string>
- <string name="pip_play" msgid="333995977693142810">"Прайграць"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Прыпыніць"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Перайсці да наступнага"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Перайсці да папярэдняга"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"З-за перагрэву тэл. выключыўся"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Тэлефон працуе нармальна"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Ваш тэлефон працуе нармальна.\nНацісніце, каб даведацца больш"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Ваш тэлефон пераграваўся, таму ён выключыўся, каб астыць. Зараз тэлефон працуе нармальна.\n\nТэлефон можа перагравацца пры:\n • Выкарыстанні рэсурсаёмістых праграм (напрыклад, гульняў, відэа або праграм навігацыі)\n • Спампоўцы або запампоўцы вялікіх файлаў\n • Выкарыстанні тэлефона пры высокіх тэмпературах"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Глядзець паэтапную дапамогу"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Тэлефон награваецца"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Некаторыя функцыі абмежаваны, пакуль тэлефон астывае"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Некаторыя функцыі абмежаваны, пакуль тэлефон не астыне.\nНацісніце, каб даведацца больш"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ваш тэлефон аўтаматычна паспрабуе астыць. Вы можаце па-ранейшаму карыстацца сваім тэлефонам, але ён можа працаваць больш павольна.\n\nПасля таго як ваш тэлефон астыне, ён будзе працаваць у звычайным рэжыме."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Глядзець паэтапную дапамогу"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Адключыце зарадную прыладу"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Узнікла праблема з зарадкай гэтай прылады. Адключыце адаптар сілкавання і праверце, ці не нагрэўся кабель."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Глядзець паэтапную дапамогу"</string>
@@ -1090,6 +1054,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Каб змяніць парадак элементаў кіравання, утрымлівайце і перацягвайце іх"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Усе элементы кіравання выдалены"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Змяненні не захаваны"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Паказаць іншыя праграмы"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Не ўдалося загрузіць элементы кіравання. Праверце, ці не змяніліся налады праграмы \"<xliff:g id="APP">%s</xliff:g>\"."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Сумяшчальныя элементы кіравання недаступныя"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Іншае"</string>
@@ -1107,8 +1072,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Пацвердзіце змяненне для прылады \"<xliff:g id="DEVICE">%s</xliff:g>\""</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Правядзіце пальцам, каб убачыць больш інфармацыі"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загружаюцца рэкамендацыі"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Закрыць гэты сеанс мультымедыя"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Мультымедыя"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Схаваць цяперашні сеанс."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Схаваць"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Узнавіць"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Налады"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактыўна, праверце праграму"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Памылка, паўторная спроба…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не знойдзена"</string>
diff --git a/packages/SystemUI/res/values-be/strings_tv.xml b/packages/SystemUI/res/values-be/strings_tv.xml
index c75a49236e04..37f925e1fae1 100644
--- a/packages/SystemUI/res/values-be/strings_tv.xml
+++ b/packages/SystemUI/res/values-be/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Відарыс у відарысе"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Праграма без назвы)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Закрыць PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Ва ўвесь экран"</string>
<string name="mic_active" msgid="5766614241012047024">"Мікрафон актыўны"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Праграма \"%1$s\" атрымала доступ да мікрафона"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 0b85f2b5f472..d0836e4509fe 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Докоснете отново, за да отворите"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Прекарайте пръст нагоре, за да отключите"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Плъзнете бързо нагоре, за да опитате отново"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Това устройство принадлежи на организацията ви"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Това устройство принадлежи на <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Плъзнете с пръст от иконата, за да използвате телефона"</string>
<string name="voice_hint" msgid="7476017460191291417">"Прекарайте пръст от иконата, за да получите гласова помощ"</string>
<string name="camera_hint" msgid="4519495795000658637">"Плъзнете с пръст от иконата, за да включите камерата"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Възможно е потребителският профил да се наблюдава"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Мрежата може да се наблюдава"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Мрежата може да се наблюдава"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Организацията ви притежава това устройство и може да наблюдава трафика в мрежата"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> притежава това устройство и може да наблюдава трафика в мрежата"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Това устройство принадлежи на организацията ви и е свързано с(ъс) <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Това устройство принадлежи на <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> и е свързано с(ъс) <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Това устройство принадлежи на организацията ви"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Това устройство принадлежи на <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Това устройство принадлежи на организацията ви и е свързано с виртуални частни мрежи (VPN)"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Това устройство принадлежи на <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> и е свързано с виртуални частни мрежи (VPN)"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Организацията ви може да наблюдава трафика в мрежата в служебния ви потребителски профил"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> може да наблюдава трафика в мрежата в служебния ви потребителски профил"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Мрежата може да се наблюдава"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Това устройство е свързано с виртуални частни мрежи (VPN)"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Служебният ви потребителски профил е свързан с(ъс) <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Личният ви потребителски профил е свързан с(ъс) <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Това устройство е свързано с(ъс) <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Управление на устройствата"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Наблюдаване на потр. профил"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Наблюдение на мрежата"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Деактивиране на VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Прекратяване на връзката с VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Преглед на правилата"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Това устройство принадлежи на <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nСистемният ви администратор може да наблюдава и управлява настройките, корпоративния достъп, приложенията, свързаните с устройството данни и информацията за местоположението му.\n\nЗа повече информация се обърнете към него."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Това устройство принадлежи на организацията ви.\n\nСистемният ви администратор може да наблюдава и управлява настройките, корпоративния достъп, приложенията, свързаните с устройството данни и информацията за местоположението му.\n\nЗа повече информация се обърнете към него."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Организацията ви е инсталирала сертифициращ орган на това устройство. Трафикът в защитената ви мрежа може да бъде наблюдаван или променян."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Организацията ви е инсталирала сертифициращ орган в служебния ви потребителски профил. Трафикът в защитената ви мрежа може да бъде наблюдаван или променян."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На това устройство е инсталиран сертифициращ орган. Трафикът в защитената ви мрежа може да бъде наблюдаван или променян."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Тих режим"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Стандартно"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Балонче"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибриране"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибриране и се показва по-долу в секцията с разговори"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да звъни или да вибрира въз основа на настройките за телефона"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да звъни или да вибрира въз основа на настройките за телефона. Разговорите от <xliff:g id="APP_NAME">%1$s</xliff:g> се показват като балончета по подразбиране."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Задържа вниманието ви посредством плаващ пряк път към това съдържание."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Нека системата да определя дали дадено известие да се придружава от звук, или вибриране"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Показва се като плаващо балонче в горната част на секцията с разговори и показва снимката на потребителския профил на заключения екран"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Настройки"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Това приложение се показва върху други приложения на екрана и използва микрофона и камерата."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Настройки"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Това известие бе заглушено от системата."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Това известие бе повишено от системата."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Това известие бе понижено от системата."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Правилно ли е това?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Благодарим ви за отзивите!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ОК"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Контролите за известията за <xliff:g id="APP_NAME">%1$s</xliff:g> са оттворени"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Контролите за известията за <xliff:g id="APP_NAME">%1$s</xliff:g> са затворени"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Разрешаване на известия от този канал"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Редактиране на подредбата на настройките."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Страница <xliff:g id="ID_1">%1$d</xliff:g> от <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Заключен екран"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Разгъване"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Намаляване"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Затваряне"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Настройки"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Преместете надолу с плъзгане, за да отхвърлите"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Меню"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> е в режима „Картина в картината“"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ако не искате <xliff:g id="NAME">%s</xliff:g> да използва тази функция, докоснете, за да отворите настройките, и я изключете."</string>
- <string name="pip_play" msgid="333995977693142810">"Пускане"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Поставяне на пауза"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Към следващия елемент"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Към предишния елемент"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Тел. се изкл. поради загряване"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Телефонът ви вече работи нормално"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Телефонът ви вече работи нормално.\nДокоснете за още информация"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Телефонът ви бе твърде горещ, затова се изключи с цел охлаждане. Вече работи нормално.\n\nТелефонът ви може да стане твърде горещ, ако:\n • използвате приложения, които ползват голям обем ресурси (като например игри, видеосъдържание или приложения за навигация);\n • изтегляте или качвате големи файлове;\n • използвате устройството си при високи температури."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Вижте стъпките, които да предприемете"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Телефонът загрява"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Някои функции са ограничени, докато телефонът се охлажда"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Някои функции са ограничени, докато телефонът се охлажда.\nДокоснете за още информация"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефонът ви автоматично ще направи опит за охлаждане. Пак можете да го използвате, но той може да работи по-бавно.\n\nСлед като се охлади, ще работи нормално."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Вижте стъпките, които да предприемете"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Изключете зарядното устройство"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"При зареждането на това устройство възникна проблем. Изключете захранващия адаптер и внимавайте, тъй като кабелът може да е топъл."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Вижте стъпките, които да предприемете"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Задръжте и плъзнете, за да пренаредите контролите"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Всички контроли са премахнати"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Промените не са запазени"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Преглед на други приложения"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Контролите не се заредиха. Отворете приложението <xliff:g id="APP">%s</xliff:g> и проверете дали настройките му не са променени."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Не са налице съвместими контроли"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Друго"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Потвърдете промяната за <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Прекарайте пръст, за да видите повече"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Препоръките се зареждат"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Затваряне на тази сесия за мултимедия"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Мултимедия"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Скриване на текущата сесия."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Скриване"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Възобновяване"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, проверете прилож."</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Грешка. Извършва се нов опит…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не е намерено"</string>
diff --git a/packages/SystemUI/res/values-bg/strings_tv.xml b/packages/SystemUI/res/values-bg/strings_tv.xml
index 6d2b842117f1..f322079f77a1 100644
--- a/packages/SystemUI/res/values-bg/strings_tv.xml
+++ b/packages/SystemUI/res/values-bg/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Картина в картината"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Програма без заглавие)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Затваряне на PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Цял екран"</string>
<string name="mic_active" msgid="5766614241012047024">"Микрофонът е активен"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s осъществи достъп до микрофона ви"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index e59fca2df04a..a584955b534d 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -76,7 +76,7 @@
<string name="learn_more" msgid="4690632085667273811">"আরও জানুন"</string>
<string name="compat_mode_on" msgid="4963711187149440884">"স্ক্রীণ পূরণ করতে জুম করুন"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ফুল স্ক্রিন করুন"</string>
- <string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্রিনশট"</string>
+ <string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্রিনশট নিন"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"একটি ছবি পাঠানো হয়েছে"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"খোলার জন্য আবার আলতো চাপুন"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"খোলার জন্য উপরে সোয়াইপ করুন"</string>
<string name="keyguard_retry" msgid="886802522584053523">"আবার চেষ্টা করতে উপরের দিকে সোয়াইপ করুন"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"এই ডিভাইসটি <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>-এর"</string>
<string name="phone_hint" msgid="6682125338461375925">"ফোনের জন্য আইকন থেকে সোয়াইপ করুন"</string>
<string name="voice_hint" msgid="7476017460191291417">"ভয়েস সহায়তার জন্য আইকন থেকে সোয়াইপ করুন"</string>
<string name="camera_hint" msgid="4519495795000658637">"ক্যামেরার জন্য আইকন থেকে সোয়াইপ করুন"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"প্রোফাইল পর্যবেক্ষণ করা হতে পারে"</string>
<string name="vpn_footer" msgid="3457155078010607471">"নেটওয়ার্ক নিরীক্ষণ করা হতে পারে"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"নেটওয়ার্ক নিরীক্ষণ করা হতে পারে"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের এবং এরা ডিভাইসের নেটওয়ার্ক ট্রাফিক মনিটর করতে পারে"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> এই ডিভাইসের মালিক এবং এটির নেটওয়ার্ক ট্রাফিক মনিটর করতে পারে"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের এবং <xliff:g id="VPN_APP">%1$s</xliff:g>-এ কানেক্ট করা আছে"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"এই ডিভাইস <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-এর এবং <xliff:g id="VPN_APP">%2$s</xliff:g>-এ কানেক্ট করা আছে"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"এই ডিভাইসটি <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-এর"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের এবং একাধিক VPN-এ কানেক্ট করা আছে"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"এই ডিভাইস <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-এর এবং একাধিক VPN-এ কানেক্ট করা আছে"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"আপনার প্রতিষ্ঠান আপনার কর্মস্থলের প্রোফাইলের নেটওয়ার্ক ট্রাফিকে নজর রাখতে পারে"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> আপনার কর্মস্থলের প্রোফাইলের নেটওয়ার্ক ট্রাফিকে নজর রাখতে পারে"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"নেটওয়ার্কের উপরে নজর রাখা হতে পারে"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"এই ডিভাইস একাধিক VPN-এ কানেক্ট করা আছে"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"<xliff:g id="VPN_APP">%1$s</xliff:g>-এ আপনার অফিস প্রোফাইল কানেক্ট করা রয়েছে"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"আপনার ব্যক্তিগত প্রোফাইল <xliff:g id="VPN_APP">%1$s</xliff:g>-এ কানেক্ট করা আছে"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"এই ডিভাইস <xliff:g id="VPN_APP">%1$s</xliff:g>-এ কানেক্ট করা আছে"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ডিভাইসের পরিচালনা"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"প্রোফাইল দেখরেখ করা"</string>
<string name="monitoring_title" msgid="4063890083735924568">"নেটওয়ার্ক নিরীক্ষণ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN অক্ষম করুন"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN এর সংযোগ বিচ্ছিন্ন করুন"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"নীতিগুলি দেখুন"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"এই ডিভাইসটি <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-এর।\n\nআপনার আইটি অ্যাডমিন এই ডিভাইসের সেটিংস, কর্পোরেট অ্যাক্সেস, অ্যাপ, ডিভাইসের সাথে সম্পর্কিত ডেটা এবং ডিভাইসের লোকেশন সম্পর্কিত ডেটা মনিটর ও ম্যানেজ করতে পারে।\n\nআরও তথ্যের জন্য আপনার আইটি অ্যাডমিনের সাথে যোগাযোগ করুন।"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের।\n\nআপনার আইটি অ্যাডমিন এই ডিভাইসের সেটিংস, কর্পোরেট অ্যাক্সেস, অ্যাপ, ডিভাইসের সাথে সম্পর্কিত ডেটা এবং ডিভাইসের লোকেশন সম্পর্কিত ডেটা মনিটর ও ম্যানেজ করতে পারে।\n\nআরও তথ্যের জন্য আপনার আইটি অ্যাডমিনের সাথে যোগাযোগ করুন।"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"আপনার প্রতিষ্ঠান আপনার অফিস প্রোফাইলে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করেছে।আপনার সুরক্ষিত নেটওয়ার্ক ট্রাফিক নিরীক্ষণ বা পরিবর্তন করা হতে পারে।"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"আপনার প্রতিষ্ঠান আপনার অফিস প্রোফাইলে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করেছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"এই ডিভাইসে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করা আছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"সাইলেন্ট"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ডিফল্ট"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"বাবল"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"অটোমেটিক"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"আওয়াজ করবে না বা ভাইব্রেট হবে না"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"আওয়াজ করবে না বা ভাইব্রেট হবে না এবং কথোপকথন বিভাগের নিচের দিকে দেখা যাবে"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ফোনের সেটিংস অনুযায়ী ফোন রিং বা ভাইব্রেট হতে পারে"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ফোনের সেটিংস অনুযায়ী ফোন রিং বা ভাইব্রেট হতে পারে। <xliff:g id="APP_NAME">%1$s</xliff:g>-এর কথোপকথন সাধারণত বাবলের মতো দেখাবে।"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ফ্লোটিং শর্টকাট ব্যবহার করে এই কন্টেন্টে আপনার দৃষ্টি আকর্ষণ করে রাখে।"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"এই বিজ্ঞপ্তি এলে ডিভাইস আওয়াজ করবে না ভাইব্রেট করবে তা সিস্টেমকে সেট করতে দিন"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"কথোপকথন বিভাগের উপরে ভাসমান বাবলের মতো দেখা যাবে, লক স্ক্রিনে প্রোফাইল ছবি দেখাবে"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"সেটিংস"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"অগ্রাধিকার"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"এই অ্যাপটি স্ক্রিনে অন্যান্য অ্যাপের উপরে দেখানো হচ্ছে এবং মাইক্রোফোন ও ক্যামেরা ব্যবহার করছে।"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"সেটিংস"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ঠিক আছে"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"সিস্টেম এই বিজ্ঞপ্তি মিউট করে রেখেছিল।"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"সিস্টেম এই বিজ্ঞপ্তি দেখাতে চেয়েছে।"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"সিস্টেম এই বিজ্ঞপ্তি বন্ধ রেখেছে।"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"এটি কি সঠিক ছিল?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"মতামতের জন্য ধন্যবাদ!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"বুঝেছি"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> খোলা থাকলে বিজ্ঞপ্তি নিয়ন্ত্রণ"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> বন্ধ থাকলে বিজ্ঞপ্তি নিয়ন্ত্রণ"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"এই চ্যানেল থেকে বিজ্ঞপ্তি আসতে দেয়"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ক্রম বা সেটিংস সম্পাদনা করুন৷"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>টির মধ্যে <xliff:g id="ID_1">%1$d</xliff:g> নং পৃষ্ঠা"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্রিন"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"বড় করুন"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"ছোটো করুন"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"বন্ধ করুন"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"সেটিংস"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"খারিজ করতে নিচের দিকে টেনে আনুন"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"মেনু"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"ছবির-মধ্যে-ছবি তে <xliff:g id="NAME">%s</xliff:g> আছেন"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> কে এই বৈশিষ্ট্যটি ব্যবহার করতে দিতে না চাইলে ট্যাপ করে সেটিংসে গিয়ে সেটি বন্ধ করে দিন।"</string>
- <string name="pip_play" msgid="333995977693142810">"চালান"</string>
- <string name="pip_pause" msgid="1139598607050555845">"বিরাম দিন"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"এগিয়ে যাওয়ার জন্য এড়িয়ে যান"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"পিছনে যাওয়ার জন্য এড়িয়ে যান"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"আপনার ফোন গরম হওয়ার জন্য বন্ধ হয়ে গেছে"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"আপনার ফোন এখন ঠিক-ঠাক চলছে"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"আপনার ফোন এখন ভালভাবে কাজ করছে।\nআরও তথ্যের জন্য ট্যাপ করুন"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"আপনার ফোন খুব বেশি গরম হয়েছিল বলে ঠাণ্ডা হওয়ার জন্য বন্ধ হয়ে গেছে। আপনার ফোন ঠিক-ঠাক ভাবে চলছে না।\n\nআপনার ফোন খুব বেশি গরম হয়ে যাবে যদি আপনি:\n •এমন অ্যাপ ব্যবহার করলে যেটি আপনার ডিভাইসের রিসোর্স বেশি ব্যবহার করে (যেমন গেমিং, ভিডিও বা নেভিগেশন অ্যাপ)\n • বড় ফাইল ডাউনলোড বা আপলোড করলে\n • বেশি তাপমাত্রায় আপনার ফোন ব্যবহার করলে"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ডিভাইস রক্ষণাবেক্ষণের ধাপগুলি দেখুন"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ফোনটি গরম হচ্ছে"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ফোনটি ঠাণ্ডা হওয়ার সময় কিছু বৈশিষ্ট্য সীমিত হতে পারে"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ফোন ঠাণ্ডা না হওয়া পর্যন্ত কিছু ফিচার কাজ করে না।\nআরও তথ্যের জন্য ট্যাপ করুন"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"আপনার ফোনটি নিজে থেকেই ঠাণ্ডা হওয়ার চেষ্টা করবে৷ আপনি তবুও আপনার ফোন ব্যবহার করতে পারেন, কিন্তু এটি একটু ধীরে চলতে পারে৷\n\nআপনার ফোনটি পুরোপুরি ঠাণ্ডা হয়ে গেলে এটি স্বাভাবিকভাবে চলবে৷"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ডিভাইস রক্ষণাবেক্ষণের ধাপগুলি দেখুন"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"চার্জার আনপ্লাগ করুন"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"এই ডিভাইস চার্জ করার সময় সমস্যা হয়েছে। চার্জিং কেবলটি হয়ত গরম হয়ে গেছে, পাওয়ার অ্যাডাপ্টারটি আনপ্লাগ করুন।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"কী করতে হবে ধাপে ধাপে দেখুন"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"কন্ট্রোলগুলিকে আবার সাজানোর জন্য ধরে রেখে টেনে আনুন"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"সমস্ত কন্ট্রোল সরানো হয়েছে"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"পরিবর্তন সেভ করা হয়নি"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"অন্যান্য অ্যাপ দেখুন"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"কন্ট্রোল লোড করা যায়নি। অ্যাপ সেটিংসে কোনও পরিবর্তন করা হয়েছে কিনা তা ভাল করে দেখে নিতে <xliff:g id="APP">%s</xliff:g> অ্যাপ চেক করুন।"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"মানানসই কন্ট্রোল উপলভ্য নেই"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"অন্য"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g>-এর জন্য পরিবর্তন কনফার্ম করুন"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"আরও দেখতে সোয়াইপ করুন"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"সাজেশন লোড করা হচ্ছে"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"এই মিডিয়া সেশন বেছে নিন"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"বর্তমান সেশন লুকান।"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"লুকান"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"বন্ধ আছে, অ্যাপ চেক করুন"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"সমস্যা, আবার চেষ্টা করা হচ্ছে…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"খুঁজে পাওয়া যায়নি"</string>
diff --git a/packages/SystemUI/res/values-bn/strings_tv.xml b/packages/SystemUI/res/values-bn/strings_tv.xml
index 795314c4b15c..56ca0233ae45 100644
--- a/packages/SystemUI/res/values-bn/strings_tv.xml
+++ b/packages/SystemUI/res/values-bn/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ছবির-মধ্যে-ছবি"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(শিরোনামহীন প্রোগ্রাম)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP বন্ধ করুন"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"পূর্ণ স্ক্রিন"</string>
<string name="mic_active" msgid="5766614241012047024">"মাইক্রোফোন চালু আছে"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s আপনার ডিভাইসের মাইক্রোফোন অ্যাক্সেস করেছে"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 7cd106e316cb..4c872d22054f 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -456,10 +456,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Dodirnite ponovo da otvorite"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Prevucite da otvorite"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Prevucite prema gore da pokušate ponovo"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada vašoj organizaciji"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Prevucite preko ikone da otvorite telefon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Prevucite preko ikone za glasovnu pomoć"</string>
<string name="camera_hint" msgid="4519495795000658637">"Prevucite od ikone da otvorite kameru"</string>
@@ -526,33 +524,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil može biti nadziran"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Mreža može biti nadzirana"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Mreža može biti nadzirana"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Vaša organizacija je vlasnik ovog uređaja i može nadzirati mrežni saobraćaj"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> upravlja ovim uređajem i može nadzirati mrežni saobraćaj"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Ovaj uređaj pripada vašoj organizaciji i povezan je s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i povezan je s aplikacijom <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Ovaj uređaj pripada vašoj organizaciji"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Ovaj uređaj pripada vašoj organizaciji i povezan je s VPN-ovima"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i povezan je VPN-ovima"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Vaša organizacija može pratiti mrežni saobraćaj na vašem profilu."</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> može pratiti mrežni saobraćaj na vašem radnom profilu"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Mreža može biti nadzirana"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Ovaj uređaj je povezan VPN-ovima"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Vaš radni profil je povezan s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Vaš lični profil je povezan s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Ovaj uređaj je povezan s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Upravljanje uređajem"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Praćenje profila"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Praćenje mreže"</string>
@@ -562,10 +548,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Isključi VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Prekini VPN vezu"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Prikaži pravila"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nVaš IT administrator može nadzirati postavke, korporativni pristup, aplikacije, podatke povezane s vašim uređajem i informacije o lokaciji uređaja te njima upravljati.\n\nZa više informacija kontaktirajte IT administratora."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Ovaj uređaj pripada vašoj organizaciji.\n\nVaš IT administrator može nadzirati postavke, korporativni pristup, aplikacije, podatke povezane s vašim uređajem i informacije o lokaciji uređaja te njima upravljati.\n\nZa više informacija kontaktirajte IT administratora."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Vaša organizacija je instalirala CA certifikat na ovom uređaju. Vaš saobraćaj preko sigurne mreže može se pratiti."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Vaša organizacija je instalirala CA certifikat na vašem radnom profilu. Vaš saobraćaj preko sigurne mreže može se pratiti."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"CA certifikat je instaliran na ovom uređaju. Vaš saobraćaj preko sigurne mreže može se pratiti."</string>
@@ -644,9 +628,7 @@
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Isključi zvuk"</string>
<string name="qs_status_phone_vibrate" msgid="7055409506885541979">"Na telefonu je uključena vibracija"</string>
<string name="qs_status_phone_muted" msgid="3763664791309544103">"Zvuk na telefonu je isključen"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for volume_stream_content_description_unmute (7729576371406792977) -->
- <skip />
+ <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da uključite zvukove."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite za postavljanje vibracije. Zvukovi usluga pristupačnosti mogu biti isključeni."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da isključite zvuk. Zvukovi usluga pristupačnosti mogu biti isključeni."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dodirnite da postavite vibraciju."</string>
@@ -732,15 +714,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Nečujno"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Zadano"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Oblačić"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka ili vibracije"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Bez zvuka ili vibracije i pojavljuje se pri dnu odjeljka za razgovor"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Može zvoniti ili vibrirati na osnovu postavki vašeg telefona"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Može zvoniti ili vibrirati na osnovu postavki vašeg telefona. Razgovori iz oblačića u aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> kao zadana opcija."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Privlači vašu pažnju pomoću plutajuće prečice do ovog sadržaja."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sistem odluči treba li se ovo obavještenje oglasiti zvukom ili vibracijom"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Prikazuje se na vrhu odjeljka za razgovor, pojavljuje se kao plutajući oblačić, prikazuje sliku profila na zaključanom ekranu"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Postavke"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritetni"</string>
@@ -761,18 +741,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ova aplikacija prekriva druge aplikacije na ekranu i koristi mikrofon i kameru."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Postavke"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Uredu"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Sistem je utišao ovo obavještenje."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Sistem je pomovirao ovo obavještenje."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Sistem je umanjio značaj ovog obavještenja."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Je li ovo bilo tačno?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Hvala na povratnim informacijama!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"UREDU"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Otvorene su kontrole obavještenja za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Zatvorene su kontrole obavještenja za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Dozvoli obavještenja s ovog kanala"</string>
@@ -947,26 +921,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Urediti raspored postavki."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključavanje ekrana"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Proširi"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimiziraj"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Zatvori"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Postavke"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Povucite prema dolje da odbacite"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Meni"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> je u načinu priakza Slika u slici"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ako ne želite da <xliff:g id="NAME">%s</xliff:g> koristi ovu funkciju, dodirnite da otvorite postavke i isključite je."</string>
- <string name="pip_play" msgid="333995977693142810">"Reproduciraj"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pauziraj"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Preskoči na sljedeći"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Preskoči na prethodni"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefon se isključio zbog pregrijavanja"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Vaš telefon sada radi normalno"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Vaš telefon sada radi normalno.\nDodirnite za više informacija"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Vaš telefon se pregrijao, pa se isključio da se ohladi. Telefon sada radi normalno.\n\nTelefon se može pregrijati ako:\n • Koristite aplikacije koje troše puno resursa (kao što su aplikacije za igranje, videozapise ili navigaciju)\n • Preuzimate ili otpremate velike fajlove\n • Koristite telefon na visokim temperaturama"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Pogledajte korake za zaštitu"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon se pregrijava"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Neke funkcije su ograničene dok se telefon hladi"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Neke funkcije su ograničene dok se telefon hladi.\nDodirnite za više informacija"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Vaš telefon će se automatski pokušati ohladiti. I dalje možete koristi telefon, ali će možda raditi sporije.\n\nNakon što se ohladi, telefon će normalno raditi."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pogledajte korake za zaštitu"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Isključite punjač"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Došlo je do problema prilikom punjenja ovog uređaja. Pažljivo isključite adapter za napajanje jer je moguće da je kabl vruć."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Prikaz koraka za zaštitu"</string>
@@ -1086,8 +1048,9 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Držite i prevucite da preuredite kontrole"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Sve kontrole su uklonjene"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Promjene nisu sačuvane"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Prikaži druge aplikacije"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Učitavanje kontrola nije uspjelo. Provjerite aplikaciju <xliff:g id="APP">%s</xliff:g> da se uvjerite da postavke aplikacije nisu izmijenjene."</string>
- <string name="controls_favorite_load_none" msgid="7687593026725357775">"Kompatibilne kontrole su nedostupne"</string>
+ <string name="controls_favorite_load_none" msgid="7687593026725357775">"Kompatibilne kontrole nisu dostupne"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Drugo"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"Dodajte u kontrole uređaja"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"Dodaj"</string>
@@ -1103,8 +1066,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Potvrdite promjenu za uređaj <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Prevucite da vidite više"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Zatvori ovu medijsku sesiju"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte trenutnu sesiju."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sakrij"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, vidite aplikaciju"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Greška, ponovni pokušaj…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
diff --git a/packages/SystemUI/res/values-bs/strings_tv.xml b/packages/SystemUI/res/values-bs/strings_tv.xml
index 1dd803516841..acd93d657bac 100644
--- a/packages/SystemUI/res/values-bs/strings_tv.xml
+++ b/packages/SystemUI/res/values-bs/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Slika u slici"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program bez naslova)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Zatvori PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Cijeli ekran"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon je aktivan"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Aplikacija %1$s je pristupila vašem mikrofonu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index f461c312b9af..7267c31b1f36 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -21,7 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4811759950673118541">"IU del sistema"</string>
<string name="status_bar_clear_all_button" msgid="2491321682873657397">"Esborra"</string>
- <string name="status_bar_no_notifications_title" msgid="7812479124981107507">"Cap notificació"</string>
+ <string name="status_bar_no_notifications_title" msgid="7812479124981107507">"No hi ha cap notificació"</string>
<string name="status_bar_ongoing_events_title" msgid="3986169317496615446">"Continu"</string>
<string name="status_bar_latest_events_title" msgid="202755896454005436">"Notificacions"</string>
<string name="battery_low_title" msgid="6891106956328275225">"És possible que la bateria s\'esgoti aviat"</string>
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Torna a tocar per obrir-la."</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Llisca cap amunt per obrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Llisca cap a dalt per tornar-ho a provar"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Aquest dispositiu pertany a la teva organització"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Aquest dispositiu pertany a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Llisca des de la icona per obrir el telèfon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Llisca des de la icona per obrir l\'assistent de veu"</string>
<string name="camera_hint" msgid="4519495795000658637">"Llisca des de la icona per obrir la càmera"</string>
@@ -519,37 +517,25 @@
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Esborra totes les notificacions silencioses"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificacions pausades pel mode No molestis"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Comença ara"</string>
- <string name="empty_shade_text" msgid="8935967157319717412">"Cap notificació"</string>
+ <string name="empty_shade_text" msgid="8935967157319717412">"No hi ha cap notificació"</string>
<string name="profile_owned_footer" msgid="2756770645766113964">"El perfil es pot supervisar"</string>
<string name="vpn_footer" msgid="3457155078010607471">"És possible que la xarxa estigui supervisada."</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"És possible que la xarxa estigui supervisada"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"La teva organització és propietària del dispositiu i és possible que supervisi el trànsit de xarxa"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> és propietària d\'aquest dispositiu i és possible que supervisi el trànsit de xarxa"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Aquest dispositiu pertany a la teva organització i està connectat a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Aquest dispositiu pertany a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i està connectat a <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Aquest dispositiu pertany a la teva organització"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Aquest dispositiu pertany a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Aquest dispositiu pertany a la teva organització i està connectat a xarxes VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Aquest dispositiu pertany a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i està connectat a xarxes VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"És possible que la teva organització supervisi el trànsit de xarxa al teu perfil de treball"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"És possible que <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> supervisi el trànsit de xarxa del teu perfil de treball"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"És possible que la xarxa estigui supervisada"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Aquest dispositiu està connectat a xarxes VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"El teu perfil de treball està connectat a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"El teu perfil personal està connectat a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Aquest dispositiu està connectat a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gestió del dispositiu"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Supervisió del perfil"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Supervisió de la xarxa"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Desactiva la VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Desconnecta la VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Consulta les polítiques"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"El dispositiu pertany a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nL\'administrador de TI pot supervisar i gestionar la configuració, l\'accés corporatiu, les aplicacions, les dades associades al dispositiu i la informació d\'ubicació.\n\nPer obtenir més informació, contacta amb l\'administrador de TI."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"El dispositiu pertany a la teva organització.\n\nL\'administrador de TI pot supervisar i gestionar la configuració, l\'accés corporatiu, les aplicacions, les dades associades al dispositiu i la informació d\'ubicació.\n\nPer obtenir més informació, contacta amb l\'administrador de TI."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"La teva organització ha instal·lat una autoritat de certificació en aquest dispositiu. És possible que el trànsit a la xarxa segura se supervisi o es modifiqui."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"La teva organització ha instal·lat una autoritat de certificació al teu perfil de treball. És possible que el trànsit de xarxa segura se supervisi o es modifiqui."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"S\'ha instal·lat una autoritat de certificació en aquest dispositiu. És possible que el trànsit de xarxa segura se supervisi o es modifiqui."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silenci"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bombolla"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automàtic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sense so ni vibració"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sense so ni vibració i es mostra més avall a la secció de converses"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Pot sonar o vibrar en funció de la configuració del telèfon"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pot sonar o vibrar en funció de la configuració del telèfon. Les converses de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> es mostren com a bombolles de manera predeterminada."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Atrau la teva atenció amb una drecera flotant a aquest contingut."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fes que el sistema determini si aquesta notificació ha d\'emetre un so o una vibració"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Es mostra com a bombolla flotant a la part superior de la secció de converses i mostra la foto de perfil a la pantalla de bloqueig"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configuració"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritat"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Aquesta aplicació es mostra sobre altres aplicacions a la pantalla i utilitza el micròfon i la càmera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Configuració"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"D\'acord"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"El sistema ha silenciat aquesta notificació."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"El sistema ha augmentat el nivell d\'aquesta notificació."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"El sistema ha disminuït el nivell d\'aquesta notificació."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"La informació ha estat correcta?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Gràcies pels suggeriments."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"D\'acord"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"S\'han obert els controls de notificació per a <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"S\'han tancat els controls de notificació per a <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Permet les notificacions d\'aquest canal"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Edita l\'ordre de la configuració."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pàgina <xliff:g id="ID_1">%1$d</xliff:g> (<xliff:g id="ID_2">%2$d</xliff:g> en total)"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueig"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Desplega"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimitza"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Tanca"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Configuració"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Arrossega cap avall per ignorar-ho"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menú"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> està en pantalla en pantalla"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Si no vols que <xliff:g id="NAME">%s</xliff:g> utilitzi aquesta funció, toca per obrir la configuració i desactiva-la."</string>
- <string name="pip_play" msgid="333995977693142810">"Reprodueix"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Posa en pausa"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Ves al següent"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Torna a l\'anterior"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telèfon apagat per la calor"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Ara el telèfon funciona de manera normal"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Ara el telèfon funciona correctament.\nToca per obtenir més informació"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"El telèfon s\'havia sobreescalfat i s\'ha apagat per refredar-se. Ara funciona amb normalitat.\n\nEs pot sobreescalfar si:\n • utilitzes aplicacions que consumeixen molts recursos (com ara, videojocs, vídeos o aplicacions de navegació);\n • baixes o penges fitxers grans;\n • l\'utilitzes amb temperatures altes."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Mostra els passos de manteniment"</string>
<string name="high_temp_title" msgid="2218333576838496100">"El telèfon s\'està escalfant"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Algunes funcions estaran limitades mentre el telèfon es refreda"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Algunes funcions estan limitades mentre el telèfon es refreda.\nToca per obtenir més informació"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"El telèfon provarà de refredar-se automàticament. Podràs continuar utilitzant-lo, però és possible que funcioni més lentament.\n\nUn cop s\'hagi refredat, funcionarà amb normalitat."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Mostra els passos de manteniment"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Desconnecta el carregador"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"No es pot carregar el dispositiu. Desconnecta l\'adaptador de corrent amb compte, ja que el cable podria estar calent."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Mostra els pasos de manteniment"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Mantén premut i arrossega per reorganitzar els controls"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"S\'han suprimit tots els controls"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Els canvis no s\'han desat"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Mostra altres aplicacions"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"No s\'han pogut carregar els controls. Consulta l\'aplicació <xliff:g id="APP">%s</xliff:g> per assegurar-te que la configuració de l\'aplicació no hagi canviat."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Els controls compatibles no estan disponibles"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Altres"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirma el canvi per a <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Llisca per veure\'n més"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregant les recomanacions"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Tanca aquesta sessió multimèdia"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Multimèdia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Amaga la sessió actual."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Amaga"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reprèn"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuració"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactiu; comprova l\'aplicació"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Error. S\'està tornant a provar…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No s\'ha trobat"</string>
diff --git a/packages/SystemUI/res/values-ca/strings_tv.xml b/packages/SystemUI/res/values-ca/strings_tv.xml
index 44cc9410ed0b..c41d5714e60e 100644
--- a/packages/SystemUI/res/values-ca/strings_tv.xml
+++ b/packages/SystemUI/res/values-ca/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Pantalla en pantalla"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programa sense títol)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Tanca PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Pantalla completa"</string>
<string name="mic_active" msgid="5766614241012047024">"Micròfon actiu"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ha accedit al teu micròfon"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index e5583a93a1ce..29b5281f689f 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -458,10 +458,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Oznámení otevřete opětovným klepnutím"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Otevřete přejetím prstem nahoru"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Přejetím nahoru to zkusíte znovu"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Toto zařízení patří vaší organizaci"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Toto zařízení patří organizaci <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefon otevřete přejetím prstem od ikony"</string>
<string name="voice_hint" msgid="7476017460191291417">"Hlasovou asistenci otevřete přejetím prstem od ikony"</string>
<string name="camera_hint" msgid="4519495795000658637">"Fotoaparát otevřete přejetím prstem od ikony"</string>
@@ -529,33 +527,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil může být monitorován"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Síť může být sledována"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Síť může být monitorována"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Toto zařízení vlastní vaše organizace, která může sledovat síťový provoz"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Toto zařízení spravuje organizace <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>, která může sledovat síťový provoz"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Toto zařízení patří vaší organizaci a je připojené k síti <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Toto zařízení patří organizaci <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> a je připojené k síti <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Toto zařízení patří vaší organizaci"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Toto zařízení patří organizaci <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Toto zařízení patří vaší organizaci a je připojené k sítím VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Toto zařízení patří organizaci <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> a je připojené k sítím VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Organizace může ve vašem pracovním profilu sledovat síťový provoz"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> může ve vašem pracovním profilu sledovat síťový provoz"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Síť může být sledována"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Toto zařízení je připojené k sítím VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Váš pracovní profil je připojen k síti <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Váš osobní profil je připojený k síti <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Toto zařízení je připojené k síti <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Správa zařízení"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Monitoring profilu"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Sledování sítě"</string>
@@ -565,10 +551,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Deaktivovat VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Odpojit VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Zobrazit zásady"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Toto zařízení patří organizaci <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nVáš administrátor IT může sledovat a spravovat nastavení, firemní přístup, aplikace, data přidružená k tomuto zařízení a jeho polohu.\n\nDalší informace vám poskytne váš administrátor IT."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Toto zařízení patří vaší organizaci\n\nVáš administrátor IT může sledovat a spravovat nastavení, firemní přístup, aplikace, data přidružená k tomuto zařízení a jeho polohu.\n\nDalší informace vám poskytne váš administrátor IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizace do tohoto zařízení nainstalovala certifikační autoritu. Zabezpečený síťový provoz může být sledován nebo upravován."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizace do vašeho pracovního profilu nainstalovala certifikační autoritu. Zabezpečený síťový provoz může být sledován nebo upravován."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V zařízení je nainstalována certifikační autorita. Zabezpečený síťový provoz může být sledován nebo upravován."</string>
@@ -733,15 +717,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Ticho"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Výchozí"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bublina"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Žádný zvuk ani vibrace"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Žádný zvuk ani vibrace a zobrazovat níže v sekci konverzací"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Může vyzvánět nebo vibrovat v závislosti na nastavení telefonu"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Může vyzvánět nebo vibrovat v závislosti na nastavení telefonu. Konverzace z aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> ve výchozím nastavení bublají."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Přitahuje pozornost pomocí plovoucí zkratky k tomuto obsahu."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nechat systém rozhodnout, zda má toto oznámení vydat zvuk či zavibrovat"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Zobrazuje se v horní části sekce konverzace a má podobu plovoucí bubliny, zobrazuje profilovou fotku na obrazovce uzamčení"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Nastavení"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priorita"</string>
@@ -762,18 +744,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Tato aplikace se zobrazuje přes ostatní aplikace na obrazovce a využívá mikrofon a fotoaparát."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Nastavení"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Toto oznámení systém ztlumil."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"U tohoto oznámení systém zvýšil prioritu."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"U tohoto oznámení systém snížil prioritu."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Udělal to správně?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Děkujeme za zpětnou vazbu."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Ovládací prvky oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byly otevřeny"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Ovládací prvky oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byly zavřeny"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Povolit oznámení z tohoto kanálu"</string>
@@ -950,26 +926,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Upravit pořadí nastavení."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Stránka <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Obrazovka uzamčení"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Rozbalit"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimalizovat"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Zavřít"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Nastavení"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Nápovědu zavřete přetažením dolů"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Nabídka"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"Aplikace <xliff:g id="NAME">%s</xliff:g> je v režimu obraz v obraze"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Pokud nechcete, aby aplikace <xliff:g id="NAME">%s</xliff:g> tuto funkci používala, klepnutím otevřete nastavení a funkci vypněte."</string>
- <string name="pip_play" msgid="333995977693142810">"Přehrát"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pozastavit"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Přeskočit na další"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Přeskočit na předchozí"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefon se vypnul z důvodu zahřátí"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Nyní telefon funguje jako obvykle."</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Nyní telefon funguje jako obvykle.\nKlepnutím zobrazíte další informace"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon byl příliš zahřátý, proto se vypnul, aby vychladl. Nyní telefon funguje jako obvykle.\n\nTelefon se může příliš zahřát v těchto případech:\n • používání náročných aplikací (např. her, videí nebo navigace),\n • stahování nebo nahrávání velkých souborů,\n • používání telefonu při vysokých teplotách."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Zobrazit pokyny, co dělat"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon se zahřívá"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Některé funkce jsou při chladnutí omezeny"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Některé funkce jsou při chladnutí telefonu omezeny.\nKlepnutím zobrazíte další informace"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon se automaticky pokusí vychladnout. Lze jej nadále používat, ale může být pomalejší.\n\nAž telefon vychladne, bude fungovat normálně."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Zobrazit pokyny, co dělat"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Odpojte nabíječku"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Při nabíjení zařízení došlo k problému. Odpojte napájecí adaptér (dávejte pozor, kabel může být horký)."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Zobrazit pokyny, co dělat"</string>
@@ -1090,6 +1054,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Ovládací prvky můžete uspořádat podržením a přetažením"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Všechny ovládací prvky byly odstraněny"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Změny nebyly uloženy"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Zobrazit další aplikace"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Ovládací prvky se nepodařilo načíst. V aplikaci <xliff:g id="APP">%s</xliff:g> zkontrolujte, zda se nezměnilo nastavení."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kompatibilní ovládání není k dispozici"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Jiné"</string>
@@ -1107,8 +1072,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Ověřte změnu v zařízení <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Přejetím prstem zobrazíte další položky"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítání doporučení"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Zavřít tuto mediální relaci"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Skrýt aktuální relaci."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skrýt"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Pokračovat"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavení"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivní, zkontrolujte aplikaci"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Chyba. Nový pokus…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nenalezeno"</string>
diff --git a/packages/SystemUI/res/values-cs/strings_tv.xml b/packages/SystemUI/res/values-cs/strings_tv.xml
index 0494bc12b469..2d95d46a078f 100644
--- a/packages/SystemUI/res/values-cs/strings_tv.xml
+++ b/packages/SystemUI/res/values-cs/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Obraz v obraze"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Bez názvu)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Ukončit obraz v obraze (PIP)"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Celá obrazovka"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon je aktivní"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Aplikace %1$s použila mikrofon"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1dbd5f527f8d..dff770d49e28 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tryk igen for at åbne"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Stryg opad for at åbne"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Stryg opad for at prøve igen"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Denne enhed tilhører din organisation"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Denne enhed tilhører <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Stryg fra telefonikonet"</string>
<string name="voice_hint" msgid="7476017460191291417">"Stryg fra mikrofonikonet"</string>
<string name="camera_hint" msgid="4519495795000658637">"Stryg fra kameraikonet"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profilen kan overvåges"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Netværket kan være overvåget"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Netværket kan være overvåget"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Din organisation ejer denne enhed og overvåger muligvis netværkstrafikken"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ejer denne enhed og overvåger muligvis netværkstrafikken"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Denne enhed tilhører din organisation og har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Denne enhed tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> og har forbindelse til <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Denne enhed tilhører din organisation"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Denne enhed tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Denne enhed tilhører din organisation og har forbindelse til VPN-netværk"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Denne enhed tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> og har forbindelse til VPN-netværk"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Din organisation kan overvåge netværkstrafikken på din arbejdsprofil"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kan overvåge netværkstrafik på din arbejdsprofil"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Netværket kan være overvåget"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Denne enhed har forbindelse til VPN-netværk"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Din arbejdsprofil har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Din personlige profil har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Denne enhed har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Administration af enheder"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profilovervågning"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Overvågning af netværk"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Deaktiver VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Afbryd VPN-forbindelse"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Se politikker"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Denne enhed tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nDin it-administrator kan overvåge og administrere indstillinger, virksomhedsadgang, apps, data, der er tilknyttet din enhed, og din enheds placeringsdata.\n\nKontakt din it-administrator for at få mere at vide."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Denne enhed tilhører din organisation.\n\nDin it-administrator kan overvåge og administrere indstillinger, virksomhedsadgang, apps, data, der er tilknyttet din enhed, og din enheds placeringsdata.\n\nKontakt din it-administrator for at få mere at vide."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Din organisation har installeret et nøglecenter på denne enhed. Din sikre netværkstrafik kan overvåges eller ændres."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Din organisation har installeret et nøglecenter på din arbejdsprofil. Din sikre netværkstrafik kan overvåges eller ændres."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Der er installeret et nøglecenter på denne enhed. Din sikre netværkstrafik kan overvåges eller ændres."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Lydløs"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Boble"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ingen lyd eller vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ingen lyd eller vibration, og den vises længere nede i samtalesektionen"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan ringe eller vibrere baseret på telefonens indstillinger"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan ringe eller vibrere baseret på telefonens indstillinger. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> vises som standard i bobler."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Fastholder din opmærksomhed med en svævende genvej til indholdet."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Få systemet til at afgøre, om denne notifikation skal vibrere eller afspille en lyd"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Vises øverst i samtalesektionen, som en svævende boble og med profilbillede på låseskærmen"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Indstillinger"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Denne app vises over andre apps på din skærm og anvender mikrofonen og kameraet."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Indstillinger"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Denne notifikation blev sat på lydløs af systemet."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Denne notifikation blev rykket op af systemet."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Denne notifikation blev rykket ned af systemet."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Var dette korrekt?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Tak for din feedback"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Styring af notifikationer for <xliff:g id="APP_NAME">%1$s</xliff:g> blev åbnet"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Styring af notifikationer for <xliff:g id="APP_NAME">%1$s</xliff:g> blev lukket"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Tillad notifikationer fra denne kanal"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Rediger rækkefølgen af indstillinger."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Side <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Låseskærm"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Udvid"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimer"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Luk"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Indstillinger"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Træk ned for at fjerne"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> vises som integreret billede"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Hvis du ikke ønsker, at <xliff:g id="NAME">%s</xliff:g> skal benytte denne funktion, kan du åbne indstillingerne og deaktivere den."</string>
- <string name="pip_play" msgid="333995977693142810">"Afspil"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Sæt på pause"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Gå videre til næste"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Gå til forrige"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefonen slukkede pga. varme"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Din telefon kører nu normalt"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Din telefon kører nu normalt.\nTryk for at få flere oplysninger"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Din telefon var blevet for varm, så den slukkede for at køle ned. Din telefon kører nu igen normalt. \n\nDin telefon kan blive for varm, hvis du:\n • Bruger ressourcekrævende apps (f.eks. spil, video eller navigation)\n • Downloader eller uploader store filer\n • Bruger din telefon i varme omgivelser"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Se håndteringsvejledning"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefonen er ved at blive varm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Nogle funktioner er begrænsede, mens telefonen køler ned"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Nogle funktioner er begrænsede, mens telefonen køler ned.\nTryk for at få flere oplysninger"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Din telefon forsøger automatisk at køle ned. Du kan stadig bruge telefonen, men den kører muligvis langsommere.\n\nNår din telefon er kølet ned, fungerer den normalt igen."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Se håndteringsvejledning"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Frakobl opladeren"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Der er et problem med opladning af denne enhed. Frakobl strømadapteren, og vær forsigtig, da kablet kan være varmt."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Se vejledningen i pleje"</string>
@@ -1078,7 +1042,8 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Flyt rundt på styringselementer ved at holde dem nede og trække"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Alle styringselementerne blev fjernet"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Ændringerne blev ikke gemt"</string>
- <string name="controls_favorite_load_error" msgid="5126216176144877419">"Betjeningselementerne kunne ikke indlæses. Tjek <xliff:g id="APP">%s</xliff:g>-appen for at sørge for, at dine appindstillinger ikke er blevet ændret."</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Se andre apps"</string>
+ <string name="controls_favorite_load_error" msgid="5126216176144877419">"Betjeningselementerne kunne ikke indlæses. Tjek <xliff:g id="APP">%s</xliff:g>-appen for at sikre, at dine appindstillinger ikke er blevet ændret."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kompatible betjeningselementer er ikke tilgængelige"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Andre"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"Føj til enhedsstyring"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Bekræft ændring på <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Stryg for at se mere"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Indlæser anbefalinger"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Luk denne mediesession"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Medie"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den aktuelle session."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skjul"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Genoptag"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Indstillinger"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Tjek appen"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Fejl. Prøver igen…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ikke fundet"</string>
diff --git a/packages/SystemUI/res/values-da/strings_tv.xml b/packages/SystemUI/res/values-da/strings_tv.xml
index 5b8bf6c9c2c4..f70427d90cd0 100644
--- a/packages/SystemUI/res/values-da/strings_tv.xml
+++ b/packages/SystemUI/res/values-da/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Integreret billede"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program uden titel)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Luk integreret billede"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Fuld skærm"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofonen er slået til"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s fik adgang til din mikrofon"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 7c90c5740977..52275fcd637e 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Erneut tippen, um Benachrichtigung zu öffnen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Zum Öffnen nach oben wischen"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Zum Wiederholen nach oben wischen"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Dieses Gerät gehört deiner Organisation"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Dieses Gerät gehört <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Zum Öffnen des Telefons vom Symbol wegwischen"</string>
<string name="voice_hint" msgid="7476017460191291417">"Zum Öffnen des Sprachassistenten vom Symbol wegwischen"</string>
<string name="camera_hint" msgid="4519495795000658637">"Zum Öffnen der Kamera vom Symbol wegwischen"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil wird eventuell überwacht."</string>
<string name="vpn_footer" msgid="3457155078010607471">"Das Netzwerk wird eventuell überwacht."</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Das Netzwerk wird eventuell überwacht"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Deine Organisation verwaltet dieses Gerät und kann den Netzwerkverkehr überwachen"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ist der Eigentümer dieses Geräts und kann den Netzwerkverkehr überwachen"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Dieses Gerät gehört deiner Organisation und ist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Dieses Gerät gehört <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> und ist mit <xliff:g id="VPN_APP">%2$s</xliff:g> verbunden"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Dieses Gerät gehört deiner Organisation"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Dieses Gerät gehört <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Dieses Gerät gehört deiner Organisation und ist mit VPNs verbunden"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Dieses Gerät gehört <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> und ist mit VPNs verbunden"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Deine Organisation kann den Netzwerkverkehr in deinem Arbeitsprofil überwachen"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kann den Netzwerkverkehr in deinem Arbeitsprofil überwachen"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Das Netzwerk wird eventuell überwacht"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Dieses Gerät ist mit VPNs verbunden"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Dein Arbeitsprofil ist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Dein privates Profil ist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Dieses Gerät ist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Geräteverwaltung"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profilüberwachung"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Netzwerküberwachung"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN deaktivieren"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN-Verbindung trennen"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Richtlinien ansehen"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Dieses Gerät gehört <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nDein IT-Administrator kann Einstellungen, Zugriffsrechte im Unternehmen, Apps, mit diesem Gerät verknüpfte Daten und die Standortdaten deines Geräts sehen und verwalten.\n\nWeitere Informationen erhältst du von deinem IT-Administrator."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Dieses Gerät gehört deiner Organisation.\n\nDein IT-Administrator kann Einstellungen, Zugriffsrechte im Unternehmen, Apps, mit diesem Gerät verknüpfte Daten und die Standortdaten deines Geräts sehen und verwalten.\n\nWeitere Informationen erhältst du von deinem IT-Administrator."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Deine Organisation hat ein Zertifikat einer Zertifizierungsstelle auf deinem Gerät installiert. Eventuell wird dein sicherer Netzwerkverkehr überwacht oder bearbeitet."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Deine Organisation hat ein Zertifikat einer Zertifizierungsstelle in deinem Arbeitsprofil installiert. Eventuell wird dein sicherer Netzwerkverkehr überwacht oder bearbeitet."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Auf dem Gerät ist das Zertifikat einer Zertifizierungsstelle installiert. Eventuell wird dein sicherer Netzwerkverkehr überwacht oder bearbeitet."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Lautlos"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bubble"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Kein Ton und keine Vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Kein Ton und keine Vibration, erscheint weiter unten im Bereich \"Unterhaltungen\""</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Kann klingeln oder vibrieren, abhängig von den Telefoneinstellungen"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kann klingeln oder vibrieren, je nach Telefoneinstellungen. Unterhaltungen von <xliff:g id="APP_NAME">%1$s</xliff:g> werden standardmäßig als Bubble angezeigt."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Du wirst mit einer unverankerten Verknüpfung darauf aufmerksam gemacht."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Das System entscheiden lassen, ob bei dieser Benachrichtigung ein Ton oder eine Vibration ausgegeben wird"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wird oben im Bereich \"Unterhaltungen\" als unverankerte Bubble mit einem Profilbild auf dem Sperrbildschirm angezeigt"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Einstellungen"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priorität"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Diese App wird über anderen Apps auf dem Bildschirm angezeigt und verwendet das Mikrofon und die Kamera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Einstellungen"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Ok"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Diese Benachrichtigung wurde vom System stummgeschaltet."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Diese Benachrichtigung wurde vom System hochgestuft."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Diese Benachrichtigung wurde vom System heruntergestuft."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"War das richtig?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Vielen Dank für dein Feedback."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Benachrichtigungseinstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g> geöffnet"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Benachrichtigungseinstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g> geschlossen"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Benachrichtigungen von diesem Kanal zulassen"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Reihenfolge der Einstellungen bearbeiten."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Seite <xliff:g id="ID_1">%1$d</xliff:g> von <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Sperrbildschirm"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Maximieren"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimieren"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Schließen"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Einstellungen"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Zum Verwerfen nach unten ziehen"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menü"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> ist in Bild im Bild"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Wenn du nicht möchtest, dass <xliff:g id="NAME">%s</xliff:g> diese Funktion verwendet, tippe, um die Einstellungen zu öffnen und die Funktion zu deaktivieren."</string>
- <string name="pip_play" msgid="333995977693142810">"Wiedergeben"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pausieren"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Vorwärts springen"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Rückwärts springen"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Ausgeschaltet, da zu heiß"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Dein Smartphone funktioniert jetzt wieder normal"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Dein Smartphone funktioniert jetzt wieder normal.\nFür mehr Informationen tippen."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Dein Smartphone war zu heiß und wurde zum Abkühlen ausgeschaltet. Nun funktioniert es wieder normal.\n\nMögliche Ursachen:\n • Verwendung ressourcenintensiver Apps (z. B. Spiele-, Video- oder Navigations-Apps)\n • Download oder Upload großer Dateien \n • Verwendung des Smartphones bei hohen Temperaturen"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Schritte zur Abkühlung des Geräts ansehen"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Smartphone wird warm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Einige Funktionen sind während der Abkühlphase des Smartphones eingeschränkt"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Einige Funktionen sind während der Abkühlphase des Smartphones eingeschränkt.\nFür mehr Informationen tippen."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Dein Smartphone kühlt sich automatisch ab. Du kannst dein Smartphone weiterhin nutzen, aber es reagiert möglicherweise langsamer.\n\nSobald dein Smartphone abgekühlt ist, funktioniert es wieder normal."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Schritte zur Abkühlung des Geräts ansehen"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Ladegerät vom Stromnetz trennen"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Beim Laden dieses Geräts ist ein Problem aufgetreten. Trenne das Netzteil vom Stromnetz. Sei dabei vorsichtig, denn das Netzteil oder das Kabel könnte heiß sein."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Schritte zur Fehlerbehebung ansehen"</string>
@@ -1049,9 +1013,9 @@
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
<string name="priority_onboarding_title" msgid="2893070698479227616">"Unterhaltung als vorrangig eingestuft"</string>
<string name="priority_onboarding_behavior" msgid="5342816047020432929">"Vorrangige Unterhaltungen:"</string>
- <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Oben im Bereich \"Unterhaltungen\" anzeigen"</string>
- <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Profilbild auf Sperrbildschirm anzeigen"</string>
- <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Unverankertes Infofeld über anderen Apps"</string>
+ <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Werden oben im Bereich \"Unterhaltungen\" angezeigt"</string>
+ <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Zeigen Profilbild auf Sperrbildschirm an"</string>
+ <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Erscheinen als unverankerte Bubble über anderen Apps"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"\"Bitte nicht stören\" unterbrechen"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"OK"</string>
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Einstellungen"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Zum Verschieben von Steuerelementen halten und ziehen"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Alle Steuerelemente entfernt"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Änderungen nicht gespeichert"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Andere Apps ansehen"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Steuerelemente konnten nicht geladen werden. Prüfe in der <xliff:g id="APP">%s</xliff:g> App, ob die Einstellungen möglicherweise geändert wurden."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kompatible Steuerelemente nicht verfügbar"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Andere"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Änderung für <xliff:g id="DEVICE">%s</xliff:g> bestätigen"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Wischen, um weitere zu sehen"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Empfehlungen werden geladen"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Diese Mediensitzung schließen"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Medien"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Du kannst die aktuelle Sitzung ausblenden."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ausblenden"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv – sieh in der App nach"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Fehler. Neuer Versuch…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nicht gefunden"</string>
diff --git a/packages/SystemUI/res/values-de/strings_tv.xml b/packages/SystemUI/res/values-de/strings_tv.xml
index ba2052840c10..b947b2ce1336 100644
--- a/packages/SystemUI/res/values-de/strings_tv.xml
+++ b/packages/SystemUI/res/values-de/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Bild im Bild"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Kein Sendungsname gefunden)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP schließen"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Vollbild"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon aktiv"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s hat auf dein Mikrofon zugegriffen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 49ec6b9c792a..bf4c61994c29 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Πατήστε ξανά για να ανοίξετε"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Σύρετε προς τα επάνω για άνοιγμα"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Σύρετε προς τα πάνω για να δοκιμάσετε ξανά"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Αυτή η συσκευή ανήκει στον οργανισμό σας."</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Αυτή η συσκευή ανήκει στον οργανισμό <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Σύρετε προς τα έξω για τηλέφωνο"</string>
<string name="voice_hint" msgid="7476017460191291417">"Σύρετε προς τα έξω για voice assist"</string>
<string name="camera_hint" msgid="4519495795000658637">"Σύρετε προς τα έξω για κάμερα"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Το προφίλ ενδέχεται να παρακολουθείται"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Το δίκτυο ενδέχεται να παρακολουθείται"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Το δίκτυο ενδέχεται να παρακολουθείται"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ο οργανισμός σας κατέχει αυτήν τη συσκευή και μπορεί να παρακολουθεί την επισκεψιμότητα δικτύου."</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Ο οργανισμός <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> κατέχει αυτήν τη συσκευή και μπορεί να παρακολουθεί την επισκεψιμότητα δικτύου."</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Αυτή η συσκευή ανήκει στον οργανισμό σας και είναι συνδεδεμένη στην εφαρμογή <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Αυτή η συσκευή ανήκει στον οργανισμό <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> και είναι συνδεδεμένη στην εφαρμογή <xliff:g id="VPN_APP">%2$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Αυτή η συσκευή ανήκει στον οργανισμό σας."</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Αυτή η συσκευή ανήκει στον οργανισμό <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Αυτή η συσκευή ανήκει στον οργανισμό σας και είναι συνδεδεμένη σε ορισμένα VPN."</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Αυτή η συσκευή ανήκει στον οργανισμό <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> και είναι συνδεδεμένη σε ορισμένα VPN."</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Ο οργανισμός σας μπορεί να παρακολουθεί την επισκεψιμότητα δικτύου στο προφίλ εργασίας σας"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Ο οργανισμός <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> μπορεί να παρακολουθεί την επισκεψιμότητα δικτύου στο προφίλ εργασίας σας"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Το δίκτυο ενδέχεται να παρακολουθείται"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Αυτή η συσκευή είναι συνδεδεμένη σε ορισμένα VPN."</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Το προφίλ εργασίας σας είναι συνδεδεμένο στην εφαρμογή <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Το προσωπικό σας προφίλ είναι συνδεδεμένο στην εφαρμογή <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Αυτή η συσκευή είναι συνδεδεμένη στην εφαρμογή <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Διαχείριση συσκευών"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Παρακολούθηση προφίλ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Παρακολούθηση δικτύου"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Απενεργοποίηση VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Αποσύνδεση VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Προβολή πολιτικών"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Αυτή η συσκευή ανήκει στον οργανισμό <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nΟ διαχειριστής IT μπορεί να παρακολουθεί και να διαχειρίζεται τις ρυθμίσεις, την εταιρική πρόσβαση, τις εφαρμογές, τα δεδομένα που σχετίζονται με τη συσκευή καθώς και τις πληροφορίες τοποθεσίας της συσκευής σας.\n\nΓια περισσότερες πληροφορίες, επικοινωνήστε με τον διαχειριστή IT."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Αυτή η συσκευή ανήκει στον οργανισμό σας.\n\nΟ διαχειριστής IT μπορεί να παρακολουθεί και να διαχειρίζεται τις ρυθμίσεις, την εταιρική πρόσβαση, τις εφαρμογές, τα δεδομένα που σχετίζονται με τη συσκευή καθώς και τις πληροφορίες τοποθεσίας της συσκευής σας.\n\nΓια περισσότερες πληροφορίες, επικοινωνήστε με τον διαχειριστή IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Ο οργανισμός σας εγκατέστησε μια αρχή έκδοσης πιστοποιητικών σε αυτήν τη συσκευή. Η ασφαλής επισκεψιμότητα δικτύου σας μπορεί να παρακολουθείται ή να τροποποιείται."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ο οργανισμός σας εγκατέστησε μια αρχή έκδοσης πιστοποιητικών στο προφίλ εργασίας σας. Η ασφαλής επισκεψιμότητα δικτύου σας μπορεί να παρακολουθείται ή να τροποποιείται."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Μια αρχή έκδοσης πιστοποιητικών έχει εγκατασταθεί σε αυτήν τη συσκευή. Η ασφαλής επισκεψιμότητα δικτύου σας μπορεί να παρακολουθείται ή να τροποποιείται."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Σίγαση"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Προεπιλογή"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Συννεφάκι"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Αυτόματο"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Χωρίς ήχο ή δόνηση"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Χωρίς ήχο ή δόνηση και εμφανίζεται χαμηλά στην ενότητα συζητήσεων"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου. Οι συζητήσεις από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εμφανίζονται σε συννεφάκι από προεπιλογή."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Κρατάει την προσοχή σας με μια κινούμενη συντόμευση προς αυτό το περιεχόμενο."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Επιτρέψτε στο σύστημα να αποφασίσει αν αυτή η ειδοποίηση θα αναπαράγει έναν ήχο ή θα ενεργοποιήσει τη δόνηση της συσκευής"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Εμφανίζεται στο επάνω μέρος της ενότητας συζητήσεων, προβάλλεται ως κινούμενο συννεφάκι, εμφανίζει τη φωτογραφία προφίλ στην οθόνη κλειδώματος"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ρυθμίσεις"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Προτεραιότητα"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Αυτή η εφαρμογή εμφανίζεται πάνω σε άλλες εφαρμογές στην οθόνη σας και χρησιμοποιεί το μικρόφωνο και την κάμερα."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Ρυθμίσεις"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ΟΚ"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Αυτή η ειδοποίηση τέθηκε σε σίγαση από το σύστημα."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Αυτή η ειδοποίηση προωθήθηκε από το σύστημα."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Αυτή η ειδοποίηση υποβιβάστηκε από το σύστημα."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Ήταν σωστό αυτό;"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Σας ευχαριστούμε για τα σχόλιά σας!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ΟΚ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Τα στοιχεία ελέγχου ειδοποιήσεων για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> άνοιξαν"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Τα στοιχεία ελέγχου ειδοποιήσεων για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> έκλεισαν"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Να επιτρέπονται οι ειδοποιήσεις από αυτό το κανάλι"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Επεξεργασία σειράς ρυθμίσεων."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Σελίδα <xliff:g id="ID_1">%1$d</xliff:g> από <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Οθόνη κλειδώματος"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Ανάπτυξη"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Ελαχιστοποίηση"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Κλείσιμο"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Ρυθμίσεις"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Σύρετε προς τα κάτω για παράβλεψη"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Μενού"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"Η λειτουργία picture-in-picture είναι ενεργή σε <xliff:g id="NAME">%s</xliff:g>."</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Εάν δεν θέλετε να χρησιμοποιείται αυτή η λειτουργία από την εφαρμογή <xliff:g id="NAME">%s</xliff:g>, πατήστε για να ανοίξετε τις ρυθμίσεις και απενεργοποιήστε την."</string>
- <string name="pip_play" msgid="333995977693142810">"Αναπαραγωγή"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Παύση"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Μετάβαση στο επόμενο"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Μετάβαση στο προηγούμενο"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Το τηλέφωνο απεν. λόγω ζέστης"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Το τηλέφωνο λειτουργεί πλέον κανονικά"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Το τηλέφωνο λειτουργεί πλέον κανονικά.\nΠατήστε για περισσότερες πληροφορίες."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Η θερμοκρασία του τηλεφώνου είναι πολύ υψηλή και απενεργοποιήθηκε για να κρυώσει. Πλέον, λειτουργεί κανονικά.\n\nΗ θερμοκρασία ενδέχεται να ανέβει κατά τη:\n • Χρήση εφαρμογών υψηλής κατανάλωσης πόρων (όπως gaming, βίντεο ή περιήγησης)\n • Λήψη/μεταφόρτωση μεγάλων αρχείων\n • Χρήση σε υψηλές θερμοκρασίες"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Δείτε βήματα αντιμετώπισης."</string>
<string name="high_temp_title" msgid="2218333576838496100">"Αύξηση θερμοκρασίας τηλεφώνου"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Ορισμένες λειτουργίες περιορίζονται κατά τη μείωση της θερμοκρασίας"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Ορισμένες λειτουργίες περιορίζονται κατά τη μείωση της θερμοκρασίας.\nΠατήστε για περισσότερες πληροφορίες."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Το τηλέφωνό σας θα προσπαθήσει να μειώσει αυτόματα τη θερμοκρασία. Μπορείτε να εξακολουθήσετε να το χρησιμοποιείτε, αλλά είναι πιθανό να λειτουργεί πιο αργά.\n\nΜόλις μειωθεί η θερμοκρασία του τηλεφώνου σας, θα λειτουργεί ξανά κανονικά."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Δείτε βήματα αντιμετώπισης."</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Αποσυνδέστε τον φορτιστή"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Υπάρχει κάποιο πρόβλημα με τη φόρτιση αυτής της συσκευής. Αποσυνδέστε τον μετασχηματιστή με προσοχή, λαμβάνοντας υπόψη ότι το καλώδιο μπορεί να είναι ζεστό."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Δείτε βήματα αντιμετώπισης"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Κρατήστε και σύρετε για αναδιάταξη των στοιχείων ελέγχου"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Όλα τα στοιχεία ελέγχου καταργήθηκαν"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Οι αλλαγές δεν αποθηκεύτηκαν"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Εμφάνιση άλλων εφαρμογών"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Δεν ήταν δυνατή η φόρτωση των στοιχείων ελέγχου. Ελέγξτε την εφαρμογή <xliff:g id="APP">%s</xliff:g> για να βεβαιωθείτε ότι δεν έχουν αλλάξει οι ρυθμίσεις της εφαρμογής."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Μη διαθέσιμα συμβατά στοιχεία ελέγχου"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Άλλο"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Επιβεβαίωση αλλαγής για <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Σύρετε για να δείτε περισσότερα."</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Φόρτωση προτάσεων"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Κλείσιμο αυτής της περιόδου λειτουργίας μέσων."</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Μέσα"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Απόκρυψη"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Συνέχιση"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Ρυθμίσεις"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Ανενεργό, έλεγχος εφαρμογής"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Προέκυψε σφάλμα. Επανάληψη…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Δεν βρέθηκε."</string>
diff --git a/packages/SystemUI/res/values-el/strings_tv.xml b/packages/SystemUI/res/values-el/strings_tv.xml
index 5f93f3246f82..d03961366a2c 100644
--- a/packages/SystemUI/res/values-el/strings_tv.xml
+++ b/packages/SystemUI/res/values-el/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Λειτουργία Picture-in-picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Δεν υπάρχει τίτλος προγράμματος)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Κλείσιμο PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Πλήρης οθόνη"</string>
<string name="mic_active" msgid="5766614241012047024">"Ενεργό μικρόφωνο"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Πραγματοποιήθηκε πρόσβαση στο μικρόφωνό σας από το %1$s"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 8179031d4123..acb811107463 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tap again to open"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Swipe up to try again"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Swipe from icon for phone"</string>
<string name="voice_hint" msgid="7476017460191291417">"Swipe from icon for voice assist"</string>
<string name="camera_hint" msgid="4519495795000658637">"Swipe from icon for camera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profile may be monitored"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Network may be monitored"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Network may be monitored"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Your organisation owns this device and may monitor network traffic"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> owns this device and may monitor network traffic"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"This device belongs to your organisation and is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> and is connected to <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"This device belongs to your organisation"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"This device belongs to your organisation and is connected to VPNs"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> and is connected to VPNs"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Your organisation may monitor network traffic in your work profile"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> may monitor network traffic in your work profile"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Network may be monitored"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"This device is connected to VPNs"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Your personal profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"This device is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Device management"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profile monitoring"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Network monitoring"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Disable VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Disconnect VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"View Policies"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"This device belongs to your organisation.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Your organisation installed a certificate authority on this device. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bubble"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No sound or vibration and appears lower in conversation section"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"May ring or vibrate based on phone settings"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"May ring or vibrate based on phone settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Keeps your attention with a floating shortcut to this content."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Shows at top of conversation section, appears as floating bubble, displays profile picture on lock screen"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Settings"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"This app is displaying over other apps on your screen and using the microphone and camera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Settings"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"This notification was silenced by the system."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"This notification was promoted by the system."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"This notification was demoted by the system."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Allow notifications from this channel"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Edit order of settings."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Expand"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimise"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Close"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Settings"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Drag down to dismiss"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
- <string name="pip_play" msgid="333995977693142810">"Play"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pause"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Skip to next"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Skip to previous"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Phone turned off due to heat"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Your phone is now running normally"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Your phone is now running normally.\nTap for more info"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n • Use resource-intensive apps (such as gaming, video or navigation apps)\n • Download or upload large files\n • Use your phone in high temperatures"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Phone is getting warm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Some features limited while phone cools down"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Some features are limited while phone cools down.\nTap for more info"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Your phone will automatically try to cool down. You can still use your phone, but it may run more slowly.\n\nOnce your phone has cooled down, it will run normally."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Unplug charger"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"There’s an issue charging this device. Unplug the power adaptor, and take care as the cable may be warm."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"See care steps"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Hold and drag to rearrange controls"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"All controls removed"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Changes not saved"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"See other apps"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Controls could not be loaded. Check the <xliff:g id="APP">%s</xliff:g> app to make sure that the app settings haven’t changed."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Compatible controls unavailable"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Other"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirm change for <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Swipe to see more"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Close this media session"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Error, retrying…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings_tv.xml b/packages/SystemUI/res/values-en-rAU/strings_tv.xml
index efbaa1c42e60..f81611fba4a5 100644
--- a/packages/SystemUI/res/values-en-rAU/strings_tv.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(No title program)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Close PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Full screen"</string>
<string name="mic_active" msgid="5766614241012047024">"Microphone active"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s accessed your microphone"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index facb6171189c..e3bad1f83a90 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tap again to open"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Swipe up to try again"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Swipe from icon for phone"</string>
<string name="voice_hint" msgid="7476017460191291417">"Swipe from icon for voice assist"</string>
<string name="camera_hint" msgid="4519495795000658637">"Swipe from icon for camera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profile may be monitored"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Network may be monitored"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Network may be monitored"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Your organisation owns this device and may monitor network traffic"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> owns this device and may monitor network traffic"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"This device belongs to your organisation and is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> and is connected to <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"This device belongs to your organisation"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"This device belongs to your organisation and is connected to VPNs"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> and is connected to VPNs"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Your organisation may monitor network traffic in your work profile"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> may monitor network traffic in your work profile"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Network may be monitored"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"This device is connected to VPNs"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Your personal profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"This device is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Device management"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profile monitoring"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Network monitoring"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Disable VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Disconnect VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"View Policies"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"This device belongs to your organisation.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Your organisation installed a certificate authority on this device. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bubble"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No sound or vibration and appears lower in conversation section"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"May ring or vibrate based on phone settings"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"May ring or vibrate based on phone settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Keeps your attention with a floating shortcut to this content."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Shows at top of conversation section, appears as floating bubble, displays profile picture on lock screen"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Settings"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"This app is displaying over other apps on your screen and using the microphone and camera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Settings"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"This notification was silenced by the system."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"This notification was promoted by the system."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"This notification was demoted by the system."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Allow notifications from this channel"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Edit order of settings."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Expand"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimise"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Close"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Settings"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Drag down to dismiss"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
- <string name="pip_play" msgid="333995977693142810">"Play"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pause"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Skip to next"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Skip to previous"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Phone turned off due to heat"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Your phone is now running normally"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Your phone is now running normally.\nTap for more info"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n • Use resource-intensive apps (such as gaming, video or navigation apps)\n • Download or upload large files\n • Use your phone in high temperatures"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Phone is getting warm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Some features limited while phone cools down"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Some features are limited while phone cools down.\nTap for more info"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Your phone will automatically try to cool down. You can still use your phone, but it may run more slowly.\n\nOnce your phone has cooled down, it will run normally."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Unplug charger"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"There’s an issue charging this device. Unplug the power adaptor, and take care as the cable may be warm."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"See care steps"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Hold and drag to rearrange controls"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"All controls removed"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Changes not saved"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"See other apps"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Controls could not be loaded. Check the <xliff:g id="APP">%s</xliff:g> app to make sure that the app settings haven’t changed."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Compatible controls unavailable"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Other"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirm change for <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Swipe to see more"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Close this media session"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Error, retrying…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings_tv.xml b/packages/SystemUI/res/values-en-rCA/strings_tv.xml
index efbaa1c42e60..f81611fba4a5 100644
--- a/packages/SystemUI/res/values-en-rCA/strings_tv.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(No title program)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Close PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Full screen"</string>
<string name="mic_active" msgid="5766614241012047024">"Microphone active"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s accessed your microphone"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 8179031d4123..acb811107463 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tap again to open"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Swipe up to try again"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Swipe from icon for phone"</string>
<string name="voice_hint" msgid="7476017460191291417">"Swipe from icon for voice assist"</string>
<string name="camera_hint" msgid="4519495795000658637">"Swipe from icon for camera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profile may be monitored"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Network may be monitored"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Network may be monitored"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Your organisation owns this device and may monitor network traffic"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> owns this device and may monitor network traffic"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"This device belongs to your organisation and is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> and is connected to <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"This device belongs to your organisation"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"This device belongs to your organisation and is connected to VPNs"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> and is connected to VPNs"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Your organisation may monitor network traffic in your work profile"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> may monitor network traffic in your work profile"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Network may be monitored"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"This device is connected to VPNs"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Your personal profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"This device is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Device management"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profile monitoring"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Network monitoring"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Disable VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Disconnect VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"View Policies"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"This device belongs to your organisation.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Your organisation installed a certificate authority on this device. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bubble"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No sound or vibration and appears lower in conversation section"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"May ring or vibrate based on phone settings"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"May ring or vibrate based on phone settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Keeps your attention with a floating shortcut to this content."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Shows at top of conversation section, appears as floating bubble, displays profile picture on lock screen"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Settings"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"This app is displaying over other apps on your screen and using the microphone and camera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Settings"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"This notification was silenced by the system."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"This notification was promoted by the system."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"This notification was demoted by the system."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Allow notifications from this channel"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Edit order of settings."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Expand"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimise"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Close"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Settings"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Drag down to dismiss"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
- <string name="pip_play" msgid="333995977693142810">"Play"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pause"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Skip to next"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Skip to previous"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Phone turned off due to heat"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Your phone is now running normally"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Your phone is now running normally.\nTap for more info"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n • Use resource-intensive apps (such as gaming, video or navigation apps)\n • Download or upload large files\n • Use your phone in high temperatures"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Phone is getting warm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Some features limited while phone cools down"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Some features are limited while phone cools down.\nTap for more info"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Your phone will automatically try to cool down. You can still use your phone, but it may run more slowly.\n\nOnce your phone has cooled down, it will run normally."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Unplug charger"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"There’s an issue charging this device. Unplug the power adaptor, and take care as the cable may be warm."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"See care steps"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Hold and drag to rearrange controls"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"All controls removed"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Changes not saved"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"See other apps"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Controls could not be loaded. Check the <xliff:g id="APP">%s</xliff:g> app to make sure that the app settings haven’t changed."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Compatible controls unavailable"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Other"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirm change for <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Swipe to see more"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Close this media session"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Error, retrying…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings_tv.xml b/packages/SystemUI/res/values-en-rGB/strings_tv.xml
index efbaa1c42e60..f81611fba4a5 100644
--- a/packages/SystemUI/res/values-en-rGB/strings_tv.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(No title program)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Close PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Full screen"</string>
<string name="mic_active" msgid="5766614241012047024">"Microphone active"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s accessed your microphone"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 8179031d4123..acb811107463 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tap again to open"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Swipe up to try again"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Swipe from icon for phone"</string>
<string name="voice_hint" msgid="7476017460191291417">"Swipe from icon for voice assist"</string>
<string name="camera_hint" msgid="4519495795000658637">"Swipe from icon for camera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profile may be monitored"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Network may be monitored"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Network may be monitored"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Your organisation owns this device and may monitor network traffic"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> owns this device and may monitor network traffic"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"This device belongs to your organisation and is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> and is connected to <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"This device belongs to your organisation"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"This device belongs to your organisation and is connected to VPNs"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> and is connected to VPNs"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Your organisation may monitor network traffic in your work profile"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> may monitor network traffic in your work profile"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Network may be monitored"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"This device is connected to VPNs"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Your personal profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"This device is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Device management"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profile monitoring"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Network monitoring"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Disable VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Disconnect VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"View Policies"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"This device belongs to your organisation.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Your organisation installed a certificate authority on this device. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bubble"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No sound or vibration and appears lower in conversation section"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"May ring or vibrate based on phone settings"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"May ring or vibrate based on phone settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Keeps your attention with a floating shortcut to this content."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Shows at top of conversation section, appears as floating bubble, displays profile picture on lock screen"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Settings"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priority"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"This app is displaying over other apps on your screen and using the microphone and camera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Settings"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"This notification was silenced by the system."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"This notification was promoted by the system."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"This notification was demoted by the system."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Allow notifications from this channel"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Edit order of settings."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Expand"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimise"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Close"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Settings"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Drag down to dismiss"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
- <string name="pip_play" msgid="333995977693142810">"Play"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pause"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Skip to next"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Skip to previous"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Phone turned off due to heat"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Your phone is now running normally"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Your phone is now running normally.\nTap for more info"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n • Use resource-intensive apps (such as gaming, video or navigation apps)\n • Download or upload large files\n • Use your phone in high temperatures"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Phone is getting warm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Some features limited while phone cools down"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Some features are limited while phone cools down.\nTap for more info"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Your phone will automatically try to cool down. You can still use your phone, but it may run more slowly.\n\nOnce your phone has cooled down, it will run normally."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Unplug charger"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"There’s an issue charging this device. Unplug the power adaptor, and take care as the cable may be warm."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"See care steps"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Hold and drag to rearrange controls"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"All controls removed"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Changes not saved"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"See other apps"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Controls could not be loaded. Check the <xliff:g id="APP">%s</xliff:g> app to make sure that the app settings haven’t changed."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Compatible controls unavailable"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Other"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirm change for <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Swipe to see more"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Close this media session"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Error, retrying…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings_tv.xml b/packages/SystemUI/res/values-en-rIN/strings_tv.xml
index efbaa1c42e60..f81611fba4a5 100644
--- a/packages/SystemUI/res/values-en-rIN/strings_tv.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(No title program)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Close PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Full screen"</string>
<string name="mic_active" msgid="5766614241012047024">"Microphone active"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s accessed your microphone"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 1ee6e919ea23..25ec6c384157 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎Tap again to open‎‏‎‎‏‎"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎Swipe up to open‎‏‎‎‏‎"</string>
<string name="keyguard_retry" msgid="886802522584053523">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎Swipe up to try again‎‏‎‎‏‎"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎This device belongs to your organization‎‏‎‎‏‎"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎This device belongs to ‎‏‎‎‏‏‎<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="phone_hint" msgid="6682125338461375925">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‏‎Swipe from icon for phone‎‏‎‎‏‎"</string>
<string name="voice_hint" msgid="7476017460191291417">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎Swipe from icon for voice assist‎‏‎‎‏‎"</string>
<string name="camera_hint" msgid="4519495795000658637">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‏‎Swipe from icon for camera‎‏‎‎‏‎"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‏‎‏‎‏‎‏‏‎‎‎Profile may be monitored‎‏‎‎‏‎"</string>
<string name="vpn_footer" msgid="3457155078010607471">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎Network may be monitored‎‏‎‎‏‎"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎Network may be monitored‎‏‎‎‏‎"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎Your organization owns this device and may monitor network traffic‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ owns this device and may monitor network traffic‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎This device belongs to your organization and is connected to ‎‏‎‎‏‏‎<xliff:g id="VPN_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎This device belongs to ‎‏‎‎‏‏‎<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and is connected to ‎‏‎‎‏‏‎<xliff:g id="VPN_APP">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‎This device belongs to your organization‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎This device belongs to ‎‏‎‎‏‏‎<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‏‎‎This device belongs to your organization and is connected to VPNs‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎This device belongs to ‎‏‎‎‏‏‎<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and is connected to VPNs‎‏‎‎‏‎"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎Your organization may monitor network traffic in your work profile‎‏‎‎‏‎"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ may monitor network traffic in your work profile‎‏‎‎‏‎"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎‎Network may be monitored‎‏‎‎‏‎"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‎This device is connected to VPNs‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‎‏‎‏‎Your work profile is connected to ‎‏‎‎‏‏‎<xliff:g id="VPN_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‏‎Your personal profile is connected to ‎‏‎‎‏‏‎<xliff:g id="VPN_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎This device is connected to ‎‏‎‎‏‏‎<xliff:g id="VPN_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‎‎‎Device management‎‏‎‎‏‎"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‎‎‎‎‎‎Profile monitoring‎‏‎‎‏‎"</string>
<string name="monitoring_title" msgid="4063890083735924568">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎Network monitoring‎‏‎‎‏‎"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‏‎‎Disable VPN‎‏‎‎‏‎"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎Disconnect VPN‎‏‎‎‏‎"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎View Policies‎‏‎‎‏‎"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎This device belongs to ‎‏‎‎‏‏‎<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Your IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎For more information, contact your IT admin.‎‏‎‎‏‎"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎This device belongs to your organization.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Your IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎For more information, contact your IT admin.‎‏‎‎‏‎"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‎Your organization installed a certificate authority on this device. Your secure network traffic may be monitored or modified.‎‏‎‎‏‎"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‎Your organization installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified.‎‏‎‎‏‎"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎A certificate authority is installed on this device. Your secure network traffic may be monitored or modified.‎‏‎‎‏‎"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎Silent‎‏‎‎‏‎"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎Default‎‏‎‎‏‎"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‎Bubble‎‏‎‎‏‎"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎Automatic‎‏‎‎‏‎"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‏‎‎No sound or vibration‎‏‎‎‏‎"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‎No sound or vibration and appears lower in conversation section‎‏‎‎‏‎"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎May ring or vibrate based on phone settings‎‏‎‎‏‎"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎May ring or vibrate based on phone settings. Conversations from ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ bubble by default.‎‏‎‎‏‎"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎Keeps your attention with a floating shortcut to this content.‎‏‎‎‏‎"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎Have the system determine if this notification should make sound or vibration‎‏‎‎‏‎"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‎Shows at top of conversation section, appears as floating bubble, displays profile picture on lock screen‎‏‎‎‏‎"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‎Settings‎‏‎‎‏‎"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎Priority‎‏‎‎‏‎"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎This app is displaying over other apps on your screen and using the microphone and camera.‎‏‎‎‏‎"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎Settings‎‏‎‎‏‎"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‎OK‎‏‎‎‏‎"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‎‏‏‏‎This notification was silenced by the system.‎‏‎‎‏‎"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎This notification was promoted by the system.‎‏‎‎‏‎"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎This notification was demoted by the system.‎‏‎‎‏‎"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‎‎‏‎Was this correct?‎‏‎‎‏‎"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎Thanks for your feedback!‎‏‎‎‏‎"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎OK‎‏‎‎‏‎"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎Notification controls for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ opened‎‏‎‎‏‎"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎Notification controls for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ closed‎‏‎‎‏‎"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‎‎Allow notifications from this channel‎‏‎‎‏‎"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‎‎Edit order of settings.‎‏‎‎‏‎"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎Page ‎‏‎‎‏‏‎<xliff:g id="ID_1">%1$d</xliff:g>‎‏‎‎‏‏‏‎ of ‎‏‎‎‏‏‎<xliff:g id="ID_2">%2$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‎Lock screen‎‏‎‎‏‎"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎Expand‎‏‎‎‏‎"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‏‏‎‏‏‎Minimize‎‏‎‎‏‎"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‎‎‎‎Close‎‏‎‎‏‎"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‎‏‎Settings‎‏‎‎‏‎"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎Drag down to dismiss‎‏‎‎‏‎"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎Menu‎‏‎‎‏‎"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ is in picture-in-picture‎‏‎‎‏‎"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎If you don\'t want ‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ to use this feature, tap to open settings and turn it off.‎‏‎‎‏‎"</string>
- <string name="pip_play" msgid="333995977693142810">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‏‎‎Play‎‏‎‎‏‎"</string>
- <string name="pip_pause" msgid="1139598607050555845">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎Pause‎‏‎‎‏‎"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‎Skip to next‎‏‎‎‏‎"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎Skip to previous‎‏‎‎‏‎"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‎Phone turned off due to heat‎‏‎‎‏‎"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‎‏‏‏‎Your phone is now running normally‎‏‎‎‏‎"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎Your phone is now running normally.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Tap for more info‎‏‎‎‏‎"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎Your phone was too hot, so it turned off to cool down. Your phone is now running normally.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Your phone may get too hot if you:‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ • Use resource-intensive apps (such as gaming, video, or navigation apps)‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ • Download or upload large files‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ • Use your phone in high temperatures‎‏‎‎‏‎"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‎See care steps‎‏‎‎‏‎"</string>
<string name="high_temp_title" msgid="2218333576838496100">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎Phone is getting warm‎‏‎‎‏‎"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎Some features limited while phone cools down‎‏‎‎‏‎"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎Some features limited while phone cools down.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Tap for more info‎‏‎‎‏‎"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎Your phone will automatically try to cool down. You can still use your phone, but it may run slower.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Once your phone has cooled down, it will run normally.‎‏‎‎‏‎"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎See care steps‎‏‎‎‏‎"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎Unplug charger‎‏‎‎‏‎"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎There’s an issue charging this device. Unplug the power adapter, and take care as the cable may be warm.‎‏‎‎‏‎"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‎See care steps‎‏‎‎‏‎"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‏‎Hold &amp; drag to rearrange controls‎‏‎‎‏‎"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎All controls removed‎‏‎‎‏‎"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎Changes not saved‎‏‎‎‏‎"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‏‎‎‎See other apps‎‏‎‎‏‎"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎Controls could not be loaded. Check the ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ app to make sure that the app settings haven’t changed.‎‏‎‎‏‎"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‎Compatible controls unavailable‎‏‎‎‏‎"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎Other‎‏‎‎‏‎"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‎‏‏‎Confirm change for ‎‏‎‎‏‏‎<xliff:g id="DEVICE">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‏‏‎Swipe to see more‎‏‎‎‏‎"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‎‎Loading recommendations‎‏‎‎‏‎"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‎Close this media session‎‏‎‎‏‎"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎Media‎‏‎‎‏‎"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎Hide the current session.‎‏‎‎‏‎"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎Hide‎‏‎‎‏‎"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎Resume‎‏‎‎‏‎"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎Settings‎‏‎‎‏‎"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎Inactive, check app‎‏‎‎‏‎"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎Error, retrying…‎‏‎‎‏‎"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎Not found‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings_tv.xml b/packages/SystemUI/res/values-en-rXC/strings_tv.xml
index 1ca2385af9c4..88c5843b7c96 100644
--- a/packages/SystemUI/res/values-en-rXC/strings_tv.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎Picture-in-Picture‎‏‎‎‏‎"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‎(No title program)‎‏‎‎‏‎"</string>
- <string name="pip_close" msgid="5775212044472849930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‎Close PIP‎‏‎‎‏‎"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎Full screen‎‏‎‎‏‎"</string>
<string name="mic_active" msgid="5766614241012047024">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎Microphone Active‎‏‎‎‏‎"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‎‎‎%1$s accessed your microphone‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index ae2f731a584a..01bed104a951 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Presiona de nuevo para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Desliza el dedo hacia arriba para volver a intentarlo"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertenece a tu organización"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Desliza el dedo para desbloquear el teléfono."</string>
<string name="voice_hint" msgid="7476017460191291417">"Desliza el dedo desde el ícono para abrir asistente de voz."</string>
<string name="camera_hint" msgid="4519495795000658637">"Desliza el dedo para acceder a la cámara."</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Es posible que se supervise el perfil."</string>
<string name="vpn_footer" msgid="3457155078010607471">"Es posible que la red esté supervisada."</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Es posible que la red esté supervisada"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Tu organización es propietaria de este dispositivo y podría controlar el tráfico de red"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> es la organización propietaria de este dispositivo y podría controlar el tráfico de red"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Este dispositivo pertenece a tu organización y está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> y está conectado a <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Este dispositivo pertenece a tu organización"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Este dispositivo pertenece a tu organización y está conectado a VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> y está conectado a VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Tu organización puede controlar el tráfico de red en tu perfil de trabajo"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Es posible que <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> controle el tráfico de red en tu perfil de trabajo"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Es posible que la red esté supervisada"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Este dispositivo está conectado a VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Tu perfil de trabajo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Tu perfil personal está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Este dispositivo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Administración del dispositivo"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Supervisión del perfil"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Supervisión de red"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Inhabilitar VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Desconectar VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Ver políticas"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nTu administrador de TI puede controlar y administrar la configuración, el acceso corporativo, las apps, los datos asociados al dispositivo y la información de ubicación.\n\nPara obtener más información, comunícate con el administrador de TI."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Este dispositivo pertenece a tu organización.\n\nTu administrador de TI puede controlar y administrar la configuración, el acceso corporativo, las apps, los datos asociados al dispositivo y la información de ubicación.\n\nPara obtener más información, comunícate con el administrador de TI."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Tu organización instaló una autoridad de certificación en este dispositivo. Es posible que se controle o modifique el tráfico de tu red segura."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tu organización instaló una autoridad de certificación en tu perfil de trabajo. Es posible que se controle o modifique el tráfico de tu red segura."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Hay una autoridad de certificación instalada en este dispositivo. Es posible que se controle o modifique el tráfico de tu red segura."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silencio"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Cuadro"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en una parte inferior de la sección de conversaciones"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Puede sonar o vibrar en función de la configuración del teléfono"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Puede sonar o vibrar en función de la configuración del teléfono. Conversaciones de la burbuja de <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Retiene tu atención con un acceso directo flotante a este contenido."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Dejar que el sistema determine si esta notificación debe emitir un sonido o una vibración"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece en la parte superior de la sección de conversaciones, en forma de burbuja flotante, y muestra la foto de perfil en la pantalla de bloqueo"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configuración"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Esta app se muestra sobre otras apps en la pantalla y está usando el micrófono y la cámara."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Configuración"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Aceptar"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"El sistema silenció la notificación."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"El sistema ascendió el nivel de esta notificación."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"El sistema descendió el nivel de esta notificación."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"¿Te parece bien?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Gracias por tus comentarios."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Aceptar"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Se abrieron los controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Se cerraron los controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Permitir las notificaciones de este canal"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editar orden de configuración"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Expandir"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizar"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Cerrar"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Configuración"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Arrastra hacia abajo para descartar"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menú"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> está en modo de Pantalla en pantalla"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Si no quieres que <xliff:g id="NAME">%s</xliff:g> use esta función, presiona para abrir la configuración y desactivarla."</string>
- <string name="pip_play" msgid="333995977693142810">"Reproducir"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pausar"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Siguiente"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Anterior"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"El teléfono se apagó por calor"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Tu teléfono ya funciona correctamente"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Tu teléfono ahora se ejecuta con normalidad.\nPresiona para obtener más información"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Tu teléfono estaba muy caliente y se apagó para enfriarse. Ya funciona correctamente.\n\nTu teléfono puede calentarse en estos casos:\n • Usas apps que consumen muchos recursos (como juegos, videos o navegación).\n • Subes o descargas archivos grandes.\n • Usas el teléfono en condiciones de temperatura alta."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver pasos de mantenimiento"</string>
<string name="high_temp_title" msgid="2218333576838496100">"El teléfono se está calentando"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Se limitarán algunas funciones mientras se enfría el teléfono"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Algunas funciones se limitan durante el enfriamiento del teléfono.\nPresiona para obtener más información"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Tu teléfono intentará enfriarse automáticamente. Podrás usarlo, pero es posible que funcione más lento.\n\nUna vez que se haya enfriado, volverá a funcionar correctamente."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantenimiento"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Desconectar cargador"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"No se puede cargar el dispositivo. Desconecta el adaptador de la corriente con cuidado, ya que el cable podría estar caliente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver pasos de mantenimiento"</string>
@@ -1050,8 +1014,8 @@
<string name="priority_onboarding_title" msgid="2893070698479227616">"Se estableció la conversación como prioritaria"</string>
<string name="priority_onboarding_behavior" msgid="5342816047020432929">"Las conversaciones prioritarias harán lo siguiente:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Mostrarse en la parte superior de conversaciones"</string>
- <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Mostrar una foto de perfil en pantalla de bloqueo"</string>
- <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Aparecen como burbujas flotantes encima de apps"</string>
+ <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Mostrarán una foto de perfil en pantalla de bloqueo"</string>
+ <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Aparecerán como burbujas flotantes encima de apps"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"Suspender No interrumpir"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"Entendido"</string>
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuración"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Mantén presionado y arrastra un control para reubicarlo"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Se quitaron todos los controles"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"No se guardaron los cambios"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Ver otras apps"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"No se pudieron cargar los controles. Revisa la app de <xliff:g id="APP">%s</xliff:g> para asegurarte de que su configuración no haya cambiado."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"No hay ningún control compatible disponible"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Otros"</string>
@@ -1090,13 +1055,16 @@
<string name="controls_pin_wrong" msgid="6162694056042164211">"PIN incorrecto"</string>
<string name="controls_pin_verifying" msgid="3755045989392131746">"Verificando…"</string>
<string name="controls_pin_instructions" msgid="6363309783822475238">"Ingresa el PIN"</string>
- <string name="controls_pin_instructions_retry" msgid="1566667581012131046">"Probar con otro PIN"</string>
+ <string name="controls_pin_instructions_retry" msgid="1566667581012131046">"Prueba con otro PIN"</string>
<string name="controls_confirmation_confirming" msgid="2596071302617310665">"Confirmando…"</string>
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirmar cambio para <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Desliza el dedo para ver más elementos"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Cerrar esta sesión multimedia"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Contenido multimedia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta la sesión actual."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Verifica la app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Hubo un error. Reintentando…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No se encontró"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings_tv.xml b/packages/SystemUI/res/values-es-rUS/strings_tv.xml
index 3921fd822e57..8e9e048b06f6 100644
--- a/packages/SystemUI/res/values-es-rUS/strings_tv.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Pantalla en pantalla"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Sin título de programa)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Cerrar PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Pantalla completa"</string>
<string name="mic_active" msgid="5766614241012047024">"Micrófono activado"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s accedió al micrófono"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 41217f378cd9..67678d24a0a6 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Toca de nuevo para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Desliza el dedo hacia arriba para volverlo a intentar"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertenece a tu organización"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Desliza desde el icono para abrir el teléfono"</string>
<string name="voice_hint" msgid="7476017460191291417">"Desliza desde el icono para abrir asistente de voz"</string>
<string name="camera_hint" msgid="4519495795000658637">"Desliza desde el icono para abrir la cámara"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Es posible que se supervise el perfil"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Puede que la red esté supervisada"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Puede que la red esté supervisada"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"El dispositivo pertenece a tu organización, que puede monitorizar su tráfico de red"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"El dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>, que puede monitorizar su tráfico de red"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Este dispositivo pertenece a tu organización y está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> y está conectado a <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Este dispositivo pertenece a tu organización"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Este dispositivo pertenece a tu organización y está conectado a varias VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> y está conectado a varias VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Tu organización puede supervisar el tráfico de red de tu perfil de trabajo"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> puede supervisar el tráfico de red de tu perfil de trabajo"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Puede que la red esté supervisada"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Este dispositivo está conectado a varias VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Tu perfil de trabajo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Tu perfil personal está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Este dispositivo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Administración de dispositivos"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Supervisión del perfil"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Supervisión de red"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Inhabilitar VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Desconectar VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Ver políticas"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"El dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nEl administrador de TI puede monitorizar y gestionar los ajustes, el acceso corporativo, las aplicaciones, la información de ubicación del dispositivo y los datos asociados a él.\n\nPara obtener más información, ponte en contacto con el administrador de TI."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"El dispositivo pertenece a tu organización.\n\nEl administrador de TI puede monitorizar y gestionar los ajustes, el acceso corporativo, las aplicaciones, la información de ubicación del dispositivo y los datos asociados a él.\n\nPara obtener más información, ponte en contacto con el administrador de TI."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Tu organización ha instalado una entidad de certificación en este dispositivo. Es posible que se supervise o se modifique tu tráfico de red seguro."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tu organización ha instalado una entidad de certificación en tu perfil de trabajo. Es posible que se supervise o se modifique tu tráfico de red seguro."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Se ha instalado una entidad de certificación en este dispositivo. Es posible que se supervise o se modifique tu tráfico de red seguro."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silencio"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predeterminado"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Burbuja"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sin sonido ni vibración y se muestra más abajo en la sección de conversaciones"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Es posible que suene o vibre según los ajustes del teléfono"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Es posible que suene o vibre según los ajustes del teléfono. Las conversaciones de <xliff:g id="APP_NAME">%1$s</xliff:g> aparecen como burbujas de forma predeterminada."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Llama tu atención con un acceso directo flotante a este contenido."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Haz que el sistema determine si con esta notificación el dispositivo debe sonar o vibrar"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Se muestra arriba en la sección de conversaciones en forma de burbuja flotante, y la imagen de perfil aparece en la pantalla de bloqueo"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ajustes"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Esta aplicación se está mostrando sobre otras aplicaciones en tu pantalla y está usando el micrófono y la cámara."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Ajustes"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Aceptar"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"El sistema ha silenciado esta notificación."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"El sistema ha promocionado esta notificación."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"El sistema ha disminuido el nivel de esta notificación."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"¿Estuvo bien?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Gracias por tus comentarios."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Aceptar"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Se han abierto los controles de las notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Se han cerrado los controles de las notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Permite las notificaciones de este canal"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Cambiar el orden de los ajustes."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Mostrar"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizar"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Cerrar"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Ajustes"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Arrastra hacia abajo para ignorar"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menú"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> está en imagen en imagen"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Si no quieres que <xliff:g id="NAME">%s</xliff:g> utilice esta función, toca la notificación para abrir los ajustes y desactivarla."</string>
- <string name="pip_play" msgid="333995977693142810">"Reproducir"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pausar"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Saltar al siguiente"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Volver al anterior"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Teléfono apagado por calor"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"El teléfono ahora funciona con normalidad"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"El teléfono ya funciona con normalidad.\nToca para ver más información"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"El teléfono se había calentado demasiado y se ha apagado para enfriarse. Ahora funciona con normalidad.\n\nPuede calentarse demasiado si:\n • Usas aplicaciones que consumen muchos recursos (p. ej., apps de juegos, vídeos o navegación)\n • Descargas o subes archivos grandes\n • Lo usas a altas temperaturas"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver pasos de mantenimiento"</string>
<string name="high_temp_title" msgid="2218333576838496100">"El teléfono se está calentando"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Se limitan algunas funciones mientras el teléfono se enfría"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Se han limitado algunas funciones mientras el teléfono se enfría.\nToca para ver más información"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"El teléfono intentará enfriarse. Puedes seguir utilizándolo, pero es posible que funcione con mayor lentitud.\n\nUna vez que se haya enfriado, funcionará con normalidad."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantenimiento"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Desconecta el cargador"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"No se puede cargar el dispositivo. Desconecta el adaptador de corriente con cuidado, ya que el cable puede estar caliente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver pasos de mantenimiento"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Mantén pulsado y arrastra un control para reubicarlo"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Todos los controles quitados"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"No se han guardado los cambios"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Ver otras aplicaciones"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"No se han podido cargar los controles. Comprueba que no hayan cambiado los ajustes de la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Los controles compatibles no están disponibles"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Otros"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirma el cambio de <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Desliza el dedo para ver más"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Cerrar esta sesión multimedia"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar la sesión."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Ajustes"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo, comprobar aplicación"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Error; reintentando…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No se ha encontrado"</string>
diff --git a/packages/SystemUI/res/values-es/strings_tv.xml b/packages/SystemUI/res/values-es/strings_tv.xml
index e18d9b6075bc..6a72a3d3a77e 100644
--- a/packages/SystemUI/res/values-es/strings_tv.xml
+++ b/packages/SystemUI/res/values-es/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Imagen en imagen"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programa sin título)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Cerrar PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Pantalla completa"</string>
<string name="mic_active" msgid="5766614241012047024">"Micrófono activado"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ha accedido a tu micrófono"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 021931f468e1..a46fa490b4a8 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Avamiseks puudutage uuesti"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Pühkige avamiseks üles"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Uuesti proovimiseks pühkige üles"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"See seade kuulub teie organisatsioonile"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Selle seadme omanik on <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefoni kasutamiseks pühkige ikoonilt eemale"</string>
<string name="voice_hint" msgid="7476017460191291417">"Häälabi kasutamiseks pühkige ikoonilt eemale"</string>
<string name="camera_hint" msgid="4519495795000658637">"Kaamera kasutamiseks pühkige ikoonilt eemale"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profiili võidakse jälgida"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Võrku võidakse jälgida"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Võrku võidakse jälgida"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Teie organisatsioon on selle seadme omanik ja võib jälgida võrguliiklust"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> on selle seadme omanik ja võib jälgida võrguliiklust"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"See seade kuulub teie organisatsioonile ja on ühendatud rakendusega <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"See seade kuulub organisatsioonile <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ja on ühendatud rakendusega <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"See seade kuulub teie organisatsioonile"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Selle seadme omanik on <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"See seade kuulub teie organisatsioonile ja on ühendatud VPN-idega"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"See seade kuulub organisatsioonile <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ja on ühendatud VPN-idega"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Teie organisatsioon võib jälgida teie tööprofiilil võrguliiklust"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> võib jälgida võrguliiklust teie tööprofiilil"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Võrku võidakse jälgida"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"See seade on ühendatud VPN-idega"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Teie tööprofiil on ühendatud rakendusega <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Teie isiklik profiil on ühendatud rakendusega <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"See seade on ühendatud rakendusega <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Seadmehaldus"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profiili jälgimine"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Võrgu jälgimine"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Keela VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Katkesta VPN-i ühendus"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Kuva eeskirjad"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"See seade kuulub organisatsioonile <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nIT-administraator saab jälgida ning hallata seadeid, ettevõttesisest juurdepääsu, rakendusi, seadmega seotud andmeid ja seadme asukohateavet.\n\nLisateabe saamiseks võtke ühendust IT-administraatoriga."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"See seade kuulub teie organisatsioonile.\n\nIT-administraator saab jälgida ning hallata seadeid, ettevõttesisest juurdepääsu, rakendusi, seadmega seotud andmeid ja seadme asukohateavet.\n\nLisateabe saamiseks võtke ühendust IT-administraatoriga."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Teie organisatsioon installis sellesse seadmesse sertifikaadi volituse. Teie turvalist võrguliiklust võidakse jälgida ja muuta."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Teie organisatsioon installis teie tööprofiilile sertifikaadi volituse. Teie turvalist võrguliiklust võidakse jälgida ja muuta."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sertifikaadi volitus on sellesse seadmesse installitud. Teie turvalist võrguliiklust võidakse jälgida ja muuta."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Hääletu"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Vaikeseade"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Mull"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automaatne"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ilma heli ja vibreerimiseta"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ilma heli ja vibreerimiseta, kuvatakse vestluste jaotises allpool"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Võib telefoni seadete põhjal heliseda või vibreerida"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Võib telefoni seadete põhjal heliseda või vibreerida. Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> vestlused kuvatakse vaikimisi mullis."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Hoiab teie tähelepanu hõljuva otseteega selle sisu juurde."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laske süsteemil määrata, kas selle märguande puhul peaks esitama heli või vibreerima"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Kuvatakse vestluste jaotise ülaosas hõljuva mullina ja lukustuskuval kuvatakse profiilipilt"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Seaded"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioriteetne"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"See rakendus kuvatakse teie ekraanil muude rakenduste peal ning see kasutab mikrofoni ja kaamerat."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Seaded"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Selle märguande vaigistas süsteem."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Selle märguande ülendas süsteem."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Selle märguande alandas süsteem."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Kas see oli õige?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Täname tagasiside eest!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguannete juhtelemendid on avatud"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguannete juhtelemendid on suletud"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Lubab selle kanali märguanded"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Muuda seadete järjestust."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Leht <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lukustuskuva"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Laiendamine"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimeeri"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Sule"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Seaded"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Loobumiseks lohistage alla"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menüü"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> on režiimis Pilt pildis"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Kui te ei soovi, et rakendus <xliff:g id="NAME">%s</xliff:g> seda funktsiooni kasutaks, puudutage seadete avamiseks ja lülitage see välja."</string>
- <string name="pip_play" msgid="333995977693142810">"Esita"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Peata"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Järgmise juurde"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Eelmise juurde"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Tel. lül. kuumuse tõttu välja"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefon töötab nüüd tavapäraselt"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefon töötab nüüd tavapäraselt.\nPuudutage lisateabe saamiseks."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon oli liiga kuum, seetõttu lülitus see jahtumiseks välja. Telefon töötab nüüd tavapäraselt.\n\nTelefon võib kuumaks minna:\n • ressursse koormavate rakenduste kasutamisel (nt mängu-, video- või navigatsioonirakendused)\n • suurte failide alla-/üleslaadimisel\n • telefoni kasutamisel kõrgel temperatuuril"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Vaadake hooldusjuhiseid"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon soojeneb"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Mõned funktsioonid on piiratud, kuni telefon jahtub"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Mõned funktsioonid on piiratud, kuni telefon jahtub.\nPuudutage lisateabe saamiseks."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Teie telefon proovib automaatselt maha jahtuda. Saate telefoni ikka kasutada, kuid see võib olla aeglasem.\n\nKui telefon on jahtunud, töötab see tavapäraselt."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Vaadake hooldusjuhiseid"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Eemaldage laadija vooluvõrgust"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Selle seadme laadimisega on probleem. Eemaldage toiteadapter ja olge ettevaatlik, sest kaabel võib olla soe."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Vaadake hooldusjuhiseid"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Juhtelementide ümberpaigutamiseks hoidke neid all ja lohistage"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Kõik juhtelemendid eemaldati"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Muudatusi ei salvestatud"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Kuva muud rakendused"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Juhtelemente ei õnnestunud laadida. Kontrollige rakendust <xliff:g id="APP">%s</xliff:g> ja veenduge, et rakenduse seaded poleks muutunud."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Ühilduvaid juhtelemente pole saadaval"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Muu"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Kinnitage seadme <xliff:g id="DEVICE">%s</xliff:g> muudatus"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Pühkige sõrmega, et näha rohkem"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Soovituste laadimine"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Sulge see meediaseanss"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Meedia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Peidetakse praegune seanss."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Peida"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Jätka"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Seaded"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Passiivne, vaadake rakendust"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Viga, proovitakse uuesti …"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ei leitud"</string>
diff --git a/packages/SystemUI/res/values-et/strings_tv.xml b/packages/SystemUI/res/values-et/strings_tv.xml
index f36d8ec89eed..61c14356809b 100644
--- a/packages/SystemUI/res/values-et/strings_tv.xml
+++ b/packages/SystemUI/res/values-et/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Pilt pildis"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programmi pealkiri puudub)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Sule PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Täisekraan"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon on aktiivne"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s pääses teie mikrofonile juurde"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 4da5f67f80ea..19fc4f50d0d3 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Irekitzeko, ukitu berriro"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Pasatu hatza gora irekitzeko"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Berriro saiatzeko, pasatu hatza gora"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Gailu hau zure erakundearena da"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Gailu hau <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> erakundearena da"</string>
<string name="phone_hint" msgid="6682125338461375925">"Pasatu hatza ikonotik, telefonoa irekitzeko"</string>
<string name="voice_hint" msgid="7476017460191291417">"Pasatu hatza ikonotik, ahots-laguntza irekitzeko"</string>
<string name="camera_hint" msgid="4519495795000658637">"Pasatu hatza ikonotik, kamera irekitzeko"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Baliteke profila kontrolatuta egotea"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Baliteke sarea kontrolatuta egotea"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Baliteke sarea kontrolatuta egotea"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Gailu hau zure erakundearena da, eta baliteke hark sareko trafikoa gainbegiratzea"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Gailu hau <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> erakundearena da, eta baliteke sareko trafikoa gainbegiratzea"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Gailu hau zure erakundearena da, eta <xliff:g id="VPN_APP">%1$s</xliff:g> sarera dago konektatuta"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Gailu hau <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> erakundearena da, eta <xliff:g id="VPN_APP">%2$s</xliff:g> sarera dago konektatuta"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Gailu hau zure erakundearena da"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Gailu hau <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> erakundearena da"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Gailu hau zure erakundearena da, eta VPN sareetara dago konektatuta"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Gailu hau <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> erakundearena da, eta VPN sareetara dago konektatuta"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Baliteke erakundeak laneko profileko sareko trafikoa gainbegiratzea"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Baliteke <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> erakundeak laneko profilaren sareko trafikoa gainbegiratzea"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Baliteke sarea gainbegiratuta egotea"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Gailu hau VPN sareetara dago konektatuta"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"<xliff:g id="VPN_APP">%1$s</xliff:g> sarera konektatuta daukazu laneko profila"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"<xliff:g id="VPN_APP">%1$s</xliff:g> sarera konektatuta daukazu profil pertsonala"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Gailu hau <xliff:g id="VPN_APP">%1$s</xliff:g> sarera dago konektatuta"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gailuaren kudeaketa"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profila kontrolatzeko aukera"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Sareen kontrola"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Desgaitu VPN konexioa"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Deskonektatu VPN sarea"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Ikusi gidalerroak"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Gailu hau <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> erakundearena da.\n\nIKT saileko administratzaileak gainbegiratu eta kudeatu egin ditzake ezarpenak, enpresa-sarbidea, aplikazioak, gailuarekin erlazionatutako datuak eta gailuaren kokapen-informazioa.\n\nInformazio gehiago lortzeko, jarri IKT saileko administratzailearekin harremanetan."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Gailu hau zure erakundearena da.\n\nIKT saileko administratzaileak gainbegiratu eta kudeatu egin ditzake ezarpenak, enpresa-sarbidea, aplikazioak, gailuarekin erlazionatutako datuak eta gailuaren kokapen-informazioa.\n\nInformazio gehiago lortzeko, jarri IKT saileko administratzailearekin harremanetan."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Erakundeak ziurtagiri-emaile bat instalatu du gailuan. Baliteke sareko trafiko segurua gainbegiratzea edo aldatzea."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Erakundeak ziurtagiri-emaile bat instalatu dizu laneko profilean. Baliteke sareko trafiko segurua gainbegiratzea edo aldatzea."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Ziurtagiri-emaile bat dago instalatuta gailuan. Baliteke sareko trafiko segurua gainbegiratzea edo aldatzea."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Isila"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Balio lehenetsia"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Burbuila"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatikoa"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ez du tonurik jotzen edo dar-dar egiten"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ez du tonurik jotzen edo dar-dar egiten, eta elkarrizketaren atalaren behealdean agertzen da"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Tonua jo edo dar-dar egin dezake, telefonoaren ezarpenen arabera"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Tonua jo edo dar-dar egin dezake, telefonoaren ezarpenen arabera. Modu lehenetsian, <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioko elkarrizketak burbuila gisa agertzen dira."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Eduki honetarako lasterbide gainerakor bat eskaintzen dizu, arretarik gal ez dezazun."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ezarri sistemak zehaztu dezala jakinarazpen honek soinua edo dardara egin behar duen ala ez"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Burbuila gisa agertzen da elkarrizketen atalaren goialdean, eta profileko argazkia bistaratzen du pantaila blokeatuta dagoenean"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ezarpenak"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Lehentasuna"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Aplikazioa pantailako beste aplikazioen gainean agertzen da, eta mikrofonoa eta kamera erabiltzen ari da."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Ezarpenak"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Ados"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Sistemak isilarazi egin du jakinarazpen hau."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Sistemak mailaz igo du jakinarazpen hau."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Sistemak mailaz jaitsi du jakinarazpen hau."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Zuzena al da hau?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Mila esker iritzia emateagatik!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Ados"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Ireki dira <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Itxi dira <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Onartu kanal honen jakinarazpenak"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editatu ezarpenen ordena."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g> orria"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantaila blokeatua"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Zabaldu"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizatu"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Itxi"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Ezarpenak"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Baztertzeko, arrastatu behera"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menua"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"Pantaila txiki gainjarrian dago <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string>
- <string name="pip_play" msgid="333995977693142810">"Erreproduzitu"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pausatu"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Saltatu hurrengora"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Saltatu aurrekora"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Beroegi egoteagatik itzali da"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Orain, ohiko moduan dabil telefonoa"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Ohi bezala ari da funtzionatzen telefonoa orain.\nInformazio gehiago lortzeko, sakatu hau."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonoa gehiegi berotu da, eta itzali egin da tenperatura jaisteko. Orain, ohiko moduan dabil.\n\nBerotzearen zergati posibleak:\n • Baliabide asko behar dituzten aplikazioak erabiltzea (adib., jokoak, bideoak edo nabigazio-aplikazioak).\n • Fitxategi handiak deskargatu edo kargatzea.\n • Telefonoa giro beroetan erabiltzea."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ikusi zaintzeko urratsak"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Berotzen ari da telefonoa"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Eginbide batzuk ezingo dira erabili telefonoa hoztu arte"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Eginbide batzuk ezingo dira erabili telefonoa hoztu arte.\nInformazio gehiago lortzeko, sakatu hau."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonoa automatikoki saiatuko da hozten. Hoztu bitartean, telefonoa erabiltzen jarrai dezakezu, baina mantsoago funtziona lezake.\n\nTelefonoaren tenperatura jaitsi bezain laster, ohi bezala funtzionatzen jarraituko du."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ikusi zaintzeko urratsak"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Deskonektatu kargagailua"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Arazo bat izan da gailua kargatzean. Deskonektatu egokigailua eta kontuz ibili, kablea bero egon baitaiteke."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ikusi zaintzeko urratsak"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Kontrolatzeko aukerak antolatzeko, eduki itzazu sakatuta, eta arrastatu"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Kendu dira kontrolatzeko aukera guztiak"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Ez dira gorde aldaketak"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Ikusi beste aplikazio batzuk"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Ezin izan dira kargatu kontrolatzeko aukerak. Joan <xliff:g id="APP">%s</xliff:g> aplikaziora, eta ziurtatu aplikazioaren ezarpenak ez direla aldatu."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Ez dago erabilgarri kontrolatzeko aukera bateragarririk"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Beste bat"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Berretsi <xliff:g id="DEVICE">%s</xliff:g> gailuaren aldaketa"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Pasatu hatza aukera gehiago ikusteko"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Gomendioak kargatzen"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Itxi multimedia-saio hau"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Multimedia-edukia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Ezkutatu uneko saioa."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ezkutatu"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Berrekin"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Ezarpenak"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktibo; egiaztatu aplikazioa"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Errorea. Berriro saiatzen…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ez da aurkitu"</string>
diff --git a/packages/SystemUI/res/values-eu/strings_tv.xml b/packages/SystemUI/res/values-eu/strings_tv.xml
index bdd1ffadf652..e77de5015fa1 100644
--- a/packages/SystemUI/res/values-eu/strings_tv.xml
+++ b/packages/SystemUI/res/values-eu/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Pantaila txiki gainjarria"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programa izengabea)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Itxi PIPa"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Pantaila osoa"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofonoa aktibatuta dago"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s aplikazioak mikrofonoa atzitu du"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 579da4d8a289..b8c87938d423 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -256,7 +256,7 @@
<!-- no translation found for accessibility_work_mode (1280025758672376313) -->
<skip />
<string name="accessibility_notification_dismissed" msgid="4411652015138892952">"اعلان ردشد."</string>
- <string name="accessibility_bubble_dismissed" msgid="270358867566720729">"ابزارک اعلان رد شد."</string>
+ <string name="accessibility_bubble_dismissed" msgid="270358867566720729">"حبابک رد شد."</string>
<string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"مجموعه اعلان."</string>
<string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"تنظیمات سریع."</string>
<string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"صفحه قفل."</string>
@@ -426,9 +426,9 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"تا طلوع آفتاب"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ساعت <xliff:g id="TIME">%s</xliff:g> روشن می‌شود"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"تا<xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
- <string name="quick_settings_nfc_off" msgid="3465000058515424663">"‏NFC غیرفعال است"</string>
- <string name="quick_settings_nfc_on" msgid="1004976611203202230">"‏NFC فعال است"</string>
+ <string name="quick_settings_nfc_label" msgid="1054317416221168085">"‏ارتباط میدان نزدیک (NFC)"</string>
+ <string name="quick_settings_nfc_off" msgid="3465000058515424663">"‏«ارتباط میدان نزدیک» (NFC) غیرفعال است"</string>
+ <string name="quick_settings_nfc_on" msgid="1004976611203202230">"‏«ارتباط میدان نزدیک» (NFC) فعال است"</string>
<string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ضبط کردن صفحه‌نمایش"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"شروع"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"توقف"</string>
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"دوباره ضربه بزنید تا باز شود"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند به بالا بکشید"</string>
<string name="keyguard_retry" msgid="886802522584053523">"برای امتحان مجدد، انگشتتان را تند به بالا بکشید"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"این دستگاه به سازمان شما تعلق دارد"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"این دستگاه به <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> تعلق دارد"</string>
<string name="phone_hint" msgid="6682125338461375925">"انگشتتان را از نماد تلفن تند بکشید"</string>
<string name="voice_hint" msgid="7476017460191291417">"برای «دستیار صوتی»، تند بکشید"</string>
<string name="camera_hint" msgid="4519495795000658637">"انگشتتان را از نماد دوربین تند بکشید"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"شاید نمایه کنترل شود"</string>
<string name="vpn_footer" msgid="3457155078010607471">"ممکن است شبکه کنترل شود"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ممکن است شبکه کنترل شود"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"مالک این دستگاه سازمان شما است و ممکن است ترافیک شبکه را پایش کند"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"مالک این دستگاه <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> است و ممکن است ترافیک شبکه را پایش کند"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"این دستگاه به سازمان شما تعلق دارد و به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"این دستگاه به <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> تعلق دارد و به <xliff:g id="VPN_APP">%2$s</xliff:g> متصل است"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"این دستگاه به سازمان شما تعلق دارد"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"این دستگاه به <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> تعلق دارد"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"‏این دستگاه به سازمان شما تعلق دارد و به شبکه‌های VPN متصل است"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"‏این دستگاه به <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> تعلق دارد و به VPN متصل است"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"ممکن است سازمان شما ترافیک شبکه را در نمایه کاری‌تان پایش کند"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ممکن است ترافیک شبکه را در نمایه کاری شما پایش کند"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ممکن است شبکه پایش شود"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"‏این دستگاه به شبکه‌های VPN متصل است"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"نمایه کاری به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"نمایه شخصی شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"این دستگاه به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"مدیریت دستگاه"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"کنترل نمایه"</string>
<string name="monitoring_title" msgid="4063890083735924568">"کنترل شبکه"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"‏غیرفعال کردن VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"‏قطع اتصال VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"مشاهده خط‌مشی‌ها"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"این دستگاه به <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> تعلق دارد.\n\nسرپرست فناوری اطلاعات می‌تواند تنظیمات، دسترسی شرکتی، برنامه‌ها، داده‌های مرتبط با دستگاه، و اطلاعات مکان دستگاهتان را کنترل و مدیریت کند.\n\nبرای اطلاعات بیشتر، با سرپرست فناوری و اطلاعات تماس بگیرید."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"این دستگاه به سازمان شما تعلق دارد.\n\nسرپرست فناوری اطلاعات می‌تواند تنظیمات، دسترسی شرکتی، برنامه‌ها، و داده‌های مرتبط با دستگاه و اطلاعات مکان دستگاهتان را کنترل و مدیریت کند.\n\nبرای اطلاعات بیشتر، با سرپرست فناوری و اطلاعات تماس بگیرید."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"سازمان شما مرجع گواهینامه‌ای در این دستگاه نصب کرده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"سازمان شما مرجع گواهینامه‌ای در نمایه کاری شما نصب کرده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"مرجع گواهینامه‌ای در این دستگاه نصب شده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string>
@@ -727,21 +711,19 @@
<string name="notification_silence_title" msgid="8608090968400832335">"بی‌صدا"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"پیش‌فرض"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"حباب"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"بدون صدا یا لرزش"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"بدون صدا و لرزش در پایین بخش مکالمه نشان داده می‌شود"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"بسته به تنظیمات ممکن است تلفن زنگ بزند یا لرزش داشته باشد"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"بسته به تنظیمات ممکن است تلفن زنگ بزند یا لرزش داشته باشد. مکالمه‌های <xliff:g id="APP_NAME">%1$s</xliff:g> به‌طور پیش‌فرض در حبابک نشان داده می‌شوند."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"با میان‌بری شناور به این محتوا، توجه‌تان را جلب می‌کند."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"سیستم را تنظیم کنید که تشخیص دهد اعلان صدا و لرزش داشته باشد یا نه"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"در بالای بخش مکالمه به‌صورت حبابک شناور نشان داده می‌شود و تصویر نمایه را در صفحه قفل نمایش می‌دهد"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"تنظیمات"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"اولویت"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> از ویژگی‌های مکالمه پشتیبانی نمی‌کند"</string>
- <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"هیچ ابزارک اعلان جدیدی وجود ندارد"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"ابزارک اعلان اخیر و ابزارک اعلان ردشده اینجا ظاهر خواهند شد"</string>
+ <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"هیچ حبابک جدیدی وجود ندارد"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"حبابک‌ها اخیر و حبابک‌ها ردشده اینجا ظاهر خواهند شد"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"این اعلان‌ها قابل اصلاح نیستند."</string>
<string name="notification_multichannel_desc" msgid="7414593090056236179">"نمی‌توانید این گروه اعلان‌ها را در اینجا پیکربندی کنید"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"اعلان‌های دارای پراکسی"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"این برنامه روی برنامه‌های دیگر در صفحه‌نمایش نشان داده می‌شود و از میکروفون و دوربین استفاده می‌کند."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"تنظیمات"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"تأیید"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"سیستم این اعلان را بی‌صدا کرده است."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"سیستمْ این اعلان را ارتقا داده است."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"سیستمْ این اعلان را تنزل داده است."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"این مورد درست بود؟"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"از بازخورد شما سپاسگزاریم!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"تأیید"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"کنترل‌های اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> باز شد"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"کنترل‌های اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> بسته شد"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"مجاز کردن اعلان‌های این کانال"</string>
@@ -780,8 +756,8 @@
<string name="notification_conversation_unfavorite" msgid="181383708304763807">"مکالمه غیرمهم"</string>
<string name="notification_conversation_mute" msgid="268951550222925548">"بی‌صدا شد"</string>
<string name="notification_conversation_unmute" msgid="2692255619510896710">"هشدار دادن"</string>
- <string name="notification_conversation_bubble" msgid="2242180995373949022">"نمایش ابزارک اعلان"</string>
- <string name="notification_conversation_unbubble" msgid="6908427185031099868">"برداشتن ابزارک اعلان"</string>
+ <string name="notification_conversation_bubble" msgid="2242180995373949022">"نمایش حبابک"</string>
+ <string name="notification_conversation_unbubble" msgid="6908427185031099868">"برداشتن حبابک‌ها"</string>
<string name="notification_conversation_home_screen" msgid="8347136037958438935">"افزودن به صفحه اصلی"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"کنترل‌های اعلان"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ویرایش ترتیب تنظیمات."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"صفحه <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"صفحه قفل"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"بزرگ کردن"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"کوچک کردن"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"بستن"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"تنظیمات"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"برای نپذیرفتن، به پایین بکشید"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"منو"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> درحالت تصویر در تصویر است"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"اگر نمی‌خواهید <xliff:g id="NAME">%s</xliff:g> از این قابلیت استفاده کند، با ضربه زدن، تنظیمات را باز کنید و آن را خاموش کنید."</string>
- <string name="pip_play" msgid="333995977693142810">"پخش"</string>
- <string name="pip_pause" msgid="1139598607050555845">"توقف موقت"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"رد شدن به بعدی"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"رد شدن به قبلی"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"تلفن به علت گرم شدن خاموش شد"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"اکنون تلفنتان عملکرد معمولش را دارد"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"اکنون عملکرد تلفنتان به حالت عادی برگشته است.\nبرای اطلاعات بیشتر ضربه بزنید"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"تلفنتان خیلی گرم شده بود، بنابراین خاموش شد تا خنک شود. اکنون تلفنتان عملکرد معمولش را دارد.\n\nتلفنتان خیلی گرم می‌شود، اگر:\n • از برنامه‌های نیازمند پردازش زیاد (مانند بازی، برنامه‌های ویدیویی یا پیمایشی) استفاده کنید\n • فایل‌های بزرگ بارگیری یا بارگذاری کنید\n • در دماهای بالا از تلفنتان استفاده کنید"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"دیدن اقدامات محافظتی"</string>
<string name="high_temp_title" msgid="2218333576838496100">"تلفن درحال گرم شدن است"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"وقتی تلفن درحال خنک شدن است، بعضی از قابلیت‌ها محدود می‌شوند"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"وقتی تلفن درحال خنک شدن است، بعضی از ویژگی‌ها محدود می‌شوند.\nبرای اطلاعات بیشتر ضربه بزنید"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"تلفنتان به‌طور خودکار سعی می‌کند خنک شود. همچنان می‌توانید از تلفنتان استفاده کنید، اما ممکن است کندتر عمل کند.\n\nوقتی تلفن خنک شد، عملکرد عادی‌اش از سرگرفته می‌شود."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"دیدن اقدامات محافظتی"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"جدا کردن شارژر از برق"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"مشکلی در شارژ کردن این دستگاه وجود دارد. آداپتور برق را از برق جدا کنید و مراقب باشید زیرا ممکن است کابل گرم باشد."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"مشاهده مراحل احتیاط"</string>
@@ -1025,7 +989,7 @@
<string name="device_services" msgid="1549944177856658705">"سرویس‌های دستگاه"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"بدون عنوان"</string>
<string name="restart_button_description" msgid="6916116576177456480">"برای بازراه‌اندازی این برنامه و تغییر به حالت تمام‌صفحه، ضربه بزنید."</string>
- <string name="bubbles_settings_button_description" msgid="7324245408859877545">"تنظیم برای ابزارک‌های اعلان <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubbles_settings_button_description" msgid="7324245408859877545">"تنظیمات برای حبابک‌های <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="5523744621434300510">"لبریزشده"</string>
<string name="bubble_accessibility_action_add_back" msgid="6217995665917123890">"افزودن برگشت به پشته"</string>
<string name="manage_bubbles_text" msgid="6856830436329494850">"مدیریت"</string>
@@ -1038,10 +1002,10 @@
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"انتقال به پایین سمت چپ"</string>
<string name="bubble_dismiss_text" msgid="1314082410868930066">"رد کردن حبابک"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"مکالمه در حباب نشان داده نشود"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"گپ بااستفاده از ابزارک اعلان"</string>
- <string name="bubbles_user_education_description" msgid="1160281719576715211">"مکالمه‌های جدید به‌صورت نمادهای شناور یا ابزارک اعلان نشان داده شوند. برای باز کردن ابزارک اعلان ضربه بزنید. برای جابه‌جایی، آن را بکشید."</string>
- <string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"کنترل ابزارک اعلان در هرزمانی"</string>
- <string name="bubbles_user_education_manage" msgid="1391639189507036423">"برای خاموش کردن «ابزارک اعلان» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"گپ بااستفاده از حبابک‌ها"</string>
+ <string name="bubbles_user_education_description" msgid="1160281719576715211">"مکالمه‌های جدید به‌صورت نمادهای شناور یا حبابک‌ها نشان داده می‌شوند. برای باز کردن حبابک‌ها ضربه بزنید. برای جابه‌جایی، آن را بکشید."</string>
+ <string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"کنترل حبابک‌ها در هرزمانی"</string>
+ <string name="bubbles_user_education_manage" msgid="1391639189507036423">"برای خاموش کردن «حبابک‌ها» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
<string name="bubbles_user_education_got_it" msgid="8282812431953161143">"متوجه‌ام"</string>
<string name="bubbles_app_settings" msgid="5779443644062348657">"تنظیمات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"پیمایش سیستم به‌روزرسانی شد. برای انجام تغییرات به «تنظیمات» بروید."</string>
@@ -1050,8 +1014,8 @@
<string name="priority_onboarding_title" msgid="2893070698479227616">"مکالمه روی اولویت تنظیم شده است"</string>
<string name="priority_onboarding_behavior" msgid="5342816047020432929">"مکالمه‌های اولویت‌دار:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"نمایش در بالای بخش مکالمه"</string>
- <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"نمایش تصویر نمایه در صفحه قفل"</string>
- <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"به‌شکل ابزارک اعلان شناور روی برنامه‌ها ظاهر می‌شود"</string>
+ <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"تصویر نمایه را در صفحه قفل نمایش می‌دهد"</string>
+ <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"به‌شکل حبابک شناور روی برنامه‌ها ظاهر می‌شود"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"وقفه در «مزاحم نشوید»"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"متوجه‌ام"</string>
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"تنظیمات"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"برای تغییر دادن ترتیب کنترل‌ها، آن‌ها را نگه دارید و بکشید"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"همه کنترل‌ها برداشته شده‌اند"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"تغییرات ذخیره نشد"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"دیدن برنامه‌های دیگر"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"کنترل‌ها بار نشدند. برنامه <xliff:g id="APP">%s</xliff:g> را بررسی کنید تا مطمئن شوید تنظیمات برنامه تغییر نکرده باشد."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"کنترل‌های سازگار دردسترس نیستند"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"موارد دیگر"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"تأیید تغییر مربوط به <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"برای دیدن موارد بیشتر، تند بکشید"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"درحال بار کردن توصیه‌ها"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"بستن این جلسه رسانه"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"رسانه"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"جلسه فعلی پنهان شود."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"پنهان کردن"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ازسرگیری"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"تنظیمات"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"غیرفعال، برنامه را بررسی کنید"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"خطا، درحال تلاش مجدد…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"پیدا نشد"</string>
diff --git a/packages/SystemUI/res/values-fa/strings_tv.xml b/packages/SystemUI/res/values-fa/strings_tv.xml
index fb6d42c2e3ba..bf3987d6e10d 100644
--- a/packages/SystemUI/res/values-fa/strings_tv.xml
+++ b/packages/SystemUI/res/values-fa/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"تصویر در تصویر"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(برنامه بدون عنوان)"</string>
- <string name="pip_close" msgid="5775212044472849930">"‏بستن PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"تمام صفحه"</string>
<string name="mic_active" msgid="5766614241012047024">"میکروفون فعال است"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"‏%1$s به میکروفون شما دسترسی پیدا کرد"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 73ee434881b2..44a66c31b954 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Avaa napauttamalla uudelleen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Avaa pyyhkäisemällä ylös"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Yritä uudelleen pyyhkäisemällä ylös"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Organisaatiosi omistaa tämän laitteen"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g> omistaa tämän laitteen"</string>
<string name="phone_hint" msgid="6682125338461375925">"Avaa puhelu pyyhkäisemällä."</string>
<string name="voice_hint" msgid="7476017460191291417">"Avaa ääniapuri pyyhkäisemällä kuvakkeesta."</string>
<string name="camera_hint" msgid="4519495795000658637">"Avaa kamera pyyhkäisemällä."</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profiilia saatetaan valvoa"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Verkkoa saatetaan valvoa"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Verkkoa saatetaan valvoa"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisaatiosi omistaa laitteen ja voi valvoa verkkoliikennettä"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> omistaa laitteen ja voi valvoa verkkoliikennettä"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Organisaatiosi omistaa laitteen, joka on yhdistetty tähän: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> omistaa laitteen, joka on yhdistetty tähän: <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Organisaatiosi omistaa tämän laitteen"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> omistaa tämän laitteen"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Organisaatiosi omistaa tämän laitteen, joka on yhdistetty VPN:iin"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> omistaa tämän laitteen, joka on yhdistetty VPN:iin"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Organisaatiosi voi valvoa työprofiilisi verkkoliikennettä."</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> voi valvoa työprofiilisi verkkoliikennettä."</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Verkkoa saatetaan valvoa"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Tämä laite on yhdistetty VPN:iin"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Työprofiilisi on yhdistetty tähän: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Henkilökohtainen profiilisi on yhdistetty tähän: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Laite on yhdistetty tähän: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Laitehallinta"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profiilin valvonta"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Verkon valvonta"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Poista VPN käytöstä"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Katkaise VPN-yhteys"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Näytä säännöt"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> omistaa tämän laitteen.\n\nJärjestelmänvalvoja voi valvoa ja muuttaa asetuksia, yrityskäyttöä, sovelluksia sekä laitteeseen yhdistettyjä tietoja ja sen sijaintitietoja.\n\nSaat lisätietoja järjestelmänvalvojalta."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Organisaatiosi omistaa tämän laitteen.\n\nJärjestelmänvalvoja voi valvoa ja muuttaa asetuksia, yrityskäyttöä, sovelluksia sekä laitteeseen yhdistettyjä tietoja ja sen sijaintitietoja.\n\nSaat lisätietoja järjestelmänvalvojalta."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organisaatiosi asensi laitteeseen varmenteen myöntäjän. Suojattua verkkoliikennettäsi voidaan valvoa tai muuttaa."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisaatiosi lisäsi työprofiiliin varmenteen myöntäjän. Suojattua verkkoliikennettäsi voidaan valvoa tai muuttaa."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Laitteeseen on asennettu varmenteen myöntäjä. Suojattua verkkoliikennettäsi voidaan valvoa tai muuttaa."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Äänetön"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Oletus"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Kupla"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automaattinen"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ei ääntä tai värinää"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ei ääntä tai värinää ja näkyy alempana keskusteluosiossa"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Voi soida tai väristä puhelimen asetuksista riippuen"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Voi soida tai väristä puhelimen asetuksista riippuen. Näistä keskusteluista (<xliff:g id="APP_NAME">%1$s</xliff:g>) syntyy oletuksena kuplia."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Kelluva sisällön pikakuvake säilyttää huomiosi"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Järjestelmä valitsee, kuuluuko tästä ilmoituksesta ääntä tai väriseekö se"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Näkyy keskusteluosion yläosassa kelluvana kuplana, profiilikuva näkyy lukitusnäytöllä"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Asetukset"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Tärkeä"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Tämä sovellus näkyy näytöllä muiden sovellusten päällä ja käyttää mikrofonia sekä kameraa."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Asetukset"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Järjestelmä hiljensi tämän ilmoituksen."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Järjestelmä nosti tämän ilmoituksen tasoa."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Järjestelmä laski tämän ilmoituksen tasoa."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Oliko tämä oikein?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Kiitos palautteesta!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> ilmoitusten hallinta on avattu."</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> ilmoitusten hallinta on suljettu."</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Salli ilmoitukset tältä kanavalta."</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Muokkaa asetusten järjestystä."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Sivu <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lukitusnäyttö"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Laajenna"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Pienennä"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Sulje"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Asetukset"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Hylkää vetämällä alas."</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Valikko"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> on kuva kuvassa ‑tilassa"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Jos et halua, että <xliff:g id="NAME">%s</xliff:g> voi käyttää tätä ominaisuutta, avaa asetukset napauttamalla ja poista se käytöstä."</string>
- <string name="pip_play" msgid="333995977693142810">"Toista"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Keskeytä"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Siirry seuraavaan"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Siirry edelliseen"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Puhelin sammui kuumuuden takia"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Puhelimesi toimii nyt normaalisti."</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Puhelimesi toimii nyt normaalisti.\nLue lisää napauttamalla"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Puhelimesi oli liian kuuma, joten se sammui. Puhelimesi toimii nyt normaalisti.\n\nPuhelimesi voi kuumentua liikaa, jos\n • käytät paljon resursseja vaativia sovelluksia (esim. pelejä, videoita tai navigointisovelluksia)\n • lataat tai lähetät suuria tiedostoja\n • käytät puhelintasi korkeissa lämpötiloissa."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Katso huoltovaiheet"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Puhelin lämpenee"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Joidenkin ominaisuuksien käyttöä on rajoitettu puhelimen jäähtymisen aikana."</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Joidenkin ominaisuuksien käyttöä on rajoitettu puhelimen jäähtymisen aikana.\nLue lisää napauttamalla"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Puhelimesi yrittää automaattisesti jäähdyttää itsensä. Voit silti käyttää puhelinta, mutta se voi toimia hitaammin.\n\nKun puhelin on jäähtynyt, se toimii normaalisti."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Katso huoltovaiheet"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Irrota laturi"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Laitetta ladattaessa tapahtui virhe. Irrota virtalähde varovasti – johto voi olla lämmin."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Katso huoltovaiheet"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Järjestele säätimiä koskettamalla pitkään ja vetämällä"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Kaikki säätimet poistettu"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Muutoksia ei tallennettu"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Katso muita sovelluksia"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Säätimiä ei voitu ladata. Avaa <xliff:g id="APP">%s</xliff:g> ja tarkista, että sovelluksen asetukset eivät ole muuttuneet."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Yhteensopivat säätimet eivät käytettävissä"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Muu"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Vahvista muutos: <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Pyyhkäise nähdäksesi lisää"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ladataan suosituksia"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Sulje tämä median käyttökerta"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Piilota nykyinen käyttökerta."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Piilota"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Epäaktiivinen, tarkista sovellus"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Virhe, yritetään uudelleen…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ei löydy"</string>
diff --git a/packages/SystemUI/res/values-fi/strings_tv.xml b/packages/SystemUI/res/values-fi/strings_tv.xml
index e22a1660db0c..0b1f02f4cad4 100644
--- a/packages/SystemUI/res/values-fi/strings_tv.xml
+++ b/packages/SystemUI/res/values-fi/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Kuva kuvassa"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Nimetön)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Sulje PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Koko näyttö"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofoni aktiivinen"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s sai pääsyn mikrofoniisi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 49f0c8b7f6dd..15cf6eb40658 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Touchez à nouveau pour ouvrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Balayez l\'écran vers le haut pour ouvrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Balayez l\'écran vers le haut pour réessayer"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Cet appareil appartient à votre organisation"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Balayez à partir de l\'icône pour accéder au téléphone"</string>
<string name="voice_hint" msgid="7476017460191291417">"Balayez à partir de l\'icône pour accéder à l\'assist. vocale"</string>
<string name="camera_hint" msgid="4519495795000658637">"Balayez à partir de l\'icône pour accéder à l\'appareil photo"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"le profil peut être contrôlé"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Le réseau peut être surveillé"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Le réseau peut être surveillé"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Votre organisation possède cet appareil et peut contrôler le trafic réseau"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> possède cet appareil et peut contrôler le trafic réseau"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Cet appareil appartient à votre organisation et est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> et est connecté à <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Cet appareil appartient à votre organisation"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Cet appareil appartient à votre organisation et est connecté à des RPV"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> et est connecté à des RPV"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Votre organisation peut contrôler le trafic réseau dans votre profil professionnel"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> peut contrôler votre trafic réseau dans votre profil professionnel"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Le réseau peut être surveillé"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Cet appareil est connecté à des RPV"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Votre profil professionnel est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Votre profil personnel est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Cet appareil est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gestion d\'appareils"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Contrôle de profil"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Surveillance réseau"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Désactiver le RPV"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Déconnecter le RPV"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Afficher les politiques"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Votre appareil appartient à <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nVotre administrateur informatique peut contrôler et gérer les paramètres, l\'accès aux données d\'entreprise, les applications, les données associées à l\'appareil et les renseignements sur sa position.\n\nPour obtenir plus d\'information, communiquez avec votre administrateur informatique."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Cet appareil appartient à votre organisation.\n\nVotre administrateur informatique peut contrôler et gérer les paramètres, l\'accès aux données d\'entreprise, les applications, les données associées à votre appareil et les renseignements sur sa position.\n\nPour obtenir plus d\'information, communiquez avec votre administrateur informatique."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Votre entreprise a installé une autorité de certification sur cet appareil. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Votre entreprise a installé une autorité de certification dans votre profil professionnel. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Une autorité de certification est installée sur cet appareil. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Mode silencieux"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Par défaut"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bulle"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Aucun son ni vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Aucun son ni vibration, et s\'affiche plus bas dans la section des conversations"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Peut sonner ou vibrer, selon les paramètres du téléphone"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Peut sonner ou vibrer, selon les paramètres du téléphone. Conversations des bulles de <xliff:g id="APP_NAME">%1$s</xliff:g> par défaut."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Garde votre attention à l\'aide d\'un raccourci flottant vers ce contenu."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faire en sorte que le système détermine si cette notification devrait émettre un son ou vibrer"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"S\'affiche en haut de la section des conversations sous forme de bulle flottante et affiche la photo du profil sur l\'écran de verrouillage"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Paramètres"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priorité"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Cette application superpose du contenu par-dessus d\'autres applications à l\'écran et utilise le microphone et l\'appareil photo."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Paramètres"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Cette notification a été désactivée par le système."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Cette notification a été promue par le système."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Cette notification a été rétrogradée par le système."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Était-ce correct?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Merci de vos commentaires!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Les paramètres des notifications pour <xliff:g id="APP_NAME">%1$s</xliff:g> sont ouverts"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Les paramètres des notifications pour <xliff:g id="APP_NAME">%1$s</xliff:g> sont fermés"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Autoriser les notifications de cette chaîne"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Modifier l\'ordre des paramètres."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> sur <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Écran de verrouillage"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Développer"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Réduire"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Fermer"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Paramètres"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Faire glisser vers le bas pour ignorer"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> est en mode d\'incrustation d\'image"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Si vous ne voulez pas que <xliff:g id="NAME">%s</xliff:g> utilise cette fonctionnalité, touchez l\'écran pour ouvrir les paramètres, puis désactivez-la."</string>
- <string name="pip_play" msgid="333995977693142810">"Lire"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Interrompre"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Passer au suivant"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Revenir au précédent"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Tél. éteint car il surchauffait"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Votre téléphone fonctionne maintenant normalement"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Votre téléphone fonctionne maintenant de manière normale.\nTouchez pour en savoir plus"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Votre téléphone s\'est éteint, car il surchauffait. Il s\'est refroidi et fonctionne normalement.\n\nIl peut surchauffer si vous :\n • Util. des applis utilisant beaucoup de ressources (jeux, vidéo, navigation, etc.)\n • Téléchargez ou téléversez de gros fichiers\n • Utilisez téléphone dans des températures élevées"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Afficher les étapes d\'entretien"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Le téléphone commence à chauffer"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Les fonctionnalités sont limitées pendant que le téléphone refroidit"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Certaines fonctionnalités sont limitées pendant que le téléphone refroidit.\nTouchez pour en savoir plus"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Votre téléphone va essayer de se refroidir automatiquement. Vous pouvez toujours l\'utiliser, mais il risque d\'être plus lent.\n\nUne fois refroidi, il fonctionnera normalement."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Afficher les étapes d\'entretien"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Débranchez le chargeur"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Il y a un problème avec la recharge de cet appareil. Débranchez l\'adaptateur d\'alimentation, et faites attention, car le câble pourrait être chaud."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Afficher les étapes d\'entretien"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Maintenez le doigt sur l\'écran, puis glissez-le pour réorganiser les commandes"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Toutes les commandes ont été supprimées"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Modifications non enregistrées"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Afficher autres applications"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Impossible de charger les commandes. Vérifiez l\'application <xliff:g id="APP">%s</xliff:g> pour vous assurer que les paramètres de l\'application n\'ont pas changé."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Les commandes compatibles ne sont pas accessibles"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Autre"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirmer la modification pour <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Balayez l\'écran pour en afficher davantage"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations…"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Fermer cette session multimédia"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Commandes multimédias"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Masquer"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifiez l\'appli"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Erreur, nouvelle tentative…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings_tv.xml b/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
index abf4c1916d29..23dd656c431f 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Incrustation d\'image"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Aucun programme de titre)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Fermer mode IDI"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Plein écran"</string>
<string name="mic_active" msgid="5766614241012047024">"Microphone actif"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s a accédé à votre microphone"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 71875fc6fee9..784186b013a9 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Appuyer à nouveau pour ouvrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Balayer vers le haut pour ouvrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Balayez l\'écran vers le haut pour réessayer"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Cet appareil appartient à votre organisation"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Balayer pour téléphoner"</string>
<string name="voice_hint" msgid="7476017460191291417">"Balayer l\'écran depuis l\'icône pour l\'assistance vocale"</string>
<string name="camera_hint" msgid="4519495795000658637">"Balayer pour prendre une photo"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Le profil peut être contrôlé."</string>
<string name="vpn_footer" msgid="3457155078010607471">"Il est possible que le réseau soit surveillé."</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Il est possible que le réseau soit surveillé."</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Cet appareil appartient à votre organisation, qui peut contrôler votre trafic réseau"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>, qui peut contrôler votre trafic réseau"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Cet appareil appartient à votre organisation et il est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> et il est connecté à <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Cet appareil appartient à votre organisation"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Cet appareil appartient à votre organisation et il est connecté à des VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> et il est connecté à des VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Votre entreprise peut contrôler votre trafic réseau dans votre profil professionnel"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> peut contrôler votre trafic réseau dans votre profil professionnel"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Il est possible que le réseau soit surveillé"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Cet appareil est connecté à des VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Votre profil professionnel est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Votre profil personnel est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Cet appareil est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gestion des appareils"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Contrôle du profil"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Contrôle du réseau"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Désactiver le VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Déconnecter le VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Afficher les règles"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nVotre administrateur informatique peut contrôler et gérer les paramètres, l\'accès aux données d\'entreprise, les applications, les données associées à l\'appareil et les informations sur sa localisation.\n\nPour plus d\'informations, contactez votre administrateur informatique."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Cet appareil appartient à votre organisation.\n\nVotre administrateur informatique peut contrôler et gérer les paramètres, l\'accès aux données d\'entreprise, les applications, les données associées à l\'appareil et les informations sur sa localisation.\n\nPour plus d\'informations, contactez votre administrateur informatique."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Votre entreprise a installé une autorité de certification sur cet appareil. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Votre entreprise a installé une autorité de certification dans votre profil professionnel. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Une autorité de certification est installée sur cet appareil. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silencieux"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Par défaut"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bulle"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Aucun son ni vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Aucun son ni vibration, s\'affiche plus bas dans la section des conversations"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Peut sonner ou vibrer en fonction des paramètres du téléphone"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Peut sonner ou vibrer en fonction des paramètres du téléphone. Les conversations provenant de <xliff:g id="APP_NAME">%1$s</xliff:g> s\'affichent sous forme de bulles par défaut."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Attire votre attention à l\'aide d\'un raccourci flottant vers ce contenu."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laisser le système déterminer si cette notification doit être accompagnée d\'un son ou d\'une vibration"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"S\'affiche en haut de la section des conversations, apparaît sous forme de bulle flottante, affiche la photo de profil sur l\'écran de verrouillage"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Paramètres"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritaire"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Cette application se superpose aux autres applications sur l\'écran, et utilise le micro et la caméra."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Paramètres"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Le son de cette notification a été coupé par le système."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Cette notification a été mise en avant par le système."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Cette notification a été rétrogradée par le système."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Est-ce que c\'était correct ?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Merci de nous avoir envoyé vos commentaires."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Les commandes de notification sont disponibles pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Les commandes de notification sont indisponibles pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Autoriser les notifications pour cette chaîne"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Modifier l\'ordre des paramètres."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> sur <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Écran de verrouillage"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Développer"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Réduire"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Fermer"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Paramètres"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Faire glisser vers le bas pour ignorer"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> est en mode Picture-in-picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Si vous ne voulez pas que l\'application <xliff:g id="NAME">%s</xliff:g> utilise cette fonctionnalité, appuyez ici pour ouvrir les paramètres et la désactiver."</string>
- <string name="pip_play" msgid="333995977693142810">"Lecture"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Suspendre"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Passer au contenu suivant"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Passer au contenu précédent"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Tél. éteint car il surchauffait"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"À présent, votre téléphone fonctionne normalement"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"À présent, votre téléphone fonctionne normalement.\nAppuyer pour en savoir plus"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Votre téléphone s\'est éteint, car il surchauffait. Il s\'est refroidi et fonctionne normalement.\n\nIl peut surchauffer si vous :\n • exécutez applis utilisant beaucoup de ressources (jeux, vidéo, navigation, etc.) ;\n • téléchargez ou importez gros fichiers ;\n • utilisez téléphone à des températures élevées."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Afficher les étapes d\'entretien"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Le téléphone chauffe"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Fonctionnalités limitées pendant le refroidissement du téléphone"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Fonctionnalités limitées pendant le refroidissement du téléphone.\nAppuyer pour en savoir plus"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Votre téléphone va essayer de se refroidir automatiquement. Vous pouvez toujours l\'utiliser, mais il risque d\'être plus lent.\n\nUne fois refroidi, il fonctionnera normalement."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Afficher les étapes d\'entretien"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Débrancher le chargeur"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Un problème est survenu lors de la recharge de cet appareil. Débranchez l\'adaptateur secteur en faisant attention, car le câble risque d\'être chaud."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Afficher les étapes d\'entretien"</string>
@@ -1049,9 +1013,9 @@
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Mode Veille imminent"</string>
<string name="priority_onboarding_title" msgid="2893070698479227616">"Conversation définie comme prioritaire"</string>
<string name="priority_onboarding_behavior" msgid="5342816047020432929">"Les conversations prioritaires :"</string>
- <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"S\'affichent en haut de la liste des conversations"</string>
- <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Affichent la photo de profil sur l\'écran de verrouillage"</string>
- <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Sous forme d\'info-bulle au-dessus des applications"</string>
+ <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Apparaîtront en haut de la liste des conversations"</string>
+ <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Afficheront la photo de profil sur l\'écran de verrouillage"</string>
+ <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Apparaîtront sous forme de bulle au-dessus des applications"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"Interrompre Ne pas déranger"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"OK"</string>
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Paramètres"</string>
@@ -1063,7 +1027,7 @@
<string name="quick_controls_setup_subtitle" msgid="1681506617879773824">"Appuyez de manière prolongée sur le bouton Marche/Arrêt pour accéder aux commandes"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'appli pour laquelle ajouter des commandes"</string>
<plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> controls added.</item>
+ <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> commande ajoutée.</item>
<item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> commandes ajoutées.</item>
</plurals>
<string name="controls_removed" msgid="3731789252222856959">"Supprimé"</string>
@@ -1074,11 +1038,12 @@
<string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"supprimer des favoris"</string>
<string name="accessibility_control_move" msgid="8980344493796647792">"Déplacer l\'élément à la position <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="controls_favorite_default_title" msgid="967742178688938137">"Commandes"</string>
- <string name="controls_favorite_subtitle" msgid="6604402232298443956">"Sélectionnez les commandes auxquelles vous souhaitez accéder depuis le menu de démarrage"</string>
+ <string name="controls_favorite_subtitle" msgid="6604402232298443956">"Sélectionnez les commandes auxquelles vous souhaitez accéder depuis le menu Marche/Arrêt"</string>
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Appuyez et faites glisser pour réorganiser les commandes"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Toutes les commandes ont été supprimées"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Les modifications n\'ont pas été enregistrées"</string>
- <string name="controls_favorite_load_error" msgid="5126216176144877419">"Impossible de charger les commandes. Vérifiez l\'application <xliff:g id="APP">%s</xliff:g> pour vous assurer que les paramètres de l\'application n\'ont pas changé."</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Afficher d\'autres applications"</string>
+ <string name="controls_favorite_load_error" msgid="5126216176144877419">"Impossible de charger les commandes. Vérifiez l\'application <xliff:g id="APP">%s</xliff:g> pour vous assurer que les paramètres n\'ont pas changé."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Commandes compatibles indisponibles"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Autre"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"Ajouter aux commandes de contrôle des appareils"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirmer la modification pour <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Balayer l\'écran pour voir plus d\'annonces"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Fermer cette session multimédia"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Masquer"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifier l\'appli"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Erreur. Nouvelle tentative…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
diff --git a/packages/SystemUI/res/values-fr/strings_tv.xml b/packages/SystemUI/res/values-fr/strings_tv.xml
index 1fc43a1cff4a..4ab6a24b1aca 100644
--- a/packages/SystemUI/res/values-fr/strings_tv.xml
+++ b/packages/SystemUI/res/values-fr/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-Picture (PIP)"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programme sans titre)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Fermer mode PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Plein écran"</string>
<string name="mic_active" msgid="5766614241012047024">"Micro actif"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s a accédé à votre micro"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 7237b5b886c0..c732c7bbff1f 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Toca de novo para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Pasa o dedo cara arriba para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Pasa o dedo cara arriba para tentalo de novo"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence á túa organización"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Pasa o dedo desde a icona para acceder ao teléfono"</string>
<string name="voice_hint" msgid="7476017460191291417">"Pasa o dedo desde a icona para acceder ao asistente de voz"</string>
<string name="camera_hint" msgid="4519495795000658637">"Pasa o dedo desde a icona para acceder á cámara"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"O perfil pódese supervisar"</string>
<string name="vpn_footer" msgid="3457155078010607471">"É posible que se supervise a rede"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"É posible que se supervise a rede"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"A túa organización é propietaria deste dispositivo e pode controlar o tráfico de rede"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> é a organización propietaria deste dispositivo e pode controlar o tráfico de rede"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Este dispositivo pertence á túa organización e está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Este dispositivo pertence a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está conectado a <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Este dispositivo pertence á túa organización"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Este dispositivo pertence a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Este dispositivo pertence á túa organización e está conectado a varias VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Este dispositivo pertence a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está conectado a varias VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"A túa organización pode controlar o tráfico de rede do teu perfil de traballo"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> pode controlar o tráfico de rede do teu perfil de traballo"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"É posible que se controle a rede"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Este dispositivo está conectado a varias VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"O teu perfil de traballo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"O teu perfil persoal está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Este dispositivo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Xestión de dispositivos"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Supervisión do perfil"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Supervisión de rede"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Desactivar VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Desconectar VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Ver políticas"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Este dispositivo pertence a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nO teu administrador de TI pode supervisar e xestionar a configuración, o acceso corporativo, as aplicacións, os datos asociados co teu dispositivo e a información de localización deste último.\n\nPara obter máis información, contacta co teu administrador de TI."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Este dispositivo pertence á túa organización.\n\nO teu administrador de TI pode supervisar e xestionar a configuración, o acceso corporativo, as aplicacións, os datos asociados co teu dispositivo e a información de localización deste último.\n\nPara obter máis información, contacta co teu administrador de TI."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"A túa organización instalou unha autoridade de certificación neste dispositivo. É posible que se controle ou se modifique o teu tráfico de rede segura."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"A túa organización instalou unha autoridade de certificación no teu perfil de traballo. É posible que se controle ou se modifique o teu tráfico de rede segura."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Este dispositivo ten unha autoridade de certificación instalada. É posible que se controle ou se modifique o teu tráfico de rede segura."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silenciosas"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Configuración predeterminada"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Burbulla"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sen son nin vibración"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Non soa nin vibra, e aparece máis abaixo na sección de conversas"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Podería soar ou vibrar en función da configuración do teléfono"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Podería soar ou vibrar en función da configuración do teléfono. Conversas desde a burbulla da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantén a túa atención cun atallo flotante a este contido."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai que o sistema determine se a notificación debe emitir un son ou unha vibración"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Móstrase na parte superior da sección de conversas en forma de burbulla flotante e aparece a imaxe do perfil na pantalla de bloqueo"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configuración"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Esta aplicación móstrase sobre outras aplicacións da pantalla e está utilizando o micrófono e a cámara."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Configuración"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Aceptar"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"O sistema silenciou esta notificación."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"O sistema subiu o nivel desta notificación."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"O sistema diminuíu o nivel desta notificación."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"A información era correcta?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Grazas polo teu comentario"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Aceptar"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Abríronse os controis de notificacións da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Pecháronse os controis de notificacións da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Permitir notificacións desde esta canle"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editar a orde das opcións de configuración."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Páxina <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Despregar"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizar"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Pechar"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Configuración"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Arrastra cara abaixo para ignorar"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menú"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> está na pantalla superposta"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Se non queres que <xliff:g id="NAME">%s</xliff:g> utilice esta función, toca a configuración para abrir as opcións e desactivar a función."</string>
- <string name="pip_play" msgid="333995977693142810">"Reproducir"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pausar"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Ir ao seguinte"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Ir ao anterior"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"O teléfono apagouse pola calor"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"O teu teléfono funciona agora con normalidade"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"O teléfono funciona con normalidade.\nToca para obter máis información"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"O teléfono estaba moi quente, apagouse para que arrefríe e agora funciona con normalidade.\n\nÉ posible que estea moi quente se:\n • Usas aplicacións que requiren moitos recursos (como aplicacións de navegación, vídeos e xogos)\n • Descargas/cargas ficheiros grandes\n • Usas o teléfono a alta temperatura"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver pasos de mantemento"</string>
<string name="high_temp_title" msgid="2218333576838496100">"O teléfono está quentando"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"O uso dalgunhas funcións é limitado mentres o teléfono arrefría"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"O uso dalgunhas funcións é limitado mentres o teléfono arrefría.\nToca para obter máis información"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"O teléfono tentará arrefriar automaticamente. Podes utilizalo, pero é probable que funcione máis lento.\n\nUnha vez que arrefríe, funcionará con normalidade."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantemento"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Desconecta o cargador"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Produciuse un problema ao cargar este dispositivo. Desconecta o adaptador de corrente e ten coidado porque o cable pode estar quente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver pasos de mantemento"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Para reorganizar os controis, mantenos premidos e arrástraos"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Quitáronse todos os controis"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Non se gardaron os cambios"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Ver outras aplicacións"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Non se puideron cargar os controis. Comproba a aplicación <xliff:g id="APP">%s</xliff:g> para asegurarte de que non se modificase a súa configuración."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Non hai controis compatibles que estean dispoñibles"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Outra"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirma o cambio para <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Pasar o dedo para ver máis"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendacións"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Pechar esta sesión multimedia"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Contido multimedia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta a sesión actual."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Comproba a app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Erro. Tentando de novo…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Non se atopou"</string>
diff --git a/packages/SystemUI/res/values-gl/strings_tv.xml b/packages/SystemUI/res/values-gl/strings_tv.xml
index 9455f956e890..123a86ef8322 100644
--- a/packages/SystemUI/res/values-gl/strings_tv.xml
+++ b/packages/SystemUI/res/values-gl/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Pantalla superposta"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programa sen título)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Pechar PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Pantalla completa"</string>
<string name="mic_active" msgid="5766614241012047024">"Micrófono activo"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s accedeu ao teu micrófono"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 7242f9e2bdf1..5d3d3af8f917 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -21,7 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4811759950673118541">"સિસ્ટમ UI"</string>
<string name="status_bar_clear_all_button" msgid="2491321682873657397">"સાફ કરો"</string>
- <string name="status_bar_no_notifications_title" msgid="7812479124981107507">"કોઈ સૂચનાઓ નથી"</string>
+ <string name="status_bar_no_notifications_title" msgid="7812479124981107507">"કોઈ નોટિફિકેશન નથી"</string>
<string name="status_bar_ongoing_events_title" msgid="3986169317496615446">"ચાલુ"</string>
<string name="status_bar_latest_events_title" msgid="202755896454005436">"નોટિફિકેશનો"</string>
<string name="battery_low_title" msgid="6891106956328275225">"બૅટરી ટૂંક સમયમાં સમાપ્ત થશે"</string>
@@ -368,7 +368,7 @@
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"સ્થાન બંધ"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"મીડિયા ઉપકરણ"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
- <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ફક્ત કટોકટીના કૉલ્સ"</string>
+ <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"ફક્ત ઇમર્જન્સી કૉલ"</string>
<string name="quick_settings_settings_label" msgid="2214639529565474534">"સેટિંગ"</string>
<string name="quick_settings_time_label" msgid="3352680970557509303">"સમય"</string>
<string name="quick_settings_user_label" msgid="1253515509432672496">"હું"</string>
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ખોલવા માટે ફરીથી ટૅપ કરો"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ખોલવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ફરી પ્રયાસ કરવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>ની માલિકીનું છે"</string>
<string name="phone_hint" msgid="6682125338461375925">"ફોન માટે આયકનમાંથી સ્વાઇપ કરો"</string>
<string name="voice_hint" msgid="7476017460191291417">"વૉઇસ સહાય માટે આયકનમાંથી સ્વાઇપ કરો"</string>
<string name="camera_hint" msgid="4519495795000658637">"કૅમેરા માટે આયકનમાંથી સ્વાઇપ કરો"</string>
@@ -519,37 +517,25 @@
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"બધા સાઇલન્ટ નોટિફિકેશન સાફ કરો"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ખલેલ પાડશો નહીં દ્વારા થોભાવેલ નોટિફિકેશન"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"હવે પ્રારંભ કરો"</string>
- <string name="empty_shade_text" msgid="8935967157319717412">"કોઈ સૂચનાઓ નથી"</string>
+ <string name="empty_shade_text" msgid="8935967157319717412">"કોઈ નોટિફિકેશન નથી"</string>
<string name="profile_owned_footer" msgid="2756770645766113964">"પ્રોફાઇલ મૉનિટર કરી શકાય છે"</string>
<string name="vpn_footer" msgid="3457155078010607471">"નેટવર્ક મૉનિટર કરી શકાય છે"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"નેટવર્ક મૉનિટર કરવામાં આવી શકે છે"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"તમારી સંસ્થા આ ડિવાઇસની માલિકી ધરાવે છે અને નેટવર્ક ટ્રાફિકનું નિરીક્ષણ કરી શકે છે"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ની માલિકીનું છે અને નેટવર્ક ટ્રાફિકનું નિરીક્ષણ કરી શકે છે"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે અને <xliff:g id="VPN_APP">%1$s</xliff:g>થી કનેક્ટ કરેલું છે"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ની માલિકીનું છે અને <xliff:g id="VPN_APP">%2$s</xliff:g>થી કનેક્ટ કરેલું છે"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ની માલિકીનું છે"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે અને VPNsથી કનેક્ટ કરેલું છે"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ની માલિકીનું છે અને VPNsથી કનેક્ટ કરેલું છે"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"તમારી સંસ્થા તમારી કાર્ય પ્રોફાઇલમાં નેટવર્ક ટ્રાફિકનું નિયમન કરી શકે છે"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> તમારી કાર્ય પ્રોફાઇલમાં નેટવર્ક ટ્રાફિકનું નિયમન કરી શકે છે"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"નેટવર્કનું નિયમન કરવામાં આવી શકે છે"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"આ ડિવાઇસ VPNsથી કનેક્ટ કરેલું છે"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"તમારી ઑફિસની પ્રોફાઇલ <xliff:g id="VPN_APP">%1$s</xliff:g>થી કનેક્ટ કરેલી છે"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"તમારી વ્યક્તિગત પ્રોફાઇલ <xliff:g id="VPN_APP">%1$s</xliff:g>થી કનેક્ટ કરેલી છે"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"આ ડિવાઇસ <xliff:g id="VPN_APP">%1$s</xliff:g>થી કનેક્ટ કરેલું છે"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ઉપકરણનું સંચાલન"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"પ્રોફાઇલ નિરીક્ષણ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"નેટવર્ક મૉનિટરિંગ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN અક્ષમ કરો"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN ડિસ્કનેક્ટ કરો"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"નીતિઓ જુઓ"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ની માલિકીનું છે.\n\nતમારા IT વ્યવસ્થાપક સેટિંગ, કૉર્પોરેટ ઍક્સેસ, ઍપ, તમારા ડિવાઇસ સાથે સંકળાયેલો ડેટા અને તમારા ડિવાઇસની સ્થાન માહિતીનું નિરીક્ષણ તેમજ તેને મેનેજ કરી શકે છે.\n\nવધુ માહિતી માટે, તમારા IT વ્યવસ્થાપકનો સંપર્ક કરો."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે.\n\nતમારા IT વ્યવસ્થાપક સેટિંગ, કૉર્પોરેટ ઍક્સેસ, ઍપ, તમારા ડિવાઇસ સાથે સંકળાયેલો ડેટા અને તમારા ડિવાઇસની સ્થાન માહિતીનું નિરીક્ષણ તેમજ તેને મેનેજ કરી શકે છે.\n\nવધુ માહિતી માટે, તમારા IT વ્યવસ્થાપકનો સંપર્ક કરો."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"તમારી સંસ્થાએ આ ઉપકરણ પર પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કર્યું છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"તમારી સંસ્થાએ તમારી કાર્ય પ્રોફાઇલમાં પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કર્યું છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"આ ઉપકરણ પર પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કરેલ છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"સાઇલન્ટ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ડિફૉલ્ટ"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"બબલ"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ઑટોમૅટિક રીતે"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"કોઈપણ સાઉન્ડ અથવા વાઇબ્રેશન નથી"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"કોઈપણ સાઉન્ડ અથવા વાઇબ્રેશન નથી અને વાતચીત વિભાગમાં તે વધુ નીચેની દિશાએ દેખાય છે"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ફોન સેટિંગના આધારે રિંગ અથવા વાઇબ્રેટ થઈ શકે છે"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ફોન સેટિંગના આધારે રિંગ અથવા વાઇબ્રેટ થઈ શકે છે. ડિફૉલ્ટ તરીકે <xliff:g id="APP_NAME">%1$s</xliff:g> બબલની વાતચીત."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ફ્લોટિંગ શૉર્ટકટથી આ કન્ટેન્ટ પર તમારું ધ્યાન દોરી રાખે છે."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"આ નોટિફિકેશન સાઉન્ડ અથવા વાઇબ્રેટ કરી શકશે કે નહીં તે સિસ્ટમને નક્કી કરવા દો"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"એને વાતચીત વિભાગની ટોચ પર બતાવે છે, તરતા બબલ તરીકે દેખાય છે, લૉક સ્ક્રીન પર પ્રોફાઇલ ફોટા તરીકે બતાવે છે"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"સેટિંગ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"પ્રાધાન્યતા"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"આ ઍપ તમારી સ્ક્રીન પરની અન્ય ઍપની ઉપર પ્રદર્શિત થઈ રહી છે અને માઇક્રોફોન અને કૅમેરાનો ઉપયોગ કરી રહી છે."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"સેટિંગ"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ઓકે"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"આ નોટિફિકેશનને સિસ્ટમ દ્વારા સાઇલન્ટ કરવામાં આવ્યું હતું."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"આ નોટિફિકેશનને સિસ્ટમ દ્વારા બઢતી આપવામાં આવી હતી."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"આ નોટિફિકેશનને સિસ્ટમ દ્વારા ડિમોટ કરવામાં આવ્યું હતું."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"શું આ યોગ્ય હતું?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"તમારા પ્રતિસાદ બદલ આભાર!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ઓકે"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સૂચના નિયંત્રણો ચાલુ છે"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સૂચના નિયંત્રણો બંધ છે"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"આ ચૅનલની સૂચનાઓને મંજૂરી આપો"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"સેટિંગ્સનો ક્રમ સંપાદિત કરો."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> માંથી <xliff:g id="ID_1">%1$d</xliff:g> પૃષ્ઠ"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"લૉક સ્ક્રીન"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"વિસ્તૃત કરો"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"નાનું કરો"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"બંધ કરો"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"સેટિંગ"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"છોડી દેવા માટે નીચે ખેંચો"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"મેનૂ"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> ચિત્રમાં-ચિત્રની અંદર છે"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"જો તમે નથી ઇચ્છતા કે <xliff:g id="NAME">%s</xliff:g> આ સુવિધાનો ઉપયોગ કરે, તો સેટિંગ ખોલવા માટે ટૅપ કરો અને તેને બંધ કરો."</string>
- <string name="pip_play" msgid="333995977693142810">"ચલાવો"</string>
- <string name="pip_pause" msgid="1139598607050555845">"થોભાવો"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"આગલા પર જાઓ"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"પહેલાંના પર જાઓ"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"ફોન વધુ પડતી ગરમીને લીધે બંધ થઇ ગયો છે"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"તમારો ફોન હવે સામાન્યપણે કાર્ય કરી રહ્યો છે"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"તમારો ફોન હવે સામાન્યપણે કાર્ય કરી રહ્યો છે.\nવધુ માહિતી માટે ટૅપ કરો"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"તમારો ફોન અત્યંત ગરમ હતો, તેથી તે ઠંડો થવા આપમેળે બંધ થઇ ગયો છે. તમારો ફોન હવે સામાન્યપણે કાર્ય કરી રહ્યો છે.\n\nતમારો ફોન અત્યંત ગરમ થઇ શકે છે, જો તમે:\n • એવી ઍપ્લિકેશન વાપરતા હો જે સંસાધન સઘન રીતે વાપરતી હોય (જેમ કે ગેમિંગ, વીડિઓ, અથવા નેવિગેટ કરતી ઍપ્લિકેશનો)\n • મોટી ફાઇલો અપલોડ અથવા ડાઉનલોડ કરતા હો\n • તમારા ફોનનો ઉપયોગ ઉચ્ચ તાપમાનમાં કરતા હો"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"સારસંભાળના પગલાં જુઓ"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ફોન ગરમ થઈ રહ્યો છે"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ફોન ઠંડો થાય ત્યાં સુધી કેટલીક સુવિધાઓ મર્યાદિત હોય છે"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ફોન ઠંડો થાય ત્યાં સુધી અમુક સુવિધાઓ મર્યાદિત હોય છે.\nવધુ માહિતી માટે ટૅપ કરો"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"તમારો ફોન આપમેળે ઠંડો થવાનો પ્રયાસ કરશે. તમે હજી પણ તમારા ફોનનો ઉપયોગ કરી શકો છો, પરંતુ તે કદાચ થોડો ધીમો ચાલે.\n\nતમારો ફોન ઠંડો થઈ જવા પર, તે સામાન્ય રીતે ચાલશે."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"સારસંભાળના પગલાં જુઓ"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ચાર્જરને અનપ્લગ કરો"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"આ ડિવાઇસને ચાર્જ કરવામાં કોઈ સમસ્યા છે. પાવર અડૅપ્ટર અનપ્લગ કરો અને કાળજી લેજો કદાચ કેબલ થોડો ગરમ થયો હોઈ શકે છે."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"સારસંભાળના પગલાં જુઓ"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"નિયંત્રણોને ફરીથી ગોઠવવા માટે તેમને હોલ્ડ કરીને ખેંચો"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"બધા નિયંત્રણો કાઢી નાખ્યા"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ફેરફારો સાચવ્યા નથી"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"અન્ય બધી ઍપ જુઓ"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"નિયંત્રણો લોડ કરી શકાયા નથી. ઍપના સેટિંગ બદલાયા નથી તેની ખાતરી કરવા માટે <xliff:g id="APP">%s</xliff:g> ઍપ ચેક કરો."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"સુસંગત નિયંત્રણો ઉપલબ્ધ નથી"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"અન્ય"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> માટે ફેરફાર કન્ફર્મ કરો"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"વધુ જોવા માટે સ્વાઇપ કરો"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"સુઝાવ લોડ કરી રહ્યાં છીએ"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"આ મીડિયા સત્રને બંધ કરો"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"મીડિયા"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"હાલનું સત્ર છુપાવો."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"છુપાવો"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"નિષ્ક્રિય, ઍપને ચેક કરો"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"ભૂલ, ફરી પ્રયાસ કરી રહ્યા છીએ…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"મળ્યું નથી"</string>
diff --git a/packages/SystemUI/res/values-gu/strings_tv.xml b/packages/SystemUI/res/values-gu/strings_tv.xml
index 3ea64873a856..72a6803aa202 100644
--- a/packages/SystemUI/res/values-gu/strings_tv.xml
+++ b/packages/SystemUI/res/values-gu/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ચિત્રમાં-ચિત્ર"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(કોઈ ટાઇટલ પ્રોગ્રામ નથી)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP બંધ કરો"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"પૂર્ણ સ્ક્રીન"</string>
<string name="mic_active" msgid="5766614241012047024">"માઇક્રોફોન સક્રિય છે"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$sએ તમારો માઇક્રોફોન ઍક્સેસ કર્યો હતો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index effd512f9cfd..f6bc4cdab087 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -370,7 +370,7 @@
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"जगह की जानकारी बंद है"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"मीडिया डिवाइस"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
- <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"केवल आपातकालीन कॉल"</string>
+ <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"सिर्फ़ आपातकालीन कॉल"</string>
<string name="quick_settings_settings_label" msgid="2214639529565474534">"सेटिंग"</string>
<string name="quick_settings_time_label" msgid="3352680970557509303">"समय"</string>
<string name="quick_settings_user_label" msgid="1253515509432672496">"मुझे"</string>
@@ -456,10 +456,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"खोलने के लिए फिर से टैप करें"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"खोलने के लिए ऊपर स्वाइप करें"</string>
<string name="keyguard_retry" msgid="886802522584053523">"फिर से कोशिश करने के लिए ऊपर की ओर स्वाइप करें"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"इस डिवाइस का मालिकाना हक <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> के पास है"</string>
<string name="phone_hint" msgid="6682125338461375925">"फ़ोन के लिए आइकॉन से स्वाइप करें"</string>
<string name="voice_hint" msgid="7476017460191291417">"\'आवाज़ से डिवाइस का इस्तेमाल\' आइकॉन से स्वाइप करें"</string>
<string name="camera_hint" msgid="4519495795000658637">"कैमरे के लिए आइकॉन से स्वाइप करें"</string>
@@ -521,37 +519,25 @@
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"बिना आवाज़ की सभी सूचनाएं हटाएं"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'परेशान न करें\' सुविधा के ज़रिए कुछ समय के लिए सूचनाएं दिखाना रोक दिया गया है"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"अभी शुरू करें"</string>
- <string name="empty_shade_text" msgid="8935967157319717412">"कोई सूचना नहीं मिली"</string>
+ <string name="empty_shade_text" msgid="8935967157319717412">"कोई सूचना नहीं है"</string>
<string name="profile_owned_footer" msgid="2756770645766113964">"प्रोफ़ाइल को मॉनीटर किया जा सकता है"</string>
<string name="vpn_footer" msgid="3457155078010607471">"नेटवर्क को मॉनीटर किया जा सकता है"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"नेटवर्क को मॉनिटर किया जा सकता है"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है. आपका संगठन, नेटवर्क के ट्रैफ़िक की निगरानी कर सकता है"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"इस डिवाइस का मालिकाना हक <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> के पास है. आपका संगठन, नेटवर्क के ट्रैफ़िक की निगरानी कर सकता है"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है. इस डिवाइस को <xliff:g id="VPN_APP">%1$s</xliff:g> से कनेक्ट किया गया है"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"इस डिवाइस का मालिकाना हक <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> के पास है. इस डिवाइस को <xliff:g id="VPN_APP">%2$s</xliff:g> से कनेक्ट किया गया है"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"इस डिवाइस का मालिकाना हक <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> के पास है"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है. इस डिवाइस को वीपीएन से कनेक्ट किया गया है"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"इस डिवाइस का मालिकाना हक <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> के पास है. इस डिवाइस को वीपीएन से कनेक्ट किया गया है"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"आपका संगठन आपकी वर्क प्रोफ़ाइल में नेटवर्क ट्रैफ़िक की निगरानी कर सकता है"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> आपकी वर्क प्रोफ़ाइल में नेटवर्क ट्रैफ़िक की निगरानी कर सकता है"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"नेटवर्क की निगरानी की जा सकती है"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"इस डिवाइस को वीपीएन से कनेक्ट किया गया है"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"आपकी वर्क प्रोफ़ाइल <xliff:g id="VPN_APP">%1$s</xliff:g> से कनेक्ट की गई है"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"आपकी निजी प्रोफ़ाइल <xliff:g id="VPN_APP">%1$s</xliff:g> से कनेक्ट की गई है"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"इस डिवाइस को <xliff:g id="VPN_APP">%1$s</xliff:g> से कनेक्ट किया गया है"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"डिवाइस प्रबंधन"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"प्रोफ़ाइल को मॉनीटर करना"</string>
<string name="monitoring_title" msgid="4063890083735924568">"नेटवर्क को मॉनीटर करना"</string>
@@ -561,10 +547,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN अक्षम करें"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN डिस्‍कनेक्‍ट करें"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"नीतियां देखें"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"इस डिवाइस का मालिकाना हक <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> के पास है.\n\nआपके संगठन का आईटी एडमिन कुछ चीज़ों की निगरानी और उन्हें प्रबंधित कर सकता है, जैसे कि सेटिंग, कॉर्पोरेट ऐक्सेस, ऐप्लिकेशन, आपके डिवाइस से जुड़ा डेटा, और आपके डिवाइस की जगह की जानकारी.\n\nज़्यादा जानकारी के लिए, अपने आईटी एडमिन से संपर्क करें."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है.\n\nआपके संगठन का आईटी एडमिन कुछ चीज़ों की निगरानी और उन्हें प्रबंधित कर सकता है, जैसे कि सेटिंग, कॉर्पोरेट ऐक्सेस, ऐप्लिकेशन, आपके डिवाइस से जुड़ा डेटा, और आपके डिवाइस की जगह की जानकारी.\n\nज़्यादा जानकारी के लिए, अपने आईटी एडमिन से संपर्क करें."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"आपके संगठन ने इस डिवाइस पर एक प्रमाणपत्र अनुमति इंस्टॉल की है. आपके सुरक्षित नेटवर्क पर ट्रेफ़िक की निगरानी या उसमें बदलाव किया जा सकता है."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"आपके संगठन ने आपकी वर्क प्रोफ़ाइल में एक प्रमाणपत्र अनुमति इंस्टॉल की है. आपके सुरक्षित नेटवर्क ट्रैफ़िक की निगरानी या उसमें बदलाव किया जा सकता है."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"इस डिवाइस पर एक प्रमाणपत्र अनुमति इंस्टॉल की है. आपके सुरक्षित नेटवर्क ट्रैफ़िक की निगरानी या उसमें बदलाव किया जा सकता है."</string>
@@ -729,15 +713,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"आवाज़ के बिना सूचनाएं दिखाएं"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"डिफ़ॉल्ट"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"बबल"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"अपने-आप"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"किसी तरह की आवाज़ या वाइब्रेशन न हो"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"इससे किसी तरह की आवाज़ या वाइब्रेशन नहीं होता और \'बातचीत\', सेक्शन में सबसे नीचे दिखती है"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है. <xliff:g id="APP_NAME">%1$s</xliff:g> में होने वाली बातचीत, डिफ़ॉल्ट रूप से बबल के तौर पर दिखती है."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"फ़्लोट करने वाले शॉर्टकट की मदद से इस सामग्री पर आपका ध्यान बना रहता है."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"सिस्टम को यह तय करने की अनुमति दें कि इस सूचना के मिलने पर आवाज़ हो या वाइब्रेशन हो"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"इससे बातचीत की सुविधा, सेक्शन में सबसे ऊपर और फ़्लोटिंग बबल के तौर पर दिखती है. साथ ही, लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो दिखती है"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"सेटिंग"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string>
@@ -758,18 +740,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"यह ऐप्लिकेशन आपकी स्क्रीन पर इस्तेमाल हो रहे दूसरे ऐप्लिकेशन के ऊपर दिखाया जा रहा है. इसके साथ ही यह माइक्रोफ़ोन और कैमरे का भी इस्तेमाल कर रहा है."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"सेटिंग"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ठीक है"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"सिस्टम ने उपयोगकर्ता के इस्तेमाल के आधार पर, इस सूचना के मिलने पर होने वाली आवाज़ बंद कर दी है."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"सिस्टम ने उपयोगकर्ता के इस्तेमाल के आधार पर यह तय किया है कि कोई सूचना, खोज के क्रम में एकदम नीचे दिखेगी या फिर बिना आवाज़ किए मिलेगी."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"सिस्टम ने उपयोगकर्ता के इस्तेमाल के आधार पर यह तय किया है कि कोई सूचना, खोज के क्रम में एकदम नीचे दिखेगी या फिर बिना आवाज़ किए मिलेगी."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"आपको यह सुविधा कैसी लगी?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"सुझाव या शिकायत के लिए धन्यवाद!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ठीक है"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण चालू हैं"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण बंद हैं"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"इस चैनल से सूचना की पाने की मंज़ूरी दें"</string>
@@ -942,26 +918,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"सेटिंग के क्रम को बदलें"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"पेज <xliff:g id="ID_2">%2$d</xliff:g> में से <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"लॉक स्‍क्रीन"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"विस्तार करें"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"छोटा करें"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"बंद करें"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"सेटिंग"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"खारिज करने के लिए नीचे खींचें और छोड़ें"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"मेन्यू"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> \"पिक्चर में पिक्चर\" के अंदर है"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"अगर आप नहीं चाहते कि <xliff:g id="NAME">%s</xliff:g> इस सुविधा का उपयोग करे, तो सेटिंग खोलने के लिए टैप करें और उसे बंद करें ."</string>
- <string name="pip_play" msgid="333995977693142810">"चलाएं"</string>
- <string name="pip_pause" msgid="1139598607050555845">"रोकें"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"अगले पर जाएं"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"पिछले पर जाएं"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"गर्म होने की वजह से फ़ोन बंद हुआ"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"आपका फ़ोन अब सामान्य रूप से चल रहा है"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"आपका फ़ोन सामान्य रूप से काम कर रहा है.\nज़्यादा जानकारी के लिए टैप करें"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"फ़ोन बहुत गर्म हो गया था, इसलिए ठंडा होने के लिए बंद हो गया. फ़ोन अब अच्छे से चल रहा है.\n\nफ़ोन तब बहुत गर्म हो सकता है जब आप:\n • ज़्यादा रिसॉर्स का इस्तेमाल करने वाले ऐप्लिकेशन चलाते हैं (जैसे गेमिंग, वीडियो या नेविगेशन ऐप्लिकेशन)\n • बड़ी फ़ाइलें डाउनलोड या अपलोड करते हैं\n • ज़्यादा तापमान में फ़ोन का इस्तेमाल करते हैं"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"डिवाइस के रखरखाव के तरीके देखें"</string>
<string name="high_temp_title" msgid="2218333576838496100">"फ़ोन गर्म हो रहा है"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"फ़ोन के ठंडा होने के दौरान कुछ सुविधाएं सीमित होती हैं"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"फ़ोन के ठंडा होने तक कुछ सुविधाएं काम नहीं करतीं.\nज़्यादा जानकारी के लिए टैप करें"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"आपका फ़ोन अपने आप ठंडा होने की कोशिश करेगा. आप अब भी अपने फ़ोन का उपयोग कर सकते हैं, लेकिन हो सकता है कि यह धीमी गति से चले.\n\nठंडा हो जाने पर आपका फ़ोन सामान्य रूप से चलेगा."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"डिवाइस के रखरखाव के तरीके देखें"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"चार्जर निकालें"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"इस डिवाइस को चार्ज करने में समस्या हुई. पावर अडैप्टर का प्लग निकालें. ऐसा करते समय सावधानी बरतें क्योंकि तार गर्म हो सकता है."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"प्रबंधन से जुड़े चरण देखें"</string>
@@ -1080,6 +1044,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"कंट्रोल का क्रम फिर से बदलने के लिए उन्हें दबाकर रखें और खींचें"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"सभी कंट्रोल हटा दिए गए"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"बदलाव सेव नहीं किए गए"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"दूसरे ऐप्लिकेशन देखें"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"कंट्रोल लोड नहीं किए जा सके. <xliff:g id="APP">%s</xliff:g> ऐप्लिकेशन देखें, ताकि यह पक्का किया जा सके कि ऐप्लिकेशन की सेटिंग में कोई बदलाव नहीं हुआ है."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"इस सेटिंग के साथ काम करने वाले कंट्रोल उपलब्ध नहीं हैं"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"अन्य"</string>
@@ -1097,8 +1062,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> में बदलाव के लिए पुष्टि करें"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"ज़्यादा देखने के लिए स्वाइप करें"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"सुझाव लोड हो रहे हैं"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"इस मीडिया सेशन को बंद करें"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"इस मीडिया सेशन को छिपाएं."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"छिपाएं"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"फिर से शुरू करें"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"काम नहीं कर रहा, ऐप जांचें"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"कोई गड़बड़ी हुई, फिर से कोशिश की जा रही है…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"कंट्रोल नहीं है"</string>
diff --git a/packages/SystemUI/res/values-hi/strings_tv.xml b/packages/SystemUI/res/values-hi/strings_tv.xml
index e5c6eb2e6e20..9282c3c4b861 100644
--- a/packages/SystemUI/res/values-hi/strings_tv.xml
+++ b/packages/SystemUI/res/values-hi/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"पिक्चर में पिक्चर"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(कोई शीर्षक कार्यक्रम नहीं)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP बंद करें"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"फ़ुल स्‍क्रीन"</string>
<string name="mic_active" msgid="5766614241012047024">"माइक्रोफ़ोन चालू है"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ने आपका माइक्रोफ़ोन ऐक्सेस किया था"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 7705dd598eb6..bfd5aa001c24 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -456,10 +456,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Dodirnite opet za otvaranje"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Prijeđite prstom prema gore da biste otvorili"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Prijeđite prstom prema gore za ponovni pokušaj"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada vašoj organizaciji"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Prijeđite prstom od ikone za telefon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Prijeđite prstom od ikone za glasovnu pomoć"</string>
<string name="camera_hint" msgid="4519495795000658637">"Prijeđite prstom od ikone za fotoaparat"</string>
@@ -526,33 +524,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil se možda nadzire"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Mreža se možda nadzire"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Mreža se možda nadzire"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Vaša je organizacija vlasnik ovog uređaja i može nadzirati mrežni promet"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Organizacija <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> vlasnik je ovog uređaja i može nadzirati mrežni promet"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Ovaj uređaj pripada vašoj organizaciji i povezan je s mrežom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i povezan je s mrežom <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Ovaj uređaj pripada vašoj organizaciji"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Ovaj uređaj pripada vašoj organizaciji i povezan je s VPN-ovima"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i povezan je s VPN-ovima"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Vaša organizacija može nadzirati mrežni promet na vašem radnom profilu"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Organizacija <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> može nadzirati mrežni promet na vašem radnom profilu"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Mreža se možda nadzire"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Ovaj je uređaj povezan s VPN-ovima"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Vaš poslovni profil povezan je s mrežom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Vaš osobni profil povezan je s mrežom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Ovaj uređaj povezan je s mrežom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Upravljanje uređajem"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Nadzor profila"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Nadzor mreže"</string>
@@ -562,10 +548,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Onemogući VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Prekini vezu s VPN-om"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Prikaži pravila"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nVaš IT administrator može nadzirati postavke, korporacijski pristup, aplikacije, podatke o uređaju i lokaciji uređaja te upravljati njima.\n\nZa više informacija obratite se IT administratoru."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Ovaj uređaj pripada vašoj organizaciji.\n\nVaš IT administrator može nadzirati postavke, korporacijski pristup, aplikacije, podatke o uređaju i lokaciji uređaja te upravljati njima.\n\nZa više informacija obratite se IT administratoru."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Vaša je organizacija instalirala izdavač certifikata na ovom uređaju. Vaš sigurni mrežni promet možda se nadzire ili modificira."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Vaša je organizacija instalirala izdavač certifikata na vašem radnom profilu. Vaš sigurni mrežni promet možda se nadzire ili modificira."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Na ovom je uređaju instaliran izdavač certifikata. Vaš sigurni mrežni promet možda se nadzire ili modificira."</string>
@@ -730,15 +714,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Bešumno"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Zadano"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Oblačić"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka ili vibracije"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Bez zvuka ili vibracije i prikazuje se pri dnu odjeljka razgovora"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Možda će zvoniti ili vibrirati, ovisno o postavkama telefona"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Možda će zvoniti ili vibrirati, ovisno o postavkama telefona. Razgovori iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> prikazuju se u oblačiću prema zadanim postavkama."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Održava vam pozornost pomoću plutajućeg prečaca ovom sadržaju."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sustav odredi treba li obavijest najaviti zvukom ili vibracijom"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Prikazuje se pri vrhu odjeljka razgovora kao pomični oblačić i prikazuje profilnu sliku na zaključanom zaslonu"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Postavke"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
@@ -759,18 +741,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ova se aplikacija prikazuje preko drugih aplikacija na zaslonu i upotrebljava mikrofon i kameru."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Postavke"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"U redu"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Ovu obavijest utišao je sustav."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Ovu obavijest promovirao je sustav."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Ovu obavijest degradirao je sustav."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Je li to bilo točno?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Zahvaljujemo na povratnim informacijama!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"U redu"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Otvorene su kontrole obavijesti za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Zatvorene su kontrole obavijesti za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Dopusti obavijesti za ovaj kanal"</string>
@@ -945,26 +921,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Uređivanje redoslijeda postavki."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključan zaslon"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Proširivanje"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimiziraj"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Zatvori"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Postavke"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Povucite prema dolje da biste odbacili"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Izbornik"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> jest na slici u slici"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ako ne želite da aplikacija <xliff:g id="NAME">%s</xliff:g> upotrebljava tu značajku, dodirnite da biste otvorili postavke i isključili je."</string>
- <string name="pip_play" msgid="333995977693142810">"Reproduciraj"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pauziraj"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Preskoči na sljedeće"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Preskoči na prethodno"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefon se isključio zbog vrućine"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefon sada radi normalno"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefon sad radi normalno.\nDodirnite za više informacija"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon se pregrijao, stoga se isključio kako bi se ohladio Telefon sada radi normalno.\n\nTelefon se može pregrijati ako:\n • upotrebljavate zahtjevne aplikacije (kao što su igre, aplikacije za videozapise ili navigaciju)\n • preuzimate ili prenosite velike datoteke\n • upotrebljavate telefon na visokim temperaturama."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Pročitajte upute za održavanje"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon se zagrijava"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Neke su značajke ograničene dok se telefon hladi"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Neke su značajke ograničene dok se telefon ne ohladi.\nDodirnite za više informacija"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon će se automatski pokušati ohladiti. Možete ga nastaviti koristiti, no mogao bi raditi sporije.\n\nKad se ohladi, radit će normalno."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pročitajte upute za održavanje"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Iskopčajte punjač"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Pojavio se problem s punjenjem uređaja. Iskopčajte pretvarač napona i pazite jer se kabel može zagrijati."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Pogledajte upute za održavanje"</string>
@@ -1084,6 +1048,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Zadržite i povucite da biste promijenili raspored kontrola"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Sve su kontrole uklonjene"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Promjene nisu spremljene"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Pogledajte ostale aplikacije"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontrole se ne mogu učitati. U aplikaciji <xliff:g id="APP">%s</xliff:g> provjerite da se postavke aplikacije nisu promijenile."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kompatibilne kontrole nisu dostupne"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Drugo"</string>
@@ -1101,8 +1066,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Potvrdite promjenu za uređaj <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Prijeđite prstom da vidite više"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Zatvorite ovu medijsku sesiju"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrij trenutačnu sesiju."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sakrij"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, provjerite aplik."</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Pogreška, pokušavamo ponovo…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
diff --git a/packages/SystemUI/res/values-hr/strings_tv.xml b/packages/SystemUI/res/values-hr/strings_tv.xml
index e226460298b7..59dcd0ceadbb 100644
--- a/packages/SystemUI/res/values-hr/strings_tv.xml
+++ b/packages/SystemUI/res/values-hr/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Slika u slici"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program bez naslova)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Zatvori PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Cijeli zaslon"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon aktivan"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Aplikacija %1$s pristupila je mikrofonu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 6ac66f1600a4..c8f4a467f94b 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Koppintson ismét a megnyitáshoz"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Csúsztasson felfelé a megnyitáshoz"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Az újrapróbálkozáshoz csúsztassa felfelé az ujját"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Ez az eszköz az Ön szervezetének tulajdonában van"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ez az eszköz a(z) <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> tulajdonában van"</string>
<string name="phone_hint" msgid="6682125338461375925">"A telefonhoz csúsztasson az ikonról"</string>
<string name="voice_hint" msgid="7476017460191291417">"A hangsegéd eléréséhez csúsztassa ujját az ikonról"</string>
<string name="camera_hint" msgid="4519495795000658637">"A fényképezőhöz csúsztasson az ikonról"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profilját felügyelhetik"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Lehet, hogy a hálózatot figyelik"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Lehet, hogy a hálózat felügyelt"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Az eszköz az Ön szervezetének tulajdonában van, és lehetséges, hogy a hálózati forgalmat is figyelik"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Az eszköz a(z) <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tulajdonában van, és lehetséges, hogy a hálózati forgalmat is figyelik"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Ez az eszköz az Ön szervezetének tulajdonában van, és össze van kapcsolva a(z) <xliff:g id="VPN_APP">%1$s</xliff:g> alkalmazással"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Ez az eszköz a(z) <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tulajdonában van, és össze van kapcsolva a(z) <xliff:g id="VPN_APP">%2$s</xliff:g> alkalmazással"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Ez az eszköz az Ön szervezetének tulajdonában van"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Ez az eszköz a(z) <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tulajdonában van"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Ez az eszköz az Ön szervezetének tulajdonában van, és VPN-ekhez van csatlakoztatva"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Ez az eszköz a(z) <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tulajdonában van, és VPN-ekhez van csatlakoztatva"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Szervezete figyelheti a munkaprofil hálózati forgalmát"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"A(z) <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> figyelheti a munkaprofil hálózati forgalmát"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Elképzelhető, hogy a hálózatot figyelik"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Ez az eszköz VPN-ekhez van csatlakoztatva"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Munkaprofilja össze van kapcsolva a(z) <xliff:g id="VPN_APP">%1$s</xliff:g> alkalmazással"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Személyes profilja össze van kapcsolva a(z) <xliff:g id="VPN_APP">%1$s</xliff:g> alkalmazással"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Ez az eszköz össze van kapcsolva a(z) <xliff:g id="VPN_APP">%1$s</xliff:g> alkalmazással"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Eszközkezelés"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profilfelügyelet"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Hálózatfigyelés"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN letiltása"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN-kapcsolat bontása"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Házirendek megtekintése"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Ez az eszköz a(z) <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tulajdonában van.\n\nA rendszergazda figyelheti és kezelheti az eszköz beállításait, vállalati hozzáférését, alkalmazásait, adatait és helyadatait.\n\nHa további információra van szüksége, vegye fel a kapcsolatot a rendszergazdával."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Ez az eszköz az Ön szervezetének tulajdonában van.\n\nA rendszergazda figyelheti és kezelheti az eszköz beállításait, vállalati hozzáférését, alkalmazásait, adatait és helyadatait.\n\nHa további információra van szüksége, vegye fel a kapcsolatot a rendszergazdával."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Szervezete tanúsítványkibocsátót telepített az eszközre. Ezáltal figyelhetik és befolyásolhatják az Ön biztonságos hálózati forgalmát."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Szervezete tanúsítványkibocsátót telepített a munkaprofilba. Ezáltal figyelhetik és befolyásolhatják az Ön biztonságos hálózati forgalmát."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Az eszközre tanúsítványkibocsátó van telepítve. Ezáltal figyelhetik és befolyásolhatják az Ön biztonságos hálózati forgalmát."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Néma"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Alapértelmezett"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Buborék"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatikus"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Nincs hang és rezgés"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nincs hang és rezgés, továbbá lejjebb jelenik meg a beszélgetések szakaszában"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"A telefonbeállítások alapján csöröghet és rezeghet"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"A telefonbeállítások alapján csöröghet és rezeghet. A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásban lévő beszélgetések alapértelmezés szerint buborékban jelennek meg."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"A tartalomra mutató lebegő parancsikon segítségével tartja fenn az Ön figyelmét."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"A rendszer határozza meg, hogy ez az értesítés adjon-e ki hangot, illetve rezegjen-e"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"A beszélgetések szakaszának tetején, lebegő buborékként látható, megjeleníti a profilképet a lezárási képernyőn"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Beállítások"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritás"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ez az alkalmazás a képernyőn lévő egyéb alkalmazások előtt jelenik meg, és használja a mikrofont és a kamerát."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Beállítások"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Ezt az értesítést lenémította a rendszer."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Ezt az értesítést magasabb prioritásra állította a rendszer."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Ezt az értesítést alacsonyabb prioritásra állította a rendszer."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Megfelelő ez így?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Köszönjük a visszajelzést!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> értesítésvezérlői megnyitva"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> értesítésvezérlői kikapcsolva"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Értesítések engedélyezése erről a csatornáról"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Beállítások sorrendjének szerkesztése."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. oldal, összesen: <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lezárási képernyő"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Kibontás"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Kis méret"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Bezárás"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Beállítások"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Elvetéshez húzza lefelé"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menü"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"A(z) <xliff:g id="NAME">%s</xliff:g> kép a képben funkciót használ"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ha nem szeretné, hogy a(z) <xliff:g id="NAME">%s</xliff:g> használja ezt a funkciót, koppintson a beállítások megnyitásához, és kapcsolja ki."</string>
- <string name="pip_play" msgid="333995977693142810">"Lejátszás"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Szüneteltetés"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Ugrás a következőre"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Ugrás az előzőre"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"A meleg miatt kikapcsolt"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"A telefon most már megfelelően működik"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefonja most már megfelelően működik.\nKoppintson, ha további információra van szüksége."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonja túlmelegedett, így kikapcsolt, hogy lehűlhessen. Most már megfelelően működik.\n\nA telefon akkor melegedhet túl, ha Ön:\n • Energiaigényes alkalmazásokat használ (például játékokat, videókat vagy navigációs alkalmazásokat)\n • Nagy fájlokat tölt le vagy fel\n • Melegben használja a telefonját"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Olvassa el a kímélő használat lépéseit"</string>
<string name="high_temp_title" msgid="2218333576838496100">"A telefon melegszik"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Bizonyos funkciók korlátozottan működnek a telefon hűlése közben"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Bizonyos funkciók korlátozottan működnek a telefon lehűlése közben.\nKoppintson, ha további információra van szüksége."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"A telefon automatikusan megpróbál lehűlni. Továbbra is tudja használni a telefont, de elképzelhető, hogy működése lelassul.\n\nAmint a telefon lehűl, újra a szokásos módon működik majd."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Olvassa el a kímélő használat lépéseit"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Húzza ki a töltőt"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Probléma adódott az eszköz töltése során. Húzza ki a hálózati adaptert. Vigyázzon, a kábel forró lehet."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Olvassa el a megfelelő használat lépéseit"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Tartsa lenyomva, és húzza a vezérlők átrendezéséhez"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Minden vezérlő eltávolítva"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"A rendszer nem mentette a módosításokat"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Többi alkalmazás megtekintése"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Nem sikerült betölteni a vezérlőket. Ellenőrizze a(z) <xliff:g id="APP">%s</xliff:g> alkalmazást, és győződjön meg arról, hogy nem változtak az alkalmazásbeállítások."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Nem állnak rendelkezésre kompatibilis vezérlők"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Más"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"A(z) <xliff:g id="DEVICE">%s</xliff:g> módosításának megerősítése"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Továbbiak megtekintéséhez csúsztasson"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Javaslatok betöltése…"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Médiamunkamenet bezárása"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Jelenlegi munkamenet elrejtése."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Elrejtés"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Folytatás"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Beállítások"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktív, ellenőrizze az appot"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Hiba, újrapróbálkozás…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nem található"</string>
diff --git a/packages/SystemUI/res/values-hu/strings_tv.xml b/packages/SystemUI/res/values-hu/strings_tv.xml
index f039c0d82238..548207244ad0 100644
--- a/packages/SystemUI/res/values-hu/strings_tv.xml
+++ b/packages/SystemUI/res/values-hu/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Kép a képben"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Cím nélküli program)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP bezárása"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Teljes képernyő"</string>
<string name="mic_active" msgid="5766614241012047024">"A mikrofon aktív"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"A(z) %1$s hozzáfért a mikrofonhoz"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index cef564a1598c..53286188af47 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Կրկին հպեք՝ բացելու համար"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Բացելու համար սահեցրեք վերև"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Սահեցրեք վերև՝ նորից փորձելու համար"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Այս սարքը պատկանում է ձեր կազմակերպությանը"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Այս սարքը պատկանում է «<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>» կազմակերպությանը"</string>
<string name="phone_hint" msgid="6682125338461375925">"Սահահարվածեք հեռախոսի պատկերակից"</string>
<string name="voice_hint" msgid="7476017460191291417">"Սահահարվածեք ձայնային հուշման պատկերակից"</string>
<string name="camera_hint" msgid="4519495795000658637">"Սահահարվածեք խցիկի պատկերակից"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Պրոֆիլը կարող է վերահսկվել"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Ցանցը կարող է վերահսկվել"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Ցանցը կարող է վերահսկվել"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ձեր կազմակերպությունը այս սարքի սեփականատերն է և կարող է վերահսկել ցանցային թրաֆիկը"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"«<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>» կազմակերպությունը այս սարքի սեփականատերն է և կարող է վերահսկել ցանցային թրաֆիկը"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Այս սարքը պատկանում է ձեր կազմակերպությանը և միացված է <xliff:g id="VPN_APP">%1$s</xliff:g> ցանցին"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Այս սարքը պատկանում է «<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>» կազմակերպությանը և միացված է <xliff:g id="VPN_APP">%2$s</xliff:g> ցանցին"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Այս սարքը պատկանում է ձեր կազմակերպությանը"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Այս սարքը պատկանում է «<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>» կազմակերպությանը"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Այս սարքը պատկանում է ձեր կազմակերպությանը և միացված է վիրտուալ մասնավոր ցանցերի"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Այս սարքը պատկանում է «<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>» կազմակերպությանը և միացված է վիրտուալ մասնավոր ցանցերի"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Ձեր կազմակերպությունը կարող է վերահսկել ձեր աշխատանքային պրոֆիլի ցանցային թրաֆիկը"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> կազմակերպությունը կարող է վերահսկել ձեր աշխատանքային պրոֆիլի ցանցային թրաֆիկը"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Ցանցը կարող է վերահսկվել"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Այս սարքը միացված է վիրտուալ մասնավոր ցանցերի"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Ձեր աշխատանքային պրոֆիլը միացված է <xliff:g id="VPN_APP">%1$s</xliff:g> ցանցին"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Ձեր անձնական պրոֆիլը միացված է <xliff:g id="VPN_APP">%1$s</xliff:g> ցանցին"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Այս սարքը միացված է <xliff:g id="VPN_APP">%1$s</xliff:g> ցանցին"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Սարքերի կառավարում"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Պրոֆիլի վերահսկում"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Ցանցի մշտադիտարկում"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Անջատել VPN-ը"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Անջատել VPN-ը"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Դիտել քաղաքականությունները"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Այս սարքը պատկանում է «<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>» կազմակերպությանը։\n\nՁեր ՏՏ ադմինիստրատորը կարող է վերահսկել և կառավարել կարգավորումները, կորպորատիվ ռեսուրսներից օգտվելու թույլտվությունները, հավելվածները, սարքի հետ կապված տվյալները և սարքի տեղադրության տվյալները։\n\nԼրացուցիչ տեղեկությունների համար դիմեք ձեր ՏՏ ադմինիստրատորին։"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Այս սարքը պատկանում է ձեր կազմակերպությանը։\n\nՁեր ՏՏ ադմինիստրատորը կարող է վերահսկել և կառավարել կարգավորումները, կորպորատիվ ռեսուրսներից օգտվելու թույլտվությունները, հավելվածները, սարքի հետ կապված տվյալները և սարքի տեղադրության տվյալները։\n\nԼրացուցիչ տեղեկությունների համար դիմեք ձեր ՏՏ ադմինիստրատորին։"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Ձեր կազմակերպությունը այս սարքում տեղադրել է վկայագրման կենտրոն։ Ձեր ցանցի ապահով թրաֆիկը կարող է վերահսկվել կամ փոփոխվել։"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ձեր կազմակերպությունը ձեր աշխատանքային պրոֆիլում տեղադրել է վկայագրման կենտրոն։ Ձեր ցանցի ապահով թրաֆիկը կարող է վերահսկվել կամ փոփոխվել։"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Այս սարքում տեղադրված է վկայագրման կենտրոն։ Ձեր ցանցի ապահով թրաֆիկը կարող է վերահսկվել կամ փոփոխվել։"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Անձայն"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Կանխադրված"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Պղպջակ"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Ավտոմատ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Առանց ձայնի կամ թրթռոցի"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Հայտնվում է զրույցների ցանկի ներքևում, առանց ձայնի և թրթռոցի"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Կարող է զնգալ կամ թրթռալ (հեռախոսի կարգավորումներից կախված)"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Կարող է զնգալ կամ թրթռալ (հեռախոսի կարգավորումներից կախված)։ <xliff:g id="APP_NAME">%1$s</xliff:g>-ի զրույցներն ըստ կանխադրման հայտնվում են ամպիկների տեսքով։"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Լողացող դյուրանցման միջոցով ձեր ուշադրությունն է գրավում բովանդակության նկատմամբ"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Թող համակարգն ավտոմատ որոշի՝ արդյոք այս ծանուցումը ձայնով, թե թրթռոցով է պետք մատուցել"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Ցուցադրվում է զրույցների ցանկի վերևում, հայտնվում է լողացող ամպիկի տեսքով, ցուցադրում է պրոֆիլի նկարը կողպէկրանին"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Կարգավորումներ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Կարևոր"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Այս հավելվածը ցուցադրվում է մյուս հավելվածների վրայից և օգտագործում է խոսափողն ու տեսախցիկը:"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Կարգավորումներ"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Եղավ"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Այս ծանուցման ձայնն անջատվել է համակարգի կողմից։"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Այս ծանուցման կարևորության մակարդակը բարձրացվել է համակարգի կողմից։"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Այս ծանուցման կարևորության մակարդակը իջեցվել է համակարգի կողմից։"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Սա ճի՞շտ էր"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Շնորհակալություն արձագանքելու համար"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Եղավ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ծանուցումների կառավարումը բաց է"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ծանուցումների կառավարումը փակ է"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Թույլ տալ ծանուցումներ այս ալիքից"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Խմբագրել կարգավորումների հերթականությունը:"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Էջ <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Կողպէկրան"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Ընդարձակել"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Ծալել"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Փակել"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Կարգավորումներ"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Քաշեք վար՝ փակելու համար"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Ընտրացանկ"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g>-ը «Նկար նկարի մեջ» ռեժիմում է"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Եթե չեք ցանկանում, որ <xliff:g id="NAME">%s</xliff:g>-ն օգտագործի այս գործառույթը, հպեք՝ կարգավորումները բացելու և այն անջատելու համար։"</string>
- <string name="pip_play" msgid="333995977693142810">"Նվագարկել"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Դադարեցնել"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Անցնել հաջորդին"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Վերադառնալ նախորդին"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Հեռախոսն անջատվել է տաքանալու պատճառով"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Հեռախոսն այժմ նորմալ աշխատում է"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Հեռախոսն այժմ նորմալ է աշխատում։\nՀպեք՝ ավելին իմանալու համար։"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Ձեր հեռախոսը չափազանց տաք էր, այդ պատճառով այն անջատվել է՝ հովանալու համար: Հեռախոսն այժմ նորմալ աշխատում է:\n\nՀեռախոսը կարող է տաքանալ, եթե՝\n • Օգտագործում եք ռեսուրսատար հավելվածներ (օրինակ՝ խաղեր, տեսանյութեր կամ նավարկման հավելվածներ)\n • Ներբեռնում կամ վերբեռնում եք ծանր ֆայլեր\n • Օգտագործում եք ձեր հեռախոսը բարձր ջերմային պայմաններում"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Քայլեր գերտաքացման ահազանգի դեպքում"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Հեռախոսը տաքանում է"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Հովանալու ընթացքում հեռախոսի որոշ գործառույթներ սահմանափակ են"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Հովանալու ընթացքում հեռախոսի որոշ գործառույթներ սահմանափակ են։\nՀպեք՝ ավելին իմանալու համար։"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ձեր հեռախոսն ավտոմատ կերպով կփորձի hովանալ: Կարող եք շարունակել օգտագործել հեռախոսը, սակայն հնարավոր է, որ այն ավելի դանդաղ աշխատի:\n\nՀովանալուց հետո հեռախոսը կաշխատի կանոնավոր կերպով:"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Քայլեր գերտաքացման ահազանգի դեպքում"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Անջատեք լիցքավորիչը հոսանքից"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Չհաջողվեց լիցքավորել սարքը: Անջատեք հոսանքի ադապտերը և ուշադիր եղեք՝ մալուխը կարող է տաքացած լինել:"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Քայլեր գերտաքացման ահազանգի դեպքում"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Պահեք և քաշեք՝ կառավարման տարրերը վերադասավորելու համար"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Կառավարման բոլոր տարրերը հեռացվեցին"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Փոփոխությունները չեն պահվել"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Տեսնել այլ հավելվածներ"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Չհաջողվեց բեռնել կառավարման տարրերը։ Ստուգեք <xliff:g id="APP">%s</xliff:g> հավելվածը՝ համոզվելու, որ հավելվածի կարգավորումները չեն փոխվել։"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Համատեղելի կառավարման տարրերը հասանելի չեն"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Այլ"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Հաստատեք փոփոխությունը <xliff:g id="DEVICE">%s</xliff:g> սարքի համար"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Սահեցրեք մատը՝ ավելին իմանալու համար"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Բեռնման խորհուրդներ"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Փակել աշխատաշրջանը"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Մեդիա"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Թաքցրեք ընթացիկ աշխատաշրջանը"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Թաքցնել"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Շարունակել"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Կարգավորումներ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Ակտիվ չէ, ստուգեք հավելվածը"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Սխալ. նորից ենք փորձում…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Չի գտնվել"</string>
diff --git a/packages/SystemUI/res/values-hy/strings_tv.xml b/packages/SystemUI/res/values-hy/strings_tv.xml
index d5dad69ca1ac..4009335ec7d5 100644
--- a/packages/SystemUI/res/values-hy/strings_tv.xml
+++ b/packages/SystemUI/res/values-hy/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Նկար նկարի մեջ"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Առանց վերնագրի ծրագիր)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Փակել PIP-ն"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Լիէկրան"</string>
<string name="mic_active" msgid="5766614241012047024">"Խոսափողն ակտիվացված է"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s հավելվածն օգտագործել է ձեր խոսափողը"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 5d46920d7c69..dfed57b94c51 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ketuk lagi untuk membuka"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Geser ke atas untuk membuka"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Geser ke atas untuk mencoba lagi"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Perangkat ini milik organisasi Anda"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Perangkat ini milik <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Geser dari ikon untuk telepon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Geser dari ikon untuk bantuan suara"</string>
<string name="camera_hint" msgid="4519495795000658637">"Geser dari ikon untuk kamera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil dapat dipantau"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Jaringan mungkin dipantau"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Jaringan mungkin dipantau"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisasi Anda memiliki perangkat ini dan mungkin memantau traffic jaringan"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> memiliki perangkat ini dan mungkin memantau traffic jaringan"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Perangkat ini milik organisasi Anda dan terhubung ke <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Perangkat ini milik <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> dan terhubung ke <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Perangkat ini milik organisasi Anda"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Perangkat ini milik <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Perangkat ini milik organisasi Anda dan terhubung ke VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Perangkat ini milik <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> dan terhubung ke VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Organisasi dapat memantau traffic jaringan di profil kerja"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> mungkin memantau traffic jaringan di profil kerja Anda"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Jaringan mungkin dipantau"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Perangkat ini terhubung ke VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Profil kerja Anda terhubung ke <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Profil pribadi Anda terhubung ke <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Perangkat ini terhubung ke <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Pengelolaan perangkat"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Pemantauan profil"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Pemantauan jaringan"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Nonaktifkan VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Putuskan sambungan VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Lihat Kebijakan"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Perangkat ini milik <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdmin IT Anda dapat memantau dan mengelola setelan, akses perusahaan, aplikasi, data yang terkait dengan perangkat, dan informasi lokasi perangkat.\n\nUntuk informasi selengkapnya, hubungi admin IT Anda."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Perangkat ini milik organisasi Anda.\n\nAdmin IT Anda dapat memantau dan mengelola setelan, akses perusahaan, aplikasi, data yang terkait dengan perangkat, dan informasi lokasi perangkat.\n\nUntuk informasi selengkapnya, hubungi admin IT Anda."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organisasi Anda menginstal otoritas sertifikat di perangkat ini. Traffic jaringan aman Anda mungkin dipantau atau diubah."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisasi Anda menginstal otoritas sertifikat di profil kerja. Traffic jaringan aman Anda mungkin dipantau atau diubah."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Otoritas sertifikat diinstal di perangkat. Traffic jaringan aman Anda mungkin dipantau atau diubah."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Senyap"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Balon"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatis"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Tidak ada suara atau getaran"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Tidak ada suara atau getaran dan ditampilkan lebih rendah di bagian percakapan"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Dapat berdering atau bergetar berdasarkan setelan ponsel"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Dapat berdering atau bergetar berdasarkan setelan ponsel. Percakapan dari balon <xliff:g id="APP_NAME">%1$s</xliff:g> secara default."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Menjaga perhatian dengan pintasan floating ke konten ini."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Biarkan sistem menentukan apakah notifikasi ini akan berbunyi atau bergetar"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Muncul di atas bagian percakapan, ditampilkan sebagai balon yang mengambang, menampilkan gambar profil di layar kunci"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Setelan"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritas"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Aplikasi ini ditampilkan di atas aplikasi lain di layar serta sedang menggunakan mikrofon dan kamera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Setelan"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Ya"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Notifikasi ini disenyapkan oleh sistem."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Notifikasi ini dipromosikan oleh sistem."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Notifikasi ini didemosikan oleh sistem."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Sudah benar?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Terima kasih atas masukan Anda"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Oke"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrol notifikasi untuk <xliff:g id="APP_NAME">%1$s</xliff:g> dibuka"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Kontrol notifikasi untuk <xliff:g id="APP_NAME">%1$s</xliff:g> ditutup"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Izinkan notifikasi dari saluran ini"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Edit urutan setelan."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Halaman <xliff:g id="ID_1">%1$d</xliff:g> dari <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Layar kunci"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Luaskan"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimalkan"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Tutup"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Setelan"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Tarik ke bawah untuk menutup"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> adalah picture-in-picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Jika Anda tidak ingin <xliff:g id="NAME">%s</xliff:g> menggunakan fitur ini, ketuk untuk membuka setelan dan menonaktifkannya."</string>
- <string name="pip_play" msgid="333995977693142810">"Putar"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Jeda"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Lewati ke berikutnya"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Lewati ke sebelumnya"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Ponsel dimatikan karena panas"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Ponsel kini berfungsi normal"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Ponsel kini berfungsi normal.\nKetuk untuk info selengkapnya"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Ponsel menjadi terlalu panas, jadi dimatikan untuk mendinginkan. Ponsel kini berfungsi normal.\n\nPonsel dapat menjadi terlalu panas jika Anda:\n • Menggunakan aplikasi yang menggunakan sumber daya secara intensif (seperti aplikasi game, video, atau navigasi)\n • Mendownload atau mengupload file besar\n • Menggunakan ponsel dalam suhu tinggi"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Lihat langkah-langkah perawatan"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Ponsel menjadi hangat"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Beberapa fitur dibatasi saat ponsel mendingin"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Beberapa fitur dibatasi saat ponsel mendingin.\nKetuk untuk info selengkapnya"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ponsel akan otomatis mencoba mendingin. Anda tetap dapat menggunakan ponsel, tetapi mungkin berjalan lebih lambat.\n\nSetelah dingin, ponsel akan berjalan seperti biasa."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Lihat langkah-langkah perawatan"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Cabut pengisi daya"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Ada masalah saat mengisi daya perangkat ini. Cabut adaptor daya dan berhati-hatilah karena kabelnya mungkin panas."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Lihat langkah-langkah perawatan"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Tahan &amp; tarik untuk mengatur ulang kontrol"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Semua kontrol dihapus"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Perubahan tidak disimpan"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Lihat aplikasi lainnya"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontrol tidak dapat dimuat. Periksa aplikasi <xliff:g id="APP">%s</xliff:g> untuk memastikan setelan aplikasi tidak berubah."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kontrol yang kompatibel tidak tersedia"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Lainnya"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Konfirmasi perubahan untuk <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Geser untuk melihat selengkapnya"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuat rekomendasi"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Tutup sesi media ini"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Menyembunyikan sesi saat ini."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sembunyikan"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Lanjutkan"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Setelan"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Nonaktif, periksa aplikasi"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Error, mencoba lagi..."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
diff --git a/packages/SystemUI/res/values-in/strings_tv.xml b/packages/SystemUI/res/values-in/strings_tv.xml
index 5c4212358c65..670f8125582a 100644
--- a/packages/SystemUI/res/values-in/strings_tv.xml
+++ b/packages/SystemUI/res/values-in/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program tanpa judul)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Tutup PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Layar penuh"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon Aktif"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s mengakses mikrofon"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index efc567b907cd..57cdb272f176 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ýttu aftur til að opna"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Strjúktu upp til að opna"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Strjúktu upp til að reyna aftur"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Þetta tæki tilheyrir fyrirtækinu þínu"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Þetta tæki tilheyrir <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Strjúktu frá tákninu fyrir síma"</string>
<string name="voice_hint" msgid="7476017460191291417">"Strjúktu frá tákninu fyrir raddaðstoð"</string>
<string name="camera_hint" msgid="4519495795000658637">"Strjúktu frá tákninu fyrir myndavél"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Hugsanlega er fylgst með þessu sniði"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Hugsanlega er fylgst með netinu"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Hugsanlega er fylgst með netinu"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Fyrirtækið þitt á þetta tæki og fylgist hugsanlega með netumferð"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> á þetta tæki og fylgist hugsanlega með netumferð"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Þetta tæki tilheyrir fyrirtækinu þínu og er tengt við <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Þetta tæki tilheyrir <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> og er tengt við <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Þetta tæki tilheyrir fyrirtækinu þínu"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Þetta tæki tilheyrir <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Þetta tæki tilheyrir fyrirtækinu þínu og er tengt við VPN-net"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Þetta tæki tilheyrir <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> og er tengt við VPN-net"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Fyrirtækið þitt kann að fylgjast með netnotkun á vinnusniðinu þínu"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kann að fylgjast með netnotkun á vinnusniðinu þínu"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Hugsanlega er fylgst með netinu"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Þetta tæki er tengt við VPN-net"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Vinnusniðið þitt er tengt við <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Einkaprófíllinn þinn er tengdur við <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Þetta tæki er tengt við <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Tækjastjórnun"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Fylgst með sniði"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Neteftirlit"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Slökkva á VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Aftengja VPN-net"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Skoða stefnur"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Þetta tæki tilheyrir <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nKerfisstjórinn getur fylgst með og breytt stillingum, fyrirtækjaaðgangi, forritum, gögnum sem tengjast tækinu þínu og staðsetningarupplýsingum tækisins.\n\nHafðu samband við kerfisstjórann til að fá frekari upplýsingar."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Þetta tæki tilheyrir fyrirtækinu þínu.\n\nKerfisstjórinn getur fylgst með og breytt stillingum, fyrirtækjaaðgangi, forritum, gögnum sem tengjast tækinu þínu og staðsetningarupplýsingum tækisins.\n\nHafðu samband við kerfisstjórann til að fá frekari upplýsingar."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Fyrirtækið þitt setti upp CA-vottorð á þessu tæki. Eftirlit kann að vera haft með öruggri netnotkun þinni eða henni kann að vera breytt."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Fyrirtækið þitt setti upp CA-vottorð á vinnusniðinu þínu. Eftirlit kann að vera haft með öruggri netnotkun þinni eða henni kann að vera breytt."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"CA-vottorð er uppsett á þessu tæki. Eftirlit kann að vera haft með öruggri netnotkun þinni eða henni kann að vera breytt."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Hljóðlaust"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Sjálfgefið"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Blaðra"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Sjálfvirk"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ekkert hljóð eða titringur"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ekkert hljóð eða titringur og birtist neðar í samtalshluta"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Gæti hringt eða titrað eftir stillingum símans"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Gæti hringt eða titrað eftir stillingum símans. Samtöl á <xliff:g id="APP_NAME">%1$s</xliff:g> birtast sjálfkrafa í blöðru."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Fangar athygli þína með fljótandi flýtileið á þetta efni."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Láta kerfið ákvarða hvort hljóð eða titringur fylgir þessari tilkynningu"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Birtist efst í samtalshluta, birtist sem fljótandi blaðra, birtir prófílmynd á lásskjánum"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Áfram"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Forgangur"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Þetta forrit er að birta efni yfir öðrum forritum á skjánum þínum og er að nota hljóðnemann og myndavélina."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Stillingar"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Í lagi"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Kerfið þaggaði þessa tilkynningu."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Kerfið hækkaði þessa tilkynningu."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Kerfið lækkaði þessa tilkynningu."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Var þetta rétt?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Takk fyrir að segja þína skoðun!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Í lagi"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Opnað fyrir tilkynningastýringar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Lokað fyrir tilkynningastýringar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Leyfa tilkynningar frá þessari rás"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Breyta röð stillinga."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Blaðsíða <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lásskjár"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Stækka"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minnka"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Loka"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Stillingar"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Draga niður til að hunsa"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Valmynd"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> er með mynd í mynd"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ef þú vilt ekki að <xliff:g id="NAME">%s</xliff:g> noti þennan eiginleika skaltu ýta til að opna stillingarnar og slökkva á því."</string>
- <string name="pip_play" msgid="333995977693142810">"Spila"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Gera hlé"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Fara á næsta"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Fara á fyrra"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Slökkt var á símanum vegna hita"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Síminn virkar núna sem skyldi"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Síminn virkar nú eins og venjulega.\nÝttu til að fá frekari upplýsingar"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Síminn varð of heitur og því var slökkt á honum til að kæla hann. Síminn virkar núna sem skyldi.\n\nSíminn getur orðið of heitur ef þú:\n • Notar plássfrek forrit (t.d. leikja-, myndbands- eða leiðsagnarforrit\n • Sækir eða hleður upp stórum skrám\n • Notar símann í miklum hita"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Sjá varúðarskref"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Síminn er að hitna"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Sumir eiginleikar eru takmarkaðir þegar síminn kælir sig"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Sumir eiginleikar eru takmarkaðir meðan síminn kælir sig.\nÝttu til að fá frekari upplýsingar"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Síminn reynir sjálfkrafa að kæla sig. Þú getur enn notað símann en hann gæti verið hægvirkari.\n\nEftir að síminn hefur kælt sig niður virkar hann eðlilega."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Sjá varúðarskref"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Taktu hleðslutækið úr sambandi"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Upp kom vandamál varðandi hleðslu tækisins. Taktu straumbreytinn úr sambandi og farðu varlega því snúran gæti verið heit."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Sjá varúðarskref"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Haltu og dragðu til að endurraða stýringum"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Allar stýringar fjarlægðar"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Breytingar ekki vistaðar"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Sjá önnur forrit"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Ekki tókst að hlaða stýringum. Athugaðu <xliff:g id="APP">%s</xliff:g> til að ganga úr skugga um að stillingar forritsins hafi ekki breyst."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Samhæfar stýringar eru ekki tiltækar"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Annað"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Staðfesta breytingu fyrir <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Strjúktu til að sjá meira"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Hleður tillögum"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Loka þessari efnislotu"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Margmiðlunarefni"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Fela núverandi lotu."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Fela"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Halda áfram"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Stillingar"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Óvirkt, athugaðu forrit"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Villa, reynir aftur…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Fannst ekki"</string>
diff --git a/packages/SystemUI/res/values-is/strings_tv.xml b/packages/SystemUI/res/values-is/strings_tv.xml
index d3a2bec324ee..27334af093f5 100644
--- a/packages/SystemUI/res/values-is/strings_tv.xml
+++ b/packages/SystemUI/res/values-is/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Mynd í mynd"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Efni án titils)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Loka mynd í mynd"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Allur skjárinn"</string>
<string name="mic_active" msgid="5766614241012047024">"Hljóðnemi virkur"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s fékk aðgang að hljóðnemanum þínum"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 6bdc9024a93e..679eb551e7f0 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tocca ancora per aprire"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Scorri verso l\'alto per aprire"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Scorri verso l\'alto per riprovare"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Questo dispositivo appartiene alla tua organizzazione"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Questo dispositivo appartiene a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Scorri per accedere al telefono"</string>
<string name="voice_hint" msgid="7476017460191291417">"Scorri dall\'icona per accedere a Voice Assist"</string>
<string name="camera_hint" msgid="4519495795000658637">"Scorri dall\'icona per accedere alla fotocamera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Il profilo potrebbe essere monitorato"</string>
<string name="vpn_footer" msgid="3457155078010607471">"La rete potrebbe essere monitorata"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"La rete potrebbe essere monitorata"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Questo dispositivo appartiene alla tua organizzazione, che potrebbe monitorare il traffico di rete"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Questo dispositivo appartiene a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>, che potrebbe monitorare il traffico di rete"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Questo dispositivo appartiene alla tua organizzazione ed è collegato a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Questo dispositivo appartiene a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ed è collegato a <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Questo dispositivo appartiene alla tua organizzazione"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Questo dispositivo appartiene a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Questo dispositivo appartiene alla tua organizzazione ed è collegato a VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Questo dispositivo appartiene a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ed è collegato a VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"La tua organizzazione potrebbe monitorare il traffico di rete nel tuo profilo di lavoro"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> potrebbe monitorare il traffico di rete nel tuo profilo di lavoro"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"La rete potrebbe essere monitorata"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Questo dispositivo è collegato a VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Il tuo profilo di lavoro è collegato a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Il tuo profilo personale è collegato a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Questo dispositivo è collegato a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gestione dei dispositivi"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Monitoraggio del profilo"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Monitoraggio rete"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Disattiva VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Scollega VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Visualizza le norme"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Questo dispositivo appartiene a <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nIl tuo amministratore IT può monitorare e gestire impostazioni, accesso aziendale, app, dati associati al dispositivo e informazioni sulla posizione del dispositivo.\n\nPer ulteriori informazioni, contatta il tuo amministratore IT."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Questo dispositivo appartiene alla tua organizzazione.\n\nIl tuo amministratore IT può monitorare e gestire impostazioni, accesso aziendale, app, dati associati al dispositivo e informazioni sulla posizione del dispositivo.\n\nPer ulteriori informazioni, contatta il tuo amministratore IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"La tua organizzazione ha installato un\'autorità di certificazione sul dispositivo. Il tuo traffico di rete protetto potrebbe essere monitorato o modificato."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"La tua organizzazione ha installato un\'autorità di certificazione nel tuo profilo di lavoro. Il tuo traffico di rete protetto potrebbe essere monitorato o modificato."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sul dispositivo è installata un\'autorità di certificazione. Il tuo traffico di rete protetto potrebbe essere monitorato o modificato."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Modalità silenziosa"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Modalità predefinita"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Fumetto"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatico"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Nessun suono o vibrazione"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nessun suono o vibrazione e appare più in basso nella sezione delle conversazioni"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Può suonare o vibrare in base alle impostazioni del telefono"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Può suonare o vibrare in base alle impostazioni del telefono. Conversazioni dalla bolla <xliff:g id="APP_NAME">%1$s</xliff:g> per impostazione predefinita."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantiene la tua attenzione con una scorciatoia mobile a questi contenuti."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai stabilire al sistema se questa notifica deve emettere suoni o vibrazioni"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Viene mostrata in cima alla sezione delle conversazioni, appare sotto forma di bolla mobile, mostra l\'immagine del profilo nella schermata di blocco"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Impostazioni"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priorità"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Questa app è visualizzata sopra altre app sullo schermo e sta utilizzando il microfono e la fotocamera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Impostazioni"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Ok"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Questa notifica è stata silenziata dal sistema."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Questa notifica è stata promossa dal sistema."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Questa notifica è stata retrocessa dal sistema."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Era corretto?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Grazie per il feedback."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controlli di gestione delle notifiche per <xliff:g id="APP_NAME">%1$s</xliff:g> aperti"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Controlli di gestione delle notifiche per <xliff:g id="APP_NAME">%1$s</xliff:g> chiusi"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Consenti le notifiche di questo canale"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Modifica l\'ordine delle impostazioni."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> di <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Schermata di blocco"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Espandi"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Riduci a icona"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Chiudi"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Impostazioni"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Trascina verso il basso per ignorare"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> è in Picture in picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Se non desideri che l\'app <xliff:g id="NAME">%s</xliff:g> utilizzi questa funzione, tocca per aprire le impostazioni e disattivarla."</string>
- <string name="pip_play" msgid="333995977693142810">"Riproduci"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Metti in pausa"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Passa ai contenuti successivi"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Passa ai contenuti precedenti"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Il telefono si è spento perché surriscaldato"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Ora il telefono funziona normalmente"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Ora il telefono funziona normalmente.\nTocca per ulteriori informazioni"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Il telefono era surriscaldato e si è spento per raffreddarsi. Ora funziona normalmente.\n\nIl telefono può surriscaldarsi se:\n • Utilizzi app che consumano molte risorse (ad esempio app di navigazione, giochi o video)\n • Scarichi o carichi grandi file\n • Lo utilizzi in presenza di alte temperature"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Leggi le misure da adottare"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Il telefono si sta scaldando"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Alcune funzioni limitate durante il raffreddamento del telefono"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Alcune funzionalità limitate durante il raffreddamento del telefono.\nTocca per ulteriori informazioni"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Il telefono cercherà automaticamente di raffreddarsi. Puoi comunque usarlo, ma potrebbe essere più lento.\n\nUna volta raffreddato, il telefono funzionerà normalmente."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Leggi le misure da adottare"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Scollega il caricabatterie"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Si è verificato un problema durante la ricarica del dispositivo. Scollega l\'alimentatore e presta attenzione perché il cavo potrebbe essere caldo."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Leggi le misure da adottare"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Tieni premuto e trascina per riordinare i controlli"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Tutti i controlli sono stati rimossi"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Modifiche non salvate"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Mostra altre app"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Impossibile caricare i controlli. Verifica nell\'app <xliff:g id="APP">%s</xliff:g> che le relative impostazioni non siano cambiate."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Controlli compatibili non disponibili"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Altro"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Conferma modifica per <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Scorri per vedere altro"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Caricamento dei consigli"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Chiudi questa sessione multimediale"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Contenuti multimediali"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Nascondi la sessione attuale."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Nascondi"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Riprendi"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Impostazioni"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inattivo, controlla l\'app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Errore. Nuovo tentativo…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Controllo non trovato"</string>
diff --git a/packages/SystemUI/res/values-it/strings_tv.xml b/packages/SystemUI/res/values-it/strings_tv.xml
index 782b959a134a..e0fce871895a 100644
--- a/packages/SystemUI/res/values-it/strings_tv.xml
+++ b/packages/SystemUI/res/values-it/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture in picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programma senza titolo)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Chiudi PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Schermo intero"</string>
<string name="mic_active" msgid="5766614241012047024">"Microfono attivo"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ha avuto accesso al tuo microfono"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 1e248317fab9..b6a723f00024 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -458,10 +458,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"הקש שוב כדי לפתוח"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"צריך להחליק כדי לפתוח"</string>
<string name="keyguard_retry" msgid="886802522584053523">"יש להחליק למעלה כדי לנסות שוב"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"המכשיר הזה שייך לארגון שלך"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"החלק מהסמל כדי להפעיל את הטלפון"</string>
<string name="voice_hint" msgid="7476017460191291417">"החלק מהסמל כדי להפעיל את המסייע הקולי"</string>
<string name="camera_hint" msgid="4519495795000658637">"החלק מהסמל כדי להפעיל את המצלמה"</string>
@@ -529,33 +527,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ייתכן שהפרופיל נתון למעקב"</string>
<string name="vpn_footer" msgid="3457155078010607471">"ייתכן שהרשת נמצאת במעקב"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ייתכן שהרשת מנוטרת"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"הארגון שלך הוא הבעלים של מכשיר זה והוא עשוי לנטר את התנועה ברשת"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"הארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> הוא הבעלים של מכשיר זה והוא עשוי לנטר את התנועה ברשת"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"המכשיר הזה שייך לארגון שלך, והוא מחובר ל-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> והוא מחובר ל-<xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"המכשיר הזה שייך לארגון שלך"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"‏המכשיר הזה שייך לארגון שלך והוא מחובר לרשתות VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"‏המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> והוא מחובר לרשתות VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"הארגון שלך יכול לנטר את התנועה ברשת בפרופיל העבודה שלך"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"הארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> עשוי לנטר את התנועה ברשת בפרופיל העבודה שלך"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ייתכן שהרשת מנוטרת"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"‏המכשיר הזה מחובר לרשתות VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"פרופיל העבודה שלך מחובר ל-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"הפרופיל האישי שלך מחובר ל-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"המכשיר הזה מחובר ל-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ניהול מכשירים"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"מעקב אחר פרופיל"</string>
<string name="monitoring_title" msgid="4063890083735924568">"מעקב אחר פעילות ברשת"</string>
@@ -565,10 +551,8 @@
<string name="disable_vpn" msgid="482685974985502922">"‏השבת VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"‏נתק את ה-VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"הצג מדיניות"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"‏המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nמנהל ה-IT יכול לנטר ולנהל הגדרות, גישה ארגונית, אפליקציות, נתונים המשויכים למכשיר ומידע על מיקום המכשיר.\n\nלמידע נוסף, יש לפנות למנהל ה-IT."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"‏המכשיר הזה שייך לארגון שלך.\n\nמנהל ה-IT יכול לנטר ולנהל הגדרות, גישה ארגונית, אפליקציות, נתונים המשויכים למכשיר ומידע על מיקום המכשיר.\n\nלמידע נוסף, יש לפנות למנהל ה-IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"הארגון שלך התקין רשות אישורים במכשיר. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"הארגון שלך התקין רשות אישורים בפרופיל העבודה. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"במכשיר זה מותקנת רשות אישורים. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
@@ -733,15 +717,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"שקט"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ברירת מחדל"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"בועה"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"באופן אוטומטי"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ללא צליל או רטט"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ללא צליל או רטט ומופיעה למטה בקטע התראות השיחה"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ייתכן שיופעל צלצול או רטט בהתאם להגדרות הטלפון"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ייתכן שיופעל צלצול או רטט בהתאם להגדרות הטלפון. שיחות מהאפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מופיעות בבועות כברירת מחדל."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"מעוררת תשומת לב באמצעות קיצור דרך צף לתוכן הזה."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"אפשר לתת למערכת לקבוע אם ההתראה הזאת צריכה להיות מלווה בצליל או ברטט"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"מוצגת בחלק העליון של קטע התראות השיחה, מופיעה בבועה צפה, תוצג תמונת פרופיל במסך הנעילה"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"הגדרות"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"עדיפות"</string>
@@ -762,18 +744,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"האפליקציה הזו מוצגת מעל אפליקציות אחרות במסך, ומשתמשת במיקרופון ובמצלמה."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"הגדרות"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"אישור"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ההתראה הזאת הושתקה על-ידי המערכת."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ההתראה הזאת קודמה על-ידי המערכת."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ההתראה הזאת הורדה בדרגה על-ידי המערכת."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"האם פעולה זו הייתה נכונה?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"תודה על המשוב!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"אישור"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"פקדי ההודעות של <xliff:g id="APP_NAME">%1$s</xliff:g> נפתחו"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"פקדי ההודעות של <xliff:g id="APP_NAME">%1$s</xliff:g> נסגרו"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"התר התראות מערוץ זה"</string>
@@ -950,26 +926,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"עריכת סדר ההגדרות."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"דף <xliff:g id="ID_1">%1$d</xliff:g> מתוך <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"מסך נעילה"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"הרחב"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"מזער"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"סגור"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"הגדרות"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"גרור למטה כדי לסגור"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"תפריט"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולכבות את התכונה."</string>
- <string name="pip_play" msgid="333995977693142810">"הפעל"</string>
- <string name="pip_pause" msgid="1139598607050555845">"השהה"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"אפשר לדלג אל הבא"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"אפשר לדלג אל הקודם"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"הטלפון כבה עקב התחממות"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"הטלפון פועל כרגיל עכשיו"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"הטלפון פועל כרגיל עכשיו.\nיש להקיש כדי להציג מידע נוסף"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"הטלפון שלך התחמם יותר מדי וכבה כדי להתקרר. הטלפון פועל כרגיל עכשיו.\n\nייתכן שהטלפון יתחמם יותר מדי אם:\n • תשתמש באפליקציות עתירות משאבים (כגון משחקים, אפליקציות וידאו או אפליקציות ניווט)\n • תוריד או תעלה קבצים גדולים\n • תשתמש בטלפון בטמפרטורות גבוהות"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"לצפייה בשלבי הטיפול"</string>
<string name="high_temp_title" msgid="2218333576838496100">"הטלפון מתחמם"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"חלק מהתכונות מוגבלות כל עוד הטלפון מתקרר"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"חלק מהתכונות מוגבלות כל עוד הטלפון מתקרר.\nיש להקיש כדי להציג מידע נוסף"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"קירור הטלפון ייעשה באופן אוטומטי. תוכל עדיין להשתמש בטלפון, אבל ייתכן שהוא יפעל לאט יותר.\n\nהטלפון יחזור לפעול כרגיל לאחר שיתקרר."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"לצפייה בשלבי הטיפול"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"יש לנתק את המטען"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"יש בעיה עם טעינת מכשיר זה. יש לנתק את מתאם המתח בזהירות כיוון שייתכן שהכבל חם."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"לצפייה בשלבי הטיפול"</string>
@@ -1090,6 +1054,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"יש ללחוץ לחיצה ארוכה ולגרור כדי לארגן מחדש את הפקדים"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"כל הפקדים הוסרו"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"השינויים לא נשמרו"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"הצגת אפליקציות אחרות"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"לא ניתן היה לטעון את הפקדים. יש לבדוק את האפליקציה <xliff:g id="APP">%s</xliff:g> כדי לוודא שהגדרות האפליקציה לא השתנו."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"פקדים תואמים לא זמינים"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"אחר"</string>
@@ -1107,8 +1072,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"יש לאשר את השינוי עבור <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"יש להחליק כדי להציג עוד פריטים"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"בטעינת המלצות"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"סגירת הסשן הזה של המדיה"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"מדיה"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"הסתרת הסשן הנוכחי."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"הסתרה"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"המשך"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"הגדרות"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"לא פעיל, יש לבדוק את האפליקציה"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"שגיאה, מתבצע ניסיון חוזר…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"לא נמצא"</string>
diff --git a/packages/SystemUI/res/values-iw/strings_tv.xml b/packages/SystemUI/res/values-iw/strings_tv.xml
index 1e5fc91ecff1..2bd86efa02a4 100644
--- a/packages/SystemUI/res/values-iw/strings_tv.xml
+++ b/packages/SystemUI/res/values-iw/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"תמונה בתוך תמונה"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(תוכנית ללא כותרת)"</string>
- <string name="pip_close" msgid="5775212044472849930">"‏סגור PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"מסך מלא"</string>
<string name="mic_active" msgid="5766614241012047024">"המיקרופון פעיל"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"‏%1$s קיבלה גישה למיקרופון שלך"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index b2c745f6fecd..b70e2e2bc14f 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"開くにはもう一度タップしてください"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"開くには上にスワイプします"</string>
<string name="keyguard_retry" msgid="886802522584053523">"上にスワイプしてもう一度お試しください"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"これは組織が所有するデバイスです"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"これは <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> が所有するデバイスです"</string>
<string name="phone_hint" msgid="6682125338461375925">"右にスワイプして通話"</string>
<string name="voice_hint" msgid="7476017460191291417">"アイコンからスワイプして音声アシストを起動"</string>
<string name="camera_hint" msgid="4519495795000658637">"左にスワイプしてカメラを起動"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"プロファイルが監視されている可能性があります"</string>
<string name="vpn_footer" msgid="3457155078010607471">"ネットワークが監視されている可能性があります"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ネットワークが監視されている可能性があります"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"これは組織が所有するデバイスで、ネットワーク トラフィックが監視されることもあります"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"これは <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> が所有するデバイスで、ネットワーク トラフィックが監視されることもあります"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"これは組織が所有するデバイスで、<xliff:g id="VPN_APP">%1$s</xliff:g> に接続しています"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"これは <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> が所有するデバイスで、<xliff:g id="VPN_APP">%2$s</xliff:g> に接続しています"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"これは組織が所有するデバイスです"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"これは <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> が所有するデバイスです"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"これは組織が所有するデバイスで、VPN に接続しています"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"これは <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> が所有するデバイスで、VPN に接続しています"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"組織は仕事用プロファイルのネットワーク トラフィックを監視することがあります"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> によってこの仕事用プロファイルのネットワーク トラフィックが監視されることもあります"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ネットワークが監視されることもあります"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"このデバイスは VPN に接続しています"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"この仕事用プロファイルは <xliff:g id="VPN_APP">%1$s</xliff:g> に接続しています"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"この個人用プロファイルは <xliff:g id="VPN_APP">%1$s</xliff:g> に接続しています"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"このデバイスは <xliff:g id="VPN_APP">%1$s</xliff:g> に接続しています"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"デバイス管理"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"プロファイルの監視"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ネットワーク監視"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPNを無効にする"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPNを切断"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"ポリシーを見る"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"これは <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> が所有するデバイスです。\n\nIT 管理者が、このデバイスに関連付けられている設定、コーポレート アクセス、アプリ、データのほか、デバイスの位置情報を監視、管理できます。\n\n詳しくは、IT 管理者にお問い合わせください。"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"これは組織が所有するデバイスです。\n\nIT 管理者が、このデバイスに関連付けられている設定、コーポレート アクセス、アプリ、データのほか、デバイスの位置情報を監視、管理できます。\n\n詳しくは、IT 管理者にお問い合わせください。"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"組織によってこのデバイスに認証局がインストールされました。保護されたネットワーク トラフィックが監視、変更される場合があります。"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"組織によって、あなたの仕事用プロファイルに認証局がインストールされました。保護されたネットワーク トラフィックが監視、変更される場合があります。"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"このデバイスには認証局がインストールされています。保護されたネットワーク トラフィックが監視、変更される可能性があります。"</string>
@@ -727,18 +711,16 @@
<string name="notification_silence_title" msgid="8608090968400832335">"サイレント"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"デフォルト"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"バブル"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"着信音もバイブレーションも無効です"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"着信音もバイブレーションも無効になり会話セクションの下に表示されます"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"スマートフォンの設定を基に着信音またはバイブレーションが有効になります"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"スマートフォンの設定を基に着信音またはバイブレーションが有効になります。デフォルトでは <xliff:g id="APP_NAME">%1$s</xliff:g> からの会話がふきだしで表示されます。"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"スマートフォンの設定を基に着信音またはバイブレーションが有効になります。デフォルトでは <xliff:g id="APP_NAME">%1$s</xliff:g> からの会話がバブルとして表示されます。"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"このコンテンツのフローティング ショートカットで通知をお知らせします。"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"会話セクションの一番上にふきだしとして表示され、プロフィール写真がロック画面に表示されます"</string>
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"この通知を音またはバイブレーションで知らせるかどうかの自動判断"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"会話セクションの一番上にバブルとして表示され、プロフィール写真がロック画面に表示されます"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"設定"</string>
- <string name="notification_priority_title" msgid="2079708866333537093">"優先度"</string>
+ <string name="notification_priority_title" msgid="2079708866333537093">"優先"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> は会話機能に対応していません"</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"最近閉じたバブルはありません"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"最近表示されたバブルや閉じたバブルが、ここに表示されます"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"このアプリは画面上で他のアプリの上に重ねて表示されます。また、マイクとカメラを使用しています。"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"設定"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"この通知はシステムによってサイレントに設定されました。"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"この通知はシステムによって順位が上げられました。"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"この通知はシステムによって順位が下げられました。"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"間違いありませんか?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"フィードバックをお寄せいただきありがとうございます。"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> の通知管理は開いています"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> の通知管理は閉じています"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"このチャンネルからの通知を許可する"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"設定の順序を編集します。"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ページ <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ロック画面"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"展開"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"最小化"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"閉じる"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"設定"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"下にドラッグして閉じる"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"メニュー"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g>はピクチャー イン ピクチャーで表示中です"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g>でこの機能を使用しない場合は、タップして設定を開いて OFF にしてください。"</string>
- <string name="pip_play" msgid="333995977693142810">"再生"</string>
- <string name="pip_pause" msgid="1139598607050555845">"一時停止"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"次へスキップ"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"前へスキップ"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"高熱で電源が OFF になりました"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"お使いのスマートフォンは現在、正常に動作しています"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"お使いのスマートフォンは現在、正常に動作しています。\nタップして詳細を表示"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"スマートフォンが熱すぎたため電源が OFF になりました。現在は正常に動作しています。\n\nスマートフォンは以下の場合に熱くなる場合があります。\n • リソースを集中的に使用する機能やアプリ(ゲームアプリ、動画アプリ、ナビアプリなど)を使用\n • サイズの大きいファイルをダウンロードまたはアップロード\n • 高温の場所で使用"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"取り扱いに関する手順をご覧ください"</string>
<string name="high_temp_title" msgid="2218333576838496100">"スマートフォンの温度が上昇中"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"スマートフォンのクールダウン中は一部の機能が制限されます"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"スマートフォンのクールダウン中は一部の機能が制限されます。\nタップして詳細を表示"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"スマートフォンは自動的にクールダウンを行います。その間もスマートフォンを使用できますが、動作が遅くなる可能性があります。\n\nクールダウンが完了すると、通常どおり動作します。"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"取り扱いに関する手順をご覧ください"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"充電器を電源から外してください"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"このデバイスの充電中に問題が発生しました。電源アダプターを電源から外してください。ケーブルが熱くなっている可能性がありますのでご注意ください。"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"取り扱いに関する手順をご覧ください"</string>
@@ -1052,7 +1016,7 @@
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"会話セクションの一番上にバブルで表示"</string>
<string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"ロック画面にプロフィール写真を表示"</string>
<string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"他のアプリに重ねてフローティング バブルとして表示"</string>
- <string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"サイレント モードに割り込み"</string>
+ <string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"サイレント モードが ON でも表示"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"OK"</string>
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"拡大ウィンドウ"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"コントロールを並べ替えるには長押ししてドラッグします"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"すべてのコントロールを削除しました"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"変更が保存されていません"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"その他のアプリを表示"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"コントロールを読み込めませんでした。<xliff:g id="APP">%s</xliff:g> アプリで、アプリの設定が変更されていないことをご確認ください。"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"互換性のあるコントロールがありません"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"その他"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g>の変更を確認する"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"スワイプすると他の構造が表示されます"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"候補を読み込んでいます"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"このメディア セッションを閉じる"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"メディア"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"現在のセッションを非表示にします。"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"非表示"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"再開"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"無効: アプリをご確認ください"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"エラー。再試行しています…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"見つかりませんでした"</string>
diff --git a/packages/SystemUI/res/values-ja/strings_tv.xml b/packages/SystemUI/res/values-ja/strings_tv.xml
index 32f30d388320..1e7d05bb3713 100644
--- a/packages/SystemUI/res/values-ja/strings_tv.xml
+++ b/packages/SystemUI/res/values-ja/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ピクチャー イン ピクチャー"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(無題の番組)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP を閉じる"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"全画面表示"</string>
<string name="mic_active" msgid="5766614241012047024">"マイク: 有効"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s がマイクにアクセスしました"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 9862e9968a30..0dda483b9d16 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"შეეხეთ ისევ გასახსნელად"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"გასახსნელად გადაფურცლეთ ზემოთ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ხელახლა საცდელად გადაფურცლეთ ზემოთ"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ამ მოწყობილობას ფლობს <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"ტელეფონისთვის გადაფურცლეთ ხატულადან"</string>
<string name="voice_hint" msgid="7476017460191291417">"ხმოვანი დახმარებისთვის გადაფურცლეთ ხატულადან"</string>
<string name="camera_hint" msgid="4519495795000658637">"კამერისთვის გადაფურცლეთ ხატულადან"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"შესაძლოა პროფილზე ხორციელდებოდეს მონიტორინგი"</string>
<string name="vpn_footer" msgid="3457155078010607471">"შესაძლოა ქსელზე ხორციელდება მონიტორინგი"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ქსელზე შესაძლოა მონიტორინგი ხორციელდებოდეს"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია და მას ქსელის ტრაფიკის მონიტორინგი შეუძლია"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"ამ მოწყობილობას ფლობს <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> და მას ქსელის ტრაფიკის მონიტორინგი შეუძლია"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია და ის დაკავშირებულია <xliff:g id="VPN_APP">%1$s</xliff:g>-თან"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ამ მოწყობილობას ფლობს <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> და ის დაკავშირებულია <xliff:g id="VPN_APP">%2$s</xliff:g>-თან"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ამ მოწყობილობას ფლობს <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია და ის დაკავშირებულია VPN-ებთან"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ამ მოწყობილობას ფლობს <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> და ის დაკავშირებულია VPN-ებთან"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"თქვენს ორგანიზაციას სამსახურის პროფილში ქსელის ტრაფიკის მონიტორინგი შეუძლია"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-ს სამსახურის პროფილში ქსელის ტრაფიკის მონიტორინგი შეუძლია"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ქსელზე შესაძლოა ხორციელდებოდეს მონიტორინგი"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ეს მოწყობილობა დაკავშირებულია VPN-ებთან"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"თქვენი სამსახურის პროფილი დაკავშირებულია <xliff:g id="VPN_APP">%1$s</xliff:g>-თან"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"თქვენი პერსონალური პროფილი დაკავშირებულია <xliff:g id="VPN_APP">%1$s</xliff:g>-თან"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ეს მოწყობილობა დაკავშირებულია <xliff:g id="VPN_APP">%1$s</xliff:g>-თან"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"მოწყობილობის მართვა"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"პროფილის მონიტორინგი"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ქსელის მონიტორინგი"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN-ის გაუქმება"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN-ის გათიშვა"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"წესების ნახვა"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ეს მოწყობილობას ფლობს <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nთქვენს IT ადმინისტრატორს შეუძლია მოწყობილობასთან დაკავშირებული პარამეტრების, კორპორაციული წვდომის, აპებისა და მონაცემების (მათ შორის, თქვენი მოწყობილობის მდებარეობის ინფორმაციის) მონიტორინგი და მართვა.\n\nდამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს IT ადმინისტრატორს."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია.\n\nთქვენს IT ადმინისტრატორს შეუძლია მოწყობილობასთან დაკავშირებული პარამეტრების, კორპორაციული წვდომის, აპებისა და მონაცემების (მათ შორის, თქვენი მოწყობილობის მდებარეობის ინფორმაციის) მონიტორინგი და მართვა.\n\nდამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს IT ადმინისტრატორს."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"თქვენმა ორგანიზაციამ ამ მოწყობილობაზე სერტიფიცირების ორგანო დააინსტალირა. თქვენი ქსელის დაცული ტრაფიკი შეიძლება შეიცვალოს, ან მასზე მონიტორინგი განხორციელდეს."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"თქვენმა ორგანიზაციამ სამსახურის პროფილში სერტიფიცირების ორგანო დააინსტალირა. თქვენი ქსელის დაცული ტრაფიკი შეიძლება შეიცვალოს, ან მასზე მონიტორინგი განხორციელდეს."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ამ მოწყობილობაზე დაინსტალირებულია სერტიფიცირების ორგანო. თქვენი ქსელის დაცული ტრაფიკი შეიძლება შეიცვალოს, ან მასზე მონიტორინგი განხორციელდეს."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"ჩუმი"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ნაგულისხმევი"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"ბუშტი"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ავტომატური"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ხმისა და ვიბრაციის გარეშე"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ხმისა და ვიბრაციის გარეშე, ჩნდება მიმოწერების სექციის ქვედა ნაწილში"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით. მიმოწერები <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ბუშტიდან, ნაგულისხმევად."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"იპყრობს თქვენს ყურადღებას ამ კონტენტის მოლივლივე მალსახმობით."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"სისტემისთვის ისეთი უფლების მინიჭება, რომ მან განსაზღვროს, ამ შეტყობინებამ ხმოვანი სიგნალი უნდა აამოქმედოს თუ ვიბრაცია"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"გამოჩნდება მიმოწერების სექციის ზედა ნაწილში მოლივლივე ბუშტის სახით, აჩვენებს პროფილის სურათს ჩაკეტილ ეკრანზე"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"პარამეტრები"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"პრიორიტეტი"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ეს აპი თქვენს ეკრანზე ფარავს სხვა აპებს და იყენებს მიკროფონსა და კამერას."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"პარამეტრები"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"კარგი"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ეს შეტყობინება სისტემის მიერ არის ჩაჩუმებული."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ამ შეტყობინების მნიშვნელობა სისტემის მიერ არის აწეული."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ამ შეტყობინების მნიშვნელობა სისტემის მიერ არის დაწეული."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"სწორია ეს?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"გმადლობთ გამოხმაურებისთვის!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"კარგი"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"შეტყობინებების მართვა „<xliff:g id="APP_NAME">%1$s</xliff:g>“-ისთვის გახსნილია"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"შეტყობინებების მართვა „<xliff:g id="APP_NAME">%1$s</xliff:g>“-ისთვის დახურულია"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"ამ არხიდან შეტყობინებების დაშვება"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"პარამეტრების მიმდევრობის რედაქტირება."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"გვერდი <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>-დან"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ჩაკეტილი ეკრანი"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"გაშლა"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"ჩაკეცვა"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"დახურვა"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"პარამეტრები"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"დასახურად ჩავლებით ჩამოიტანეთ ქვემოთ"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"მენიუ"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> იყენებს რეჟიმს „ეკრანი ეკრანში“"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"თუ არ გსურთ, რომ <xliff:g id="NAME">%s</xliff:g> ამ ფუნქციას იყენებდეს, აქ შეხებით შეგიძლიათ გახსნათ პარამეტრები და გამორთოთ ის."</string>
- <string name="pip_play" msgid="333995977693142810">"დაკვრა"</string>
- <string name="pip_pause" msgid="1139598607050555845">"დაპაუზება"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"შემდეგზე გადასვლა"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"წინაზე გადასვლა"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"ტელეფონი გამოირთო გაცხელების გამო"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"თქვენი ტელეფონი ახლა ჩვეულებრივად მუშაობს"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"თქვენი ტელეფონი უკვე ნორმალურად მუშაობს.\nშეეხეთ დამატებითი ინფორმაციის მისაღებად"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"თქვენი ტელეფონი გამოირთო გასაგრილებლად, რადგან ის მეტისმეტად გაცხელდა. ახლა ის ჩვეულებრივად მუშაობს.\n\nტელეფონის გაცხელების მიზეზებია:\n • რესურსტევადი აპების გამოყენება (მაგ. სათამაშო, ვიდეო ან ნავიგაციის აპების)\n • დიდი ფაილების ჩამოტვირთვა ან ატვირთვა\n • ტელეფონის გამოყენება მაღალი ტემპერატურისას"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"მისაღები ზომების გაცნობა"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ტელეფონი ცხელდება"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ზოგიერთი ფუნქცია შეზღუდული იქნება, სანამ ტელეფონი გაგრილდება"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ზოგიერთი ფუნქცია შეზღუდული იქნება, სანამ ტელეფონი გაგრილდება.\nშეეხეთ დამატებითი ინფორმაციის მისაღებად"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"თქვენი ტელეფონი გაგრილებას ავტომატურად შეეცდება. შეგიძლიათ გააგრძელოთ მისით სარგებლობა, თუმცა ტელეფონმა შეიძლება უფრო ნელა იმუშაოს.\n\nგაგრილების შემდგომ ის ჩვეულებრივად იმუშავებს."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"მისაღები ზომების გაცნობა"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"გამოაერთეთ დამტენი"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"ამ მოწყობილობის დატენა ვერ ხერხდება პრობლემის გამო. გამოაერთეთ ელკვების ადაპტერი და გამოიჩინეთ სიფრთხილე, რადგან კაბელი შეიძლებოდა გაცხელებულიყო."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"მისაღები ზომების გაცნობა"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"მართვის საშუალებების გადაწყობა შეგიძლიათ მათი ჩავლებით გადატანით"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"მართვის ყველა საშუალება ამოიშალა"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ცვლილებები არ შენახულა"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"სხვა აპების ნახვა"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"მართვის საშუალებების ჩატვირთვა ვერ მოხერხდა. შეამოწმეთ <xliff:g id="APP">%s</xliff:g> აპი, რათა დარწმუნდეთ, რომ აპის პარამეტრები არ შეცვლილა."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"მართვის თავსებადი საშუალებები მიუწვდომელია"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"სხვა"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"დაადასტურეთ ცვლილება <xliff:g id="DEVICE">%s</xliff:g>-ისთვის"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"გადაფურცლეთ მეტის სანახავად"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"მიმდინარეობს რეკომენდაციების ჩატვირთვა"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ამ მედია სესიის დახურვა"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"მედია"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"დაიმალოს მიმდინარე სესია"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"დამალვა"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"გაგრძელება"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"პარამეტრები"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"არააქტიურია, გადაამოწმეთ აპი"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"შეცდომა, ხელახალი მცდელობა…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ვერ მოიძებნა"</string>
diff --git a/packages/SystemUI/res/values-ka/strings_tv.xml b/packages/SystemUI/res/values-ka/strings_tv.xml
index b0ed30a4f218..476658d816f2 100644
--- a/packages/SystemUI/res/values-ka/strings_tv.xml
+++ b/packages/SystemUI/res/values-ka/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ეკრანი ეკრანში"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(პროგრამის სათაურის გარეშე)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP-ის დახურვა"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"სრულ ეკრანზე"</string>
<string name="mic_active" msgid="5766614241012047024">"მიკროფონი აქტიურია"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s-მა გამოიყენა თქვენი მიკროფონი"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 369c10e0f881..27bc3ab4aab0 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ашу үшін қайта түртіңіз"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ашу үшін жоғары қарай сырғытыңыз."</string>
<string name="keyguard_retry" msgid="886802522584053523">"Әрекетті қайталау үшін жоғары сырғытыңыз."</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Бұл құрылғы ұйымыңызға тиесілі."</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Бұл құрылғы <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ұйымына тиесілі."</string>
<string name="phone_hint" msgid="6682125338461375925">"Телефонды ашу үшін белгішеден әрі қарай сырғытыңыз"</string>
<string name="voice_hint" msgid="7476017460191291417">"Дауыс көмекшісін ашу үшін белгішеден әрі қарай сырғытыңыз"</string>
<string name="camera_hint" msgid="4519495795000658637">"Камераны ашу үшін белгішеден әрі қарай сырғытыңыз"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Профиль бақылануы мүмкін"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Желі бақылауда болуы мүмкін"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Желі бақылауда болуы мүмкін"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ұйымыңыз осы құрылғыны басқарады және желі трафигін бақылауы мүмкін."</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> осы құрылғыны басқарады және желі трафигін бақылауы мүмкін."</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Бұл құрылғы ұйымыңызға тиесілі және <xliff:g id="VPN_APP">%1$s</xliff:g> қолданбасына қосылған."</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Бұл құрылғы <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ұйымына тиесілі және <xliff:g id="VPN_APP">%2$s</xliff:g> қолданбасына қосылған."</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Бұл құрылғы ұйымыңызға тиесілі."</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Бұл құрылғы <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ұйымына тиесілі."</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Бұл құрылғы ұйымыңызға тиесілі және VPN-дерге қосылған."</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Бұл құрылғы <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ұйымына тиесілі және VPN-дерге қосылған."</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Ұйымыңыз жұмыс профиліңіздегі желі трафигін бақылауы мүмкін"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> жұмыс профиліңіздегі желі трафигін бақылауы мүмкін"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Желі бақылануы мүмкін"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Бұл құрылғы VPN-дерге қосылған."</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Жұмыс профиліңіз <xliff:g id="VPN_APP">%1$s</xliff:g> қолданбасына қосылған."</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Жеке профиліңіз <xliff:g id="VPN_APP">%1$s</xliff:g> қолданбасына қосылған."</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Бұл құрылғы <xliff:g id="VPN_APP">%1$s</xliff:g> қолданбасына қосылған."</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Құрылғыны басқару"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Профильді бақылау"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Желіні бақылау"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN функциясын өшіру"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN желісін ажырату"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Саясаттарды көру"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Бұл құрылғы <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ұйымына тиесілі.\n\nӘкімші параметрлерді, корпоративтік кіру құқығын, қолданбаларды, құрылғыңызбен байланысты деректерді және құрылғыңыздың орналасқан жері туралы ақпаратты бақылай және басқара алады.\n\nҚосымша ақпарат алу үшін әкімшіге хабарласыңыз."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Бұл құрылғы ұйымыңызға тиесілі.\n\nӘкімші параметрлерді, корпоративтік кіру құқығын, қолданбаларды, құрылғыңызбен байланысты деректерді және құрылғыңыздың орналасқан жері туралы ақпаратты бақылай және басқара алады.\n\nҚосымша ақпарат алу үшін әкімшіге хабарласыңыз."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Ұйымыңыз осы құрылғыда сертификат орнатқан. Қорғалған желі трафигіңіз бақылануы немесе өзгертілуі мүмкін."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ұйымыңыз жұмыс профиліңізде сертификат орнатқан. Қорғалған желі трафигіңіз бақылануы немесе өзгертілуі мүмкін."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Осы құрылғыда сертификат орнатылған. Қорғалған желі трафигіңіз бақылануы немесе өзгертілуі мүмкін."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Дыбыссыз"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Әдепкі"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Көпіршік"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматты"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Дыбыс не діріл қолданылмайды"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дыбыс не діріл қолданылмайды, төменде әңгімелер бөлімінде шығады"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефон параметрлеріне байланысты шылдырлауы не дірілдеуі мүмкін"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефон параметрлеріне байланысты шылдырлауы не дірілдеуі мүмкін. <xliff:g id="APP_NAME">%1$s</xliff:g> чаттары әдепкісінше қалқымалы етіп көрсетіледі."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Осы мазмұнға бекітілген қалқымалы таңбашамен назарыңызды өзіне тартады."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Хабарландыру дыбысының немесе дірілдің қосылуын жүйе анықтайтын болады"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Әңгімелер бөлімінің жоғарғы жағында тұрады, қалқыма хабар түрінде шығады, құлыптаулы экранда профиль суретін көрсетеді"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Параметрлер"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Маңыздылығы"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Бұл қолданба экранда басқа қолданбалардың үстінен көрсетіліп тұр және ол микрофон мен камераны пайдалануда."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Параметрлер"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Жарайды"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Хабарландырудың дыбысы жүйе арқылы өшірілген."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Хабарландырудың күйі жүйе арқылы көтерілген."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Хабарландырудың күйі жүйе арқылы түсірілген."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Барлығы дұрыс па?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Пікіріңіз үшін рақмет!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Жарайды"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> хабарландыруларын басқару элементтері ашылды"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> хабарландыруларын басқару элементтері жабылды"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Осы арнадан келетін хабарландыруларға рұқсат беру"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Параметрлер тәртібін өзгерту."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ішінен <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Құлыпталған экран"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Жаю"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Кішірейту"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Жабу"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Параметрлер"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Жабу үшін төмен қарай сүйреңіз"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Mәзір"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> \"суреттегі сурет\" режимінде"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> деген пайдаланушының бұл мүмкіндікті пайдалануын қаламасаңыз, параметрлерді түртіп ашыңыз да, оларды өшіріңіз."</string>
- <string name="pip_play" msgid="333995977693142810">"Ойнату"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Кідірту"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Келесіге өту"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Алдыңғысына оралу"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Телефон қызып кеткендіктен өшірілді"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Телефоныңыз қазір қалыпты жұмыс істеп тұр"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Телефоныңыз қалыпты жұмыс істеп тұр.\nТолығырақ ақпарат алу үшін түртіңіз."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Телефоныңыз қатты қызып кеткендіктен өшірілді. Телефоныңыз қазір қалыпты жұмыс істеп тұр.\n\nТелефоныңыз мына жағдайларда ыстық болуы мүмкін:\n • Ресурстар талап ететін қолданбаларды пайдалану (ойын, бейне немесе навигация қолданбалары)\n • Үлкен көлемді файлдарды жүктеу немесе жүктеп салу\n • Телефонды жоғары температурада пайдалану"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Пайдалану нұсқаулығын қараңыз"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Телефон қызуда"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Телефон толық суығанға дейін, кейбір функциялардың жұмысы шектеледі"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Телефон толық суығанға дейін, кейбір функциялардың жұмысы шектеледі.\nТолығырақ ақпарат үшін түртіңіз."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефон автоматты түрде суи бастайды. Оны пайдалана бере аласыз, бірақ ол баяуырақ жұмыс істеуі мүмкін.\n\nТелефон суығаннан кейін, оның жұмысы қалпына келеді."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Пайдалану нұсқаулығын қараңыз"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Зарядтағышты ажыратыңыз"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Құрылғыны зарядтау кезінде ақау шықты. Қуат адаптерін ажыратыңыз. Кабель ыстық болуы мүмкін, абай болыңыз."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Пайдалану нұсқаулығын қараңыз"</string>
@@ -1057,9 +1021,9 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Параметрлер"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ұлғайту терезесі"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ұлғайту терезесінің басқару элементтері"</string>
- <string name="quick_controls_title" msgid="6839108006171302273">"Құрылғы басқару виджеттері"</string>
+ <string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Жалғанған құрылғылар үшін басқару виджеттерін қосу"</string>
- <string name="quick_controls_setup_title" msgid="8901436655997849822">"Құрылғы басқару виджеттерін реттеу"</string>
+ <string name="quick_controls_setup_title" msgid="8901436655997849822">"Құрылғыны басқару элементтерін реттеу"</string>
<string name="quick_controls_setup_subtitle" msgid="1681506617879773824">"Басқару элементтерін шығару үшін қуат түймесін басып тұрыңыз."</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Басқару элементтері енгізілетін қолданбаны таңдаңыз"</string>
<plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
@@ -1078,10 +1042,11 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Басқару элементтерінің ретін өзгерту үшін оларды басып тұрып сүйреңіз."</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Барлық басқару элементтері өшірілді."</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Өзгерістер сақталмады."</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Басқа қолданбаларды көру"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Басқару элементтері жүктелмеді. Қолданба параметрлерінің өзгермегенін тексеру үшін <xliff:g id="APP">%s</xliff:g> қолданбасын қараңыз."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Үйлесімді басқару элементтері қолжетімді емес."</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Басқа"</string>
- <string name="controls_dialog_title" msgid="2343565267424406202">"Құрылғы басқару виджеттеріне қосу"</string>
+ <string name="controls_dialog_title" msgid="2343565267424406202">"Құрылғы басқару элементтеріне қосу"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"Енгізу"</string>
<string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ұсынған"</string>
<string name="controls_dialog_confirmation" msgid="586517302736263447">"Басқару элементтері жаңартылды"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> құрылғысындағы өзгерісті растау"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Толығырақ ақпарат алу үшін сырғытыңыз."</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Жүктеуге қатысты ұсыныстар"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Мультимедиа сеансын жабу"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Мультимедиа"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Ағымдағы сеансты жасыру"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Жасыру"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Жалғастыру"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Өшірулі. Қолданба тексеріңіз."</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Қате, әрекет қайталануда…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Табылмады"</string>
diff --git a/packages/SystemUI/res/values-kk/strings_tv.xml b/packages/SystemUI/res/values-kk/strings_tv.xml
index 323c5e6c0bc7..d4b3c73f8308 100644
--- a/packages/SystemUI/res/values-kk/strings_tv.xml
+++ b/packages/SystemUI/res/values-kk/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Суреттегі сурет"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Атаусыз бағдарлама)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP жабу"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Толық экран"</string>
<string name="mic_active" msgid="5766614241012047024">"Микрофон қосулы"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s микрофоныңызды пайдаланды."</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 2e525bd3cbbc..36d5aa8ab45b 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ប៉ះ​ម្ដង​ទៀត ដើម្បី​បើក"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"អូសឡើងលើ​ដើម្បីបើក"</string>
<string name="keyguard_retry" msgid="886802522584053523">"អូសឡើងលើ ដើម្បី​ព្យាយាម​ម្ដងទៀត"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ឧបករណ៍​នេះគឺជា​កម្មសិទ្ធិរបស់​ស្ថាប័ន​អ្នក"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ឧបករណ៍នេះ​គឺជា​កម្មសិទ្ធិ​របស់ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"អូសចេញពីរូបតំណាងដើម្បីប្រើទូរស័ព្ទ"</string>
<string name="voice_hint" msgid="7476017460191291417">"អូសចេញពីរូបតំណាងដើម្បីប្រើជំនួយសំឡេង"</string>
<string name="camera_hint" msgid="4519495795000658637">"អូសចេញពីរូបតំណាងដើម្បីប្រើកាមេរ៉ា"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ប្រវត្តិរូបអាចត្រូវបានតាមដាន"</string>
<string name="vpn_footer" msgid="3457155078010607471">"បណ្ដាញ​អាច​ត្រូវ​បាន​ត្រួតពិនិត្យ"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"បណ្ដាញអាចត្រូវបានត្រួតពិនិត្យ"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ស្ថាប័ន​របស់អ្នក​ជាម្ចាស់​ឧបករណ៍​នេះ ហើយ​អាចនឹង​តាមដាន​ចរាចរណ៍បណ្តាញ"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ជាម្ចាស់​ឧបករណ៍​នេះ ហើយ​អាចនឹង​តាមដាន​ចរាចរណ៍​បណ្តាញ"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ឧបករណ៍​នេះគឺជា​កម្មសិទ្ធិរបស់​ស្ថាប័នអ្នក និងត្រូវបានភ្ជាប់ទៅ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ឧបករណ៍​នេះគឺជា​កម្មសិទ្ធិរបស់ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> និងត្រូវបានភ្ជាប់ទៅ <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ឧបករណ៍​នេះគឺជា​កម្មសិទ្ធិរបស់​ស្ថាប័ន​អ្នក"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ឧបករណ៍នេះ​គឺជា​កម្មសិទ្ធិ​របស់ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ឧបករណ៍​នេះគឺជា​កម្មសិទ្ធិរបស់​ស្ថាប័នអ្នក និងត្រូវបានភ្ជាប់ទៅ VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ឧបករណ៍​នេះគឺជា​កម្មសិទ្ធិរបស់ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> និងត្រូវបានភ្ជាប់ទៅ VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"ស្ថាប័ន​របស់អ្នក​អាចនឹង​តាមដាន​ចរាចរណ៍​បណ្តាញ​នៅក្នុង​កម្រងព័ត៌មាន​ការងារ​របស់អ្នក"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> អាចនឹង​តាមដាន​ចរាចរណ៍​បណ្តាញ​នៅក្នុង​កម្រងព័ត៌មាន​ការងារ​របស់​អ្នក"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"បណ្ដាញ​អាច​ត្រូវ​តាមដាន"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ឧបករណ៍នេះ​ត្រូវបានភ្ជាប់ទៅ VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"កម្រងព័ត៌មានការងារ​របស់អ្នក​ត្រូវបានភ្ជាប់ទៅ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"កម្រងព័ត៌មាន​ផ្ទាល់ខ្លួន​របស់អ្នក​ត្រូវបានភ្ជាប់ទៅ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ឧបករណ៍នេះ​ត្រូវបានភ្ជាប់ទៅ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ការ​គ្រប់គ្រង​ឧបករណ៍"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"តាមដានប្រវត្ថិរូប"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ការ​ត្រួតពិនិត្យ​បណ្ដាញ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"បិទ VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"ផ្ដាច់ VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"មើល​គោលការណ៍"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ឧបករណ៍នេះ​ជាគឺកម្មសិទ្ធិ​របស់ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>។\n\nអ្នកគ្រប់គ្រង​ផ្នែកព័ត៌មានវិទ្យា​របស់អ្នក​អាចតាមដាន និងគ្រប់គ្រង​ការកំណត់ ការចូលប្រើជាលក្ខណៈក្រុមហ៊ុន កម្មវិធី ទិន្នន័យដែលពាក់ព័ន្ធ​នឹង​ឧបករណ៍​របស់អ្នក និងព័ត៌មាន​ទីតាំង​របស់​ឧបករណ៍​អ្នក។\n\nសម្រាប់​ព័ត៌មាន​បន្ថែម សូម​ទាក់ទង​អ្នកគ្រប់គ្រង​ផ្នែកព័ត៌មានវិទ្យា​របស់អ្នក។"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ឧបករណ៍នេះ​គឺជាកម្មសិទ្ធិ​របស់ស្ថាប័នអ្នក។\n\nអ្នកគ្រប់គ្រង​ផ្នែកព័ត៌មានវិទ្យា​របស់អ្នក​អាចតាមដាន និងគ្រប់គ្រង​ការកំណត់ ការចូលប្រើជាលក្ខណៈក្រុមហ៊ុន កម្មវិធី ទិន្នន័យដែលពាក់ព័ន្ធ​នឹង​ឧបករណ៍​របស់អ្នក និងព័ត៌មាន​ទីតាំង​របស់ឧបករណ៍​អ្នក។\n\nសម្រាប់​ព័ត៌មាន​បន្ថែម សូម​ទាក់ទង​អ្នកគ្រប់គ្រង​ផ្នែកព័ត៌មានវិទ្យា​របស់អ្នក។"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ស្ថាប័ន​របស់អ្នក​បានដំឡើង​អាជ្ញាធរវិញ្ញាបនបត្រនៅលើ​ឧបករណ៍​នេះ។ ចរាចរណ៍​បណ្តាញដែលមាន​សុវត្ថិភាព​របស់អ្នក​អាច​ត្រូវបាន​តាមដាន ឬ​កែសម្រួល។"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ស្ថាប័នរបស់អ្នក​បានដំឡើង​អាជ្ញាធរវិញ្ញាបនបត្រ​នៅក្នុង​កម្រងព័ត៌មាន​ការងារ។ ចរាចរណ៍​បណ្តាញដែលមាន​សុវត្ថិភាព​របស់អ្នក​អាច​ត្រូវបាន​តាមដាន ឬ​កែសម្រួល។"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"បាន​ដំឡើង​អាជ្ញាធរវិញ្ញាបនបត្រ​នៅលើ​ឧបករណ៍​នេះ។ ចរាចរណ៍​បណ្តាញដែលមានសុវត្ថិភាព​របស់អ្នក​អាច​ត្រូវបាន​តាមដាន ឬ​កែសម្រួល។"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"ស្ងាត់"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"លំនាំដើម"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"ពពុះ"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ស្វ័យប្រវត្តិ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"គ្មាន​សំឡេង ឬការញ័រទេ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"គ្មានសំឡេង ឬការញ័រ និងការបង្ហាញ​កម្រិតទាបជាង​នេះនៅក្នុង​ផ្នែកសន្ទនាទេ"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"អាចរោទ៍ ឬញ័រ ដោយផ្អែកលើ​ការកំណត់​ទូរសព្ទ"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"អាចរោទ៍ ឬញ័រ ដោយផ្អែកលើ​ការកំណត់​ទូរសព្ទ។ ការសន្ទនា​ពី​ពពុះ <xliff:g id="APP_NAME">%1$s</xliff:g> តាម​លំនាំដើម​។"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ធ្វើឱ្យអ្នក​ចាប់អារម្មណ៍​ដោយប្រើ​ផ្លូវកាត់​អណ្ដែត​សម្រាប់ខ្លឹមសារនេះ។"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ឱ្យប្រព័ន្ធកំណត់ថាតើ​ការជូនដំណឹងនេះ​គួរតែបន្លឺសំឡេង ឬញ័រ"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"បង្ហាញនៅខាងលើ​ផ្នែកសន្ទនា បង្ហាញជា​ពពុះអណ្ដែត បង្ហាញ​រូបភាព​កម្រងព័ត៌មាន​នៅលើ​អេក្រង់ចាក់សោ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ការកំណត់"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"អាទិភាព"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"កម្មវិធីនេះ​កំពុងបង្ហាញ​ពីលើកម្មវិធី​ផ្សេងទៀត​នៅលើអេក្រង់​របស់អ្នក និងកំពុងប្រើ​មីក្រូហ្វូន ក៏​ដូចជា​​កាមេរ៉ា។"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ការកំណត់"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"យល់ព្រម"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ការជូនដំណឹងនេះ​ត្រូវបានបិទសំឡេង​ដោយប្រព័ន្ធ។"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ការជូនដំណឹងនេះ​ត្រូវបាន​ដំឡើងតំណែង​ដោយប្រព័ន្ធ។"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ការជូនដំណឹងនេះ​ត្រូវបានបន្ទាបតំណែងដោយប្រព័ន្ធ។"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"តើវា​ត្រឹមត្រូវទេ?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"សូមអរគុណចំពោះ​មតិកែលម្អ​របស់អ្នក!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"យល់ព្រម"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"ការគ្រប់គ្រងការជូនដំណឹងសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> បានបើក"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"ការគ្រប់គ្រងការជូនដំណឹងសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> បានបិទ"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"អនុញ្ញាតឲ្យមានការជូនដំណឹងពីប៉ុស្តិ៍នេះ"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"កែលំដាប់ការកំណត់"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ទំព័រ <xliff:g id="ID_1">%1$d</xliff:g> នៃ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"អេក្រង់​ចាក់សោ"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"ពង្រីក"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"បង្រួម"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"បិទ"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ការកំណត់"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"អូស​ចុះក្រោម​ដើម្បី​បដិសេធ"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"ម៉ឺនុយ"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> ស្ថិតក្នុងមុខងាររូបក្នុងរូប"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"ប្រសិនបើ​អ្នក​មិន​ចង់​ឲ្យ <xliff:g id="NAME">%s</xliff:g> ប្រើ​មុខងារ​នេះ​ សូមចុច​​បើក​ការកំណត់ រួច​បិទ​វា។"</string>
- <string name="pip_play" msgid="333995977693142810">"លេង"</string>
- <string name="pip_pause" msgid="1139598607050555845">"ផ្អាក"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"រំលងទៅបន្ទាប់"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"រំលងទៅក្រោយ"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"ទូរសព្ទ​បាន​បិទដោយសារ​វា​ឡើងកម្តៅ"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"ឥឡូវនេះ​ទូរសព្ទ​របស់អ្នក​កំពុង​ដំណើរការ​ធម្មតា"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"ឥឡូវនេះ ទូរសព្ទ​របស់អ្នក​កំពុងដំណើរការ​ជាធម្មតា។\nសូមចុច​ដើម្បីទទួលបាន​ព័ត៌មានបន្ថែម"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"ទូរសព្ទ​របស់អ្នក​ក្តៅពេក ដូច្នេះ​វាបាន​បិទ​ដើម្បី​បន្ថយ​កម្តៅ។ ឥឡូវនេះ ​ទូរសព្ទ​របស់អ្នក​កំពុង​ដំណើរការ​ធម្មតា។\n\nទូរសព្ទ​របស់អ្នក​អាចនឹង​ឡើង​កម្តៅ​ខ្លាំងជ្រុល ប្រសិន​បើអ្នក៖\n • ប្រើប្រាស់​កម្មវិធី​ដែល​ប្រើប្រាស់ទិន្នន័យច្រើនក្នុងរយៈពេលខ្លី (ដូចជាហ្គេម វីដេអូ ឬកម្មវិធីរុករក)\n • ទាញយក ឬ​បង្ហោះ​ឯកសារដែលមានទំហំធំ\n • ប្រើប្រាស់​ទូរសព្ទ​របស់អ្នក​នៅកន្លែង​មានសីតុណ្ហភាព​ខ្ពស់"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"មើលជំហាន​ថែទាំ"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ទូរសព្ទ​នេះ​កំពុង​កើន​កម្តៅ"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"មុខងារ​មួយ​ចំនួន​នឹង​មិន​អាច​ប្រើ​បាន​ពេញលេញ​នោះ​ទេ ខណៈពេល​ដែល​ទូរសព្ទ​កំពុង​បញ្ចុះ​កម្តៅ"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"មុខងារ​មួយចំនួន​នឹងមិនអាច​ប្រើបានពេញលេញ​នោះទេ ខណៈពេល​ដែលទូរសព្ទ​កំពុងបញ្ចុះកម្ដៅ។\nសូមចុច​ដើម្បីទទួលបាន​ព័ត៌មានបន្ថែម"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ទូរសព្ទ​របស់អ្នក​នឹង​ព្យាយាម​បញ្ចុះ​កម្តៅ​ដោយ​ស្វ័យប្រវត្តិ។ អ្នក​នៅតែ​អាច​ប្រើ​ទូរសព្ទ​របស់អ្នក​បាន​ដដែល​ ប៉ុន្តែ​វា​នឹង​ដំណើរ​ការ​យឺត​ជាង​មុន។\n\nបន្ទាប់​ពី​ទូរសព្ទ​របស់អ្នក​ត្រជាក់​ជាង​មុន​ហើយ វា​នឹង​ដំណើរការ​ដូច​ធម្មតា។"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"មើលជំហាន​ថែទាំ"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ផ្ដាច់ឆ្នាំងសាក"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"មាន​បញ្ហា​ក្នុងការសាកថ្ម​ឧបករណ៍​នេះ។ សូមផ្ដាច់​ឆ្នាំងសាក ហើយ​ប្រុងប្រយ័ត្ន ដោយសារ​ខ្សែ​អាចមាន​កម្ដៅ​ក្ដៅ។"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"មើលជំហាន​ថែទាំ"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"ចុច​ឱ្យ​ជាប់ រួចអូស​ដើម្បី​រៀបចំ​ផ្ទាំងគ្រប់គ្រង​ឡើងវិញ"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"បាន​ដកផ្ទាំងគ្រប់គ្រងទាំងអស់ហើយ"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"មិនបាន​រក្សាទុក​ការផ្លាស់ប្ដូរទេ"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"មើល​កម្មវិធី​ផ្សេងទៀត"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"មិនអាចផ្ទុក​ការគ្រប់គ្រង​បានទេ។ សូមពិនិត្យមើល​កម្មវិធី <xliff:g id="APP">%s</xliff:g> ដើម្បីធ្វើឱ្យប្រាកដថា​ការកំណត់កម្មវិធី​មិនបានផ្លាស់ប្ដូរ។"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"មិនអាចប្រើ​ការគ្រប់គ្រង​ដែលត្រូវគ្នា​បានទេ"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ផ្សេងៗ"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"បញ្ជាក់​ការផ្លាស់ប្ដូរ​សម្រាប់ <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"អូសដើម្បី​មើលច្រើនទៀត"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"កំពុងផ្ទុក​ការណែនាំ"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"បិទវគ្គ​មេឌៀ​នេះ"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"មេឌៀ"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"លាក់វគ្គ​បច្ចុប្បន្ន។"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"លាក់"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"បន្ត"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ការកំណត់"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"អសកម្ម ពិនិត្យមើល​កម្មវិធី"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"បញ្ហា កំពុងព្យាយាម​ម្ដងទៀត…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"រកមិន​ឃើញទេ"</string>
diff --git a/packages/SystemUI/res/values-km/strings_tv.xml b/packages/SystemUI/res/values-km/strings_tv.xml
index f905f49f695b..685a5e482e89 100644
--- a/packages/SystemUI/res/values-km/strings_tv.xml
+++ b/packages/SystemUI/res/values-km/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"រូបក្នុងរូប"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(កម្មវិធី​គ្មានចំណងជើង)"</string>
- <string name="pip_close" msgid="5775212044472849930">"បិទ PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"ពេញអេក្រង់"</string>
<string name="mic_active" msgid="5766614241012047024">"មីក្រូហ្វូន​កំពុង​ដំណើរការ"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s បានចូលប្រើ​មីក្រូហ្វូន​របស់អ្នក"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index e320ff5f8760..2034472d18cd 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ತೆರೆಯಲು ಮತ್ತೆ ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ತೆರೆಯಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ಈ ಸಾಧನವು ನಿಮ್ಮ ಸಂಸ್ಥೆಗೆ ಸೇರಿದೆ"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ಈ ಸಾಧನವು <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ಗೆ ಸೇರಿದೆ"</string>
<string name="phone_hint" msgid="6682125338461375925">"ಫೋನ್‌ಗಾಗಿ ಐಕಾನ್‌ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="voice_hint" msgid="7476017460191291417">"ಧ್ವನಿ ಸಹಾಯಕ್ಕಾಗಿ ಐಕಾನ್‌ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="camera_hint" msgid="4519495795000658637">"ಕ್ಯಾಮರಾಗಾಗಿ ಐಕಾನ್‌ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ಪ್ರೊಫೈಲ್ ಅನ್ನು ಪರಿವೀಕ್ಷಿಸಬಹುದಾಗಿದೆ"</string>
<string name="vpn_footer" msgid="3457155078010607471">"ನೆಟ್‌ವರ್ಕ್ ಅನ್ನು ವೀಕ್ಷಿಸಬಹುದಾಗಿ"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ನೆಟ್‌ವರ್ಕ್ ಅನ್ನು ವೀಕ್ಷಿಸಬಹುದಾಗಿರುತ್ತದೆ"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಸಾಧನದ ಮಾಲೀಕತ್ವವನ್ನು ಹೊಂದಿದೆ ಮತ್ತು ಅದು ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್‌ನ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ಈ ಸಾಧನದ ಮಾಲೀಕತ್ವವನ್ನು ಹೊಂದಿದೆ ಮತ್ತು ಅದು ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್‌ನ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ಈ ಸಾಧನವು ನಿಮ್ಮ ಸಂಸ್ಥೆಗೆ ಸೇರಿದೆ ಮತ್ತು <xliff:g id="VPN_APP">%1$s</xliff:g> ಗೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ಈ ಸಾಧನವು <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ಗೆ ಸೇರಿದೆ ಮತ್ತು <xliff:g id="VPN_APP">%2$s</xliff:g> ಗೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ಈ ಸಾಧನವು ನಿಮ್ಮ ಸಂಸ್ಥೆಗೆ ಸೇರಿದೆ"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ಈ ಸಾಧನವು <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ಗೆ ಸೇರಿದೆ"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ಈ ಸಾಧನವು ನಿಮ್ಮ ಸಂಸ್ಥೆಗೆ ಸೇರಿದೆ ಮತ್ತು VPN ಗಳಿಗೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ಈ ಸಾಧನವು <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ಗೆ ಸೇರಿದೆ ಮತ್ತು VPN ಗಳಿಗೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ನ ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ನಲ್ಲಿ ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ನೆಟ್‌ವರ್ಕ್‌ನ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದಾಗಿದೆ"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ಈ ಸಾಧನವು VPN ಗಳಿಗೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"ನಿಮ್ಮ ಉದ್ಯೋಗದ ಪ್ರೊಫೈಲ್ <xliff:g id="VPN_APP">%1$s</xliff:g> ಗೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಪ್ರೊಫೈಲ್ <xliff:g id="VPN_APP">%1$s</xliff:g> ಗೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ಈ ಸಾಧನವು <xliff:g id="VPN_APP">%1$s</xliff:g> ಗೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ಸಾಧನ ನಿರ್ವಹಣೆ"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"ಪ್ರೊಫೈಲ್ ಮೇಲ್ವಿಚಾರಣೆ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ನೆಟ್‌ವರ್ಕ್‌ ಪರಿವೀಕ್ಷಣೆ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN ಸಂಪರ್ಕಕಡಿತಗೊಳಿಸಿ"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"ಕಾರ್ಯನೀತಿಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ಈ ಸಾಧನವು <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ಗೆ ಸೇರಿದೆ.\n\nಸೆಟ್ಟಿಂಗ್‌ಗಳು, ಕಾರ್ಪೊರೇಟ್ ಪ್ರವೇಶ, ಆ್ಯಪ್‌ಗಳು, ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಡೇಟಾ ಮತ್ತು ನಿಮ್ಮ ಸಾಧನದ ಸ್ಥಳದ ಮಾಹಿತಿಯನ್ನು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ಈ ಸಾಧನವು ನಿಮ್ಮ ಸಂಸ್ಥೆಗೆ ಸೇರಿದೆ.\n\nಸೆಟ್ಟಿಂಗ್‌ಗಳು, ಕಾರ್ಪೊರೇಟ್ ಪ್ರವೇಶ, ಆ್ಯಪ್‌ಗಳು, ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಡೇಟಾ ಮತ್ತು ನಿಮ್ಮ ಸಾಧನದ ಸ್ಥಳದ ಮಾಹಿತಿಯನ್ನು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಸಾಧನದಲ್ಲಿ ಪ್ರಮಾಣಪತ್ರ ಅಂಗೀಕಾರವನ್ನು ಸ್ಥಾಪಿಸಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷಿತ ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಅಥವಾ ಮಾರ್ಪಡಿಸಬಹುದು."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ನಲ್ಲಿ ಪ್ರಮಾಣಪತ್ರ ಅಂಗೀಕಾರವನ್ನು ಸ್ಥಾಪಿಸಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷಿತ ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಅಥವಾ ಮಾರ್ಪಡಿಸಬಹುದು."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ಈ ಸಾಧನದಲ್ಲಿ ಪ್ರಮಾಣಪತ್ರ ಅಂಗೀಕಾರವನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷಿತ ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಅಥವಾ ಮಾರ್ಪಡಿಸಬಹುದು."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"ನಿಶ್ಶಬ್ದ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ಡೀಫಾಲ್ಟ್"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"ಬಬಲ್"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ಸ್ವಯಂಚಾಲಿತ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ಯಾವುದೇ ಧ್ವನಿ ಅಥವಾ ವೈಬ್ರೇಷನ್‌ ಆಗುವುದಿಲ್ಲ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ಯಾವುದೇ ಧ್ವನಿ ಅಥವಾ ವೈಬ್ರೇಷನ್‌ ಆಗುವುದಿಲ್ಲ, ಸಂಭಾಷಣೆ ವಿಭಾಗದ ಕೆಳಭಾಗದಲ್ಲಿ ಗೋಚರಿಸುತ್ತದೆ"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಆಧರಿಸಿ ಫೋನ್ ರಿಂಗ್ ಅಥವಾ ವೈಬ್ರೇಟ್ ಆಗುತ್ತದೆ"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಆಧರಿಸಿ ಫೋನ್ ರಿಂಗ್ ಅಥವಾ ವೈಬ್ರೇಟ್ ಆಗುತ್ತದೆ. ಡಿಫಾಲ್ಟ್ ಆಗಿ, <xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಬಬಲ್ ಸಂಭಾಷಣೆಗಳು."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ಈ ವಿಷಯಕ್ಕೆ ಲಿಂಕ್ ಮಾಡಿ ಕೊಂಡೊಯ್ಯುವ ಶಾರ್ಟ್‌ಕಟ್‌ ಕಡೆಗೆ ಗಮನ ಇರಿಸಿ."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ಈ ಅಧಿಸೂಚನೆಯು ಶಬ್ದ ಮಾಡಬೇಕೇ ಅಥವಾ ವೈಬ್ರೇಟ್ ಮಾಡಬೇಕೇ ಎಂಬುದನ್ನು ನಿರ್ಧರಿಸುವ ಅವಕಾಶವನ್ನು ಸಿಸ್ಟಂಗೆ ನೀಡಿ"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"ಸಂಭಾಷಣೆ ವಿಭಾಗದ ಮೇಲ್ಭಾಗದಲ್ಲಿ ತೇಲುವ ಬಬಲ್‌ ಆಗಿ ಗೋಚರಿಸುತ್ತದೆ ಮತ್ತು ಪ್ರೊಫೈಲ್ ಚಿತ್ರವನ್ನು ಲಾಕ್‌ಸ್ಕ್ರೀನ್‌ ಮೇಲೆ‌ ಗೋಚರಿಸುತ್ತದೆ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ಆದ್ಯತೆ"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಮೇಲಿಂದ ಪ್ರದರ್ಶಿಸುತ್ತಿದೆ ಮತ್ತು ಮೈಕ್ರೊಫೋನ್ ಮತ್ತು ಕ್ಯಾಮರಾವನ್ನು ಬಳಸುತ್ತಿದೆ."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ಸರಿ"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ಈ ಅಧಿಸೂಚನೆಯನ್ನು ಸಿಸ್ಟಂ ಮೌನಗೊಳಿಸಿದೆ."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ಈ ಅಧಿಸೂಚನೆಯನ್ನು ಸಿಸ್ಟಂ ಮೇಲ್ದರ್ಜೆಗೆ ಏರಿಸಿದೆ."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ಈ ಅಧಿಸೂಚನೆಯನ್ನು ಸಿಸ್ಟಂ ಕೆಳದರ್ಜೆಗೆ ಇಳಿಸಿದೆ."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"ಇದು ಸರಿಯಾಗಿತ್ತೇ?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಗೆ ಧನ್ಯವಾದಗಳು!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ಸರಿ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳನ್ನು ತೆರೆಯಲಾಗಿದೆ"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"ಈ ಚಾನಲ್‌ನ ಅಧಿಸೂಚನೆಗಳಿಗೆ ಅನುಮತಿ ನೀಡಿ"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಕ್ರಮವನ್ನು ಎಡಿಟ್ ಮಾಡಿ."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="ID_1">%1$d</xliff:g> ಪುಟ"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ಲಾಕ್ ಪರದೆ"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"ವಿಸ್ತೃತಗೊಳಿಸು"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"ಕುಗ್ಗಿಸಿ"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"ಮುಚ್ಚಿ"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"ವಜಾಗೊಳಿಸಲು ಕೆಳಕ್ಕೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"ಮೆನು"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರವಾಗಿದೆ"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> ಈ ವೈಶಿಷ್ಟ್ಯ ಬಳಸುವುದನ್ನು ನೀವು ಬಯಸದಿದ್ದರೆ, ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಲು ಮತ್ತು ಅದನ್ನು ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="pip_play" msgid="333995977693142810">"ಪ್ಲೇ"</string>
- <string name="pip_pause" msgid="1139598607050555845">"ವಿರಾಮಗೊಳಿಸಿ"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"ಮುಂದಕ್ಕೆ ಸ್ಕಿಪ್‌ ಮಾಡಿ"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"ಹಿಂದಕ್ಕೆ ಸ್ಕಿಪ್‌ ಮಾಡಿ"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"ಫೋನ್ ಬಿಸಿಯಾಗಿದ್ದರಿಂದ ಆಫ್ ಆಗಿದೆ"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"ಈಗ ನಿಮ್ಮ ಫೋನ್ ಎಂದಿನಂತೆ ಕೆಲಸ ಮಾಡುತ್ತಿದೆ"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"ನಿಮ್ಮ ಫೋನ್ ಈಗ ಎಂದಿನಂತೆ ರನ್ ಆಗುತ್ತಿದೆ.\nಇನ್ನಷ್ಟು ಮಾಹಿತಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"ನಿಮ್ಮ ಫೋನ್ ತುಂಬಾ ಬಿಸಿಯಾಗಿತ್ತು, ತಣ್ಣಗಾಗಲು ಅದು ತಾನಾಗಿ ಆಫ್ ಆಗಿದೆ. ಈಗ ನಿಮ್ಮ ಫೋನ್ ಎಂದಿನಂತೆ ಕೆಲಸ ಮಾಡುತ್ತಿದೆ.\n\nನಿಮ್ಮ ಫೋನ್ ಬಿಸಿಯಾಗಲು ಕಾರಣಗಳು:\n • ಹೆಚ್ಚು ಸಂಪನ್ಮೂಲ ಉಪಯೋಗಿಸುವ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಬಳಕೆ (ಉದಾ, ಗೇಮಿಂಗ್, ವೀಡಿಯೊ/ನ್ಯಾವಿಗೇಶನ್ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು)\n • ದೊಡ್ಡ ಫೈಲ್‌ಗಳ ಡೌನ್‌ಲೋಡ್ ಅಥವಾ ಅಪ್‌ಲೋಡ್\n • ಅಧಿಕ ಉಷ್ಣಾಂಶದಲ್ಲಿ ಫೋನಿನ ಬಳಕೆ"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ಕಾಳಜಿಯ ಹಂತಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ಫೋನ್ ಬಿಸಿಯಾಗುತ್ತಿದೆ"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ಫೋನ್ ತಣ್ಣಗಾಗುವವರೆಗೂ ಕೆಲವು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಸೀಮಿತಗೊಳಿಸುತ್ತದೆ"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ಫೋನ್ ತಣ್ಣಗಾಗುವವರೆಗೂ ಕೆಲವು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಸೀಮಿತಗೊಳಿಸಲಾಗುತ್ತದೆ\nಇನ್ನಷ್ಟು ಮಾಹಿತಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ನಿಮ್ಮ ಫೋನ್ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ತಣ್ಣಗಾಗಲು ಪ್ರಯತ್ನಿಸುತ್ತದೆ. ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ನೀವು ಈಗಲೂ ಬಳಸಬಹುದಾಗಿರುತ್ತದೆ, ಆದರೆ ಇದು ನಿಧಾನವಾಗಿರಬಹುದು.\n\nಒಮ್ಮೆ ನಿಮ್ಮ ಫೋನ್ ತಣ್ಣಗಾದ ನಂತರ ಇದು ಸಾಮಾನ್ಯ ರೀತಿಯಲ್ಲಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ಕಾಳಜಿಯ ಹಂತಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ಚಾರ್ಜರ್ ಅನ್‌ಪ್ಲಗ್ ಮಾಡಿ"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"ಈ ಸಾಧನವನ್ನು ಚಾರ್ಜ್ ಮಾಡುತ್ತಿರುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ. ಪವರ್ ಅಡಾಪ್ಟರ್ ಅನ್ನು ಅನ್‌ಪ್ಲಗ್ ಮಾಡಿ ಮತ್ತು ಕೇಬಲ್ ಬೆಚ್ಚಗಿರಬೇಕೆಂದು ಜಾಗ್ರತೆ ವಹಿಸಿ."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ಕಾಳಜಿ ಹಂತಗಳನ್ನು ನೋಡಿ"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"ನಿಯಂತ್ರಣಗಳನ್ನು ಮರುಹೊಂದಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಮತ್ತು ಡ್ರ್ಯಾಗ್‌ ಮಾಡಿ"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"ಎಲ್ಲಾ ನಿಯಂತ್ರಣಗಳನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ಬದಲಾವಣೆಗಳನ್ನು ಉಳಿಸಲಾಗಿಲ್ಲ"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ಇತರ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"ನಿಯಂತ್ರಣಗಳನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಬದಲಾಗಿಲ್ಲ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು <xliff:g id="APP">%s</xliff:g> ಆ್ಯಪ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"ಹೊಂದಾಣಿಕೆಯ ನಿಯಂತ್ರಣಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ಇತರ"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> ಸಾಧನಕ್ಕಾಗಿ ಬದಲಾವಣೆಯನ್ನು ದೃಢೀಕರಿಸಿ"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"ಇನ್ನಷ್ಟು ನೋಡಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ಶಿಫಾರಸುಗಳು ಲೋಡ್ ಆಗುತ್ತಿವೆ"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ಈ ಮಾಧ್ಯಮ ಸೆಶನ್ ಅನ್ನು ಮುಚ್ಚಿರಿ"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"ಮಾಧ್ಯಮ"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಿ."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ಮರೆಮಾಡಿ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ನಿಷ್ಕ್ರಿಯ, ಆ್ಯಪ್ ಪರಿಶೀಲಿಸಿ"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"ದೋಷ, ಮರುಪ್ರಯತ್ನಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ಕಂಡುಬಂದಿಲ್ಲ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings_tv.xml b/packages/SystemUI/res/values-kn/strings_tv.xml
index d01e14780046..7db0c7080919 100644
--- a/packages/SystemUI/res/values-kn/strings_tv.xml
+++ b/packages/SystemUI/res/values-kn/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP ಮುಚ್ಚಿ"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"ಪೂರ್ಣ ಪರದೆ"</string>
<string name="mic_active" msgid="5766614241012047024">"ಮೈಕ್ರೋಫೋನ್‌ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಪ್ರವೇಶಿಸಿದೆ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index cdf44b3c349a..ef4a5f63d0d3 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"다시 탭하여 열기"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"위로 스와이프하여 열기"</string>
<string name="keyguard_retry" msgid="886802522584053523">"위로 스와이프하여 다시 시도해 주세요"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"내 조직에 속한 기기입니다."</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>에 속한 기기입니다."</string>
<string name="phone_hint" msgid="6682125338461375925">"전화 기능을 사용하려면 아이콘에서 스와이프하세요."</string>
<string name="voice_hint" msgid="7476017460191291417">"음성 지원을 사용하려면 아이콘에서 스와이프하세요."</string>
<string name="camera_hint" msgid="4519495795000658637">"카메라를 사용하려면 아이콘에서 스와이프하세요."</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"프로필이 모니터링될 수 있음"</string>
<string name="vpn_footer" msgid="3457155078010607471">"네트워크가 모니터링될 수 있음"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"네트워크가 모니터링될 수 있음"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"내 조직에서 이 기기를 소유하며 네트워크 트래픽을 모니터링할 수 있습니다."</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>에서 이 기기를 소유하며 네트워크 트래픽을 모니터링할 수 있습니다."</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"내 조직에 속한 기기이며 <xliff:g id="VPN_APP">%1$s</xliff:g>에 연결되었습니다."</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>에 속한 기기이며 <xliff:g id="VPN_APP">%2$s</xliff:g>에 연결되었습니다."</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"내 조직에 속한 기기입니다."</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>에 속한 기기입니다."</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"내 조직에 속한 기기이며 VPN에 연결되었습니다."</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>에 속한 기기이며 VPN에 연결되었습니다."</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"조직에서 직장 프로필의 네트워크 트래픽을 모니터링할 수 있습니다."</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>에서 내 직장 프로필의 네트워크 트래픽을 모니터링할 수 있습니다."</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"네트워크가 모니터링될 수 있습니다."</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"기기가 VPN에 연결되었습니다."</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"직장 프로필이 <xliff:g id="VPN_APP">%1$s</xliff:g>에 연결되었습니다."</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"개인 프로필이 <xliff:g id="VPN_APP">%1$s</xliff:g>에 연결되었습니다."</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"기기가 <xliff:g id="VPN_APP">%1$s</xliff:g>에 연결되었습니다."</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"기기 관리"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"프로필 모니터링"</string>
<string name="monitoring_title" msgid="4063890083735924568">"네트워크 모니터링"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN 사용 중지"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN 연결 해제"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"정책 보기"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>에 속한 기기입니다.\n\nIT 관리자가 설정, 기업 액세스, 앱, 기기와 연결된 데이터, 기기 위치 정보를 모니터링 및 관리할 수 있습니다.\n\n자세한 정보를 확인하려면 IT 관리자에게 문의하세요."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"내 조직에 속한 기기입니다.\n\nIT 관리자가 설정, 기업 액세스, 앱, 기기와 연결된 데이터, 기기 위치 정보를 모니터링 및 관리할 수 있습니다.\n\n자세한 정보를 확인하려면 IT 관리자에게 문의하세요."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"조직에서 이 기기에 인증기관을 설치했습니다. 보안 네트워크 트래픽을 모니터링 또는 수정할 수 있습니다."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"조직에서 직장 프로필에 인증기관을 설치했습니다. 보안 네트워크 트래픽을 모니터링 또는 수정할 수 있습니다."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"이 기기에는 인증기관이 설치되어 있습니다. 보안 네트워크 트래픽을 모니터링 또는 수정할 수 있습니다."</string>
@@ -727,16 +711,14 @@
<string name="notification_silence_title" msgid="8608090968400832335">"무음"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"기본값"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"버블"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"자동"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"소리 또는 진동 없음"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"소리나 진동이 울리지 않으며 대화 섹션 하단에 표시됨"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있음"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있습니다. 기본적으로 <xliff:g id="APP_NAME">%1$s</xliff:g>의 대화는 대화창으로 표시됩니다."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"이 콘텐츠로 연결되는 플로팅 바로가기로 사용자의 주의를 끕니다."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"대화 섹션 상단의 플로팅 대화창 또는 잠금 화면의 프로필 사진으로 표시됩니다."</string>
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"시스템에서 알림 시 소리 또는 진동을 사용할지 결정하도록 허용합니다."</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"대화 섹션 상단에 표시, 플로팅 대화창으로 표시, 그리고 잠금 화면에 프로필 사진이 표시됩니다."</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"설정"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"우선순위"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱은 대화 기능을 지원하지 않습니다."</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"앱이 화면의 다른 앱 위에 표시되고 있으며, 마이크와 카메라를 사용 중입니다.."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"설정"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"확인"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"시스템에서 이 알림을 숨겼습니다."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"시스템에서 이 알림의 순위를 올렸습니다."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"시스템에서 이 알림의 순위를 내렸습니다."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"맞나요?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"의견을 보내 주셔서 감사합니다."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"확인"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> 알림 컨트롤을 열었습니다."</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> 알림 컨트롤을 닫았습니다."</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"이 채널의 알림을 허용합니다."</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"설정 순서 수정"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>페이지 중 <xliff:g id="ID_1">%1$d</xliff:g>페이지"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"잠금 화면"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"펼치기"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"최소화"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"닫기"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"설정"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"아래로 드래그하여 닫기"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"메뉴"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g>에서 PIP 사용 중"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g>에서 이 기능이 사용되는 것을 원하지 않는 경우 탭하여 설정을 열고 기능을 사용 중지하세요."</string>
- <string name="pip_play" msgid="333995977693142810">"재생"</string>
- <string name="pip_pause" msgid="1139598607050555845">"일시중지"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"다음으로 건너뛰기"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"이전으로 건너뛰기"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"발열로 인해 휴대전화 전원이 종료됨"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"휴대전화가 정상적으로 실행 중입니다."</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"이제 휴대전화가 정상적으로 작동합니다.\n자세히 알아보려면 탭하세요."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"휴대전화가 과열되어 온도를 낮추기 위해 전원이 종료되었습니다. 지금은 휴대전화가 정상적으로 실행 중입니다.\n\n휴대전화가 과열되는 이유는 다음과 같습니다.\n • 리소스를 많이 사용하는 앱 사용(예: 게임, 동영상 또는 내비게이션 앱)\n • 대용량 파일을 다운로드 또는 업로드\n • 온도가 높은 곳에서 휴대폰 사용"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"해결 방법 확인하기"</string>
<string name="high_temp_title" msgid="2218333576838496100">"휴대전화 온도가 높음"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"휴대전화 온도를 낮추는 동안 일부 기능이 제한됩니다."</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"휴대전화 온도를 낮추는 동안 일부 기능이 제한됩니다.\n자세히 알아보려면 탭하세요."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"휴대전화 온도를 자동으로 낮추려고 시도합니다. 휴대전화를 계속 사용할 수는 있지만 작동이 느려질 수도 있습니다.\n\n휴대전화 온도가 낮아지면 정상적으로 작동됩니다."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"해결 방법 확인하기"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"충전기를 연결 해제하세요"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"기기를 충전하는 중에 문제가 발생했습니다. 케이블이 뜨거울 수 있으므로 주의하여 전원 어댑터를 분리하세요."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"취해야 할 조치 확인"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"길게 누르고 드래그하여 컨트롤 재정렬"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"모든 컨트롤 삭제됨"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"변경사항이 저장되지 않음"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"다른 앱 보기"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"컨트롤을 로드할 수 없습니다. <xliff:g id="APP">%s</xliff:g> 앱에서 설정이 변경되지 않았는지 확인하세요."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"호환 컨트롤을 사용할 수 없습니다."</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"기타"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> 변경 확인"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"자세히 보려면 스와이프하세요."</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"추천 제어 기능 로드 중"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"이 미디어 세션 닫기"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"미디어"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"현재 세션을 숨깁니다."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"숨기기"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"다시 시작"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"설정"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"비활성. 앱을 확인하세요."</string>
<string name="controls_error_retryable" msgid="864025882878378470">"오류 발생, 다시 시도 중…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"찾을 수 없음"</string>
diff --git a/packages/SystemUI/res/values-ko/strings_tv.xml b/packages/SystemUI/res/values-ko/strings_tv.xml
index 0615fe85d72e..10889705ddc3 100644
--- a/packages/SystemUI/res/values-ko/strings_tv.xml
+++ b/packages/SystemUI/res/values-ko/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"PIP 모드"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(제목 없는 프로그램)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP 닫기"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"전체화면"</string>
<string name="mic_active" msgid="5766614241012047024">"마이크 사용 중"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s에서 내 마이크에 액세스했습니다."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index bc0bda518e8e..cfd91b56ddf5 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ачуу үчүн кайра таптап коюңуз"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ачуу үчүн өйдө сүрүңүз"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Кайталоо үчүн экранды өйдө сүрүңүз"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Бул түзмөк уюмуңузга таандык"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Бул түзмөк төмөнкүгө таандык: <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Сүрөтчөнү серпип телефонго өтүңүз"</string>
<string name="voice_hint" msgid="7476017460191291417">"Сүрөтчөнү серпип үн жардамчысына өтүңүз"</string>
<string name="camera_hint" msgid="4519495795000658637">"Сүрөтчөнү серпип камерага өтүңүз"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Профилди көзөмөлдөсө болот"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Тармак көзөмөлдөнүшү мүмкүн"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Тармак көзөмөлдөнүшү мүмкүн"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Бул түзмөк уюмуңузга таандык. Уюмуңуз тармактын трафигин көзөмөлдөй алат"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Бул түзмөк <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> уюмуна таандык. Уюм тармактын трафигин көзөмөлдөй алат"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Бул түзмөк уюмуңузга таандык жана <xliff:g id="VPN_APP">%1$s</xliff:g> колдонмосуна туташтырылган"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Бул түзмөк <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> уюмуна таандык жана <xliff:g id="VPN_APP">%2$s</xliff:g> колдонмосуна туташтырылган"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Бул түзмөк уюмуңузга таандык"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Бул түзмөк төмөнкүгө таандык: <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Бул түзмөк уюмуңузга таандык жана VPN\'дерге туташтырылган"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Бул түзмөк <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> уюмуна таандык жана VPN\'дерге туташтырылган"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Ишканаңыз жумуш профилиңиздин тармак трафигин көзөмөлдөй алат"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> жумуш профилиңиздеги тармак трафигин көзөмөлдөй алат"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Тармак көзөмөлдөнүшү мүмкүн"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Бул түзмөк VPN\'дерге туташтырылган"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Жумуш профилиңиз <xliff:g id="VPN_APP">%1$s</xliff:g> колдонмосуна туташып турат"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Жеке профилиңиз <xliff:g id="VPN_APP">%1$s</xliff:g> колдонмосуна туташтырылган"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Бул түзмөк <xliff:g id="VPN_APP">%1$s</xliff:g> колдонмосуна туташтырылган"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Түзмөктү башкаруу"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Профилди көзөмөлдөө"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Тармакка көз салуу"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN\'ди өчүрүү"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN\'ди ажыратуу"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Саясаттарды карап көрүү"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Бул түзмөк <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> уюмуна таандык.\n\nIT администраторуңуз жөндөөлөрдү, корпоративдик мүмкүнчүлүктү, колдонмолорду, түзмөгүңүзгө байланыштуу дайын-даректерди жана түзмөгүңүздүн жайгашкан жери тууралуу маалыматты көзөмөлдөп жана башкара алат.\n\nТолугураак маалымат алуу үчүн, IT администраторуңузга кайрылыңыз."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Бул түзмөк уюмуңузга таандык.\n\nIT администраторуңуз жөндөөлөрдү, корпоративдик мүмкүнчүлүктү, колдонмолорду, түзмөгүңүзгө байланыштуу дайын-даректерди жана түзмөгүңүздүн жайгашкан жери тууралуу маалыматты көзөмөлдөп жана башкара алат.\n\nТолугураак маалымат алуу үчүн, IT администраторуңузга кайрылыңыз."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Ишканаңыз бул түзмөккө тастыктоочу борборду орнотту. Коопсуз тармагыңыздын трафиги көзөмөлдөнүп же өзгөртүлүшү мүмкүн."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ишканаңыз жумуш профилиңизге тастыктоочу борборду орнотту. Коопсуз тармагыңыздын трафиги көзөмөлдөнүп же өзгөртүлүшү мүмкүн."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Бул түзмөктө тастыктоочу борбор орнотулган. Коопсуз тармагыңыздын трафиги көзөмөлдөнүп же өзгөртүлүшү мүмкүн."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Үнсүз"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Демейки"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Көбүк"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматтык"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Үнү чыкпайт жана дирилдебейт"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Үнү чыгып же дирилдебейт жана жазышуу бөлүмүнүн ылдый жагында көрүнөт"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн. <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосундагы жазышуулар демейки жөндөө боюнча калкып чыкма билдирмелер түрүндө көрүнөт."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Калкыма ыкчам баскыч менен көңүлүңүздү бул мазмунга буруп турат."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Билдирменин үнүн чыгартууну же басууну тутумга тапшырыңыз"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Жазышуу бөлүмүнүн жогорку жагында калкып чыкма билдирме түрүндө көрүнүп, профиль сүрөтү кулпуланган экрандан чагылдырылат"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Жөндөөлөр"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Маанилүүлүгү"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Бул колдонмо экрандагы башка терезелердин үстүнөн көрсөтүлүп, микрофонду жана камераны колдонууда."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Жөндөөлөр"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ЖАРАЙТ"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Бул билдирменин үнү тутум тарабынан басылды."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Бул билдирменин маанилүүлүгү тутум тарабынан жогорулатылды."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Бул билдирменин маанилүүлүгү тутум тарабынан төмөндөтүлдү."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Бул туурабы?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Пикириңиз үчүн рахмат!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Жарайт"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу үчүн эскертмени көзөмөлдөө функциялары ачылды"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу үчүн эскертмени көзөмөлдөө функциялары жабылды"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Бул каналдан келген эскертмелерге уруксат берүү"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Жөндөөлөрдүн иретин өзгөртүү."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ичинен <xliff:g id="ID_1">%1$d</xliff:g>-бет"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Кулпуланган экран"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Жайып көрсөтүү"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Кичирейтүү"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Жабуу"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Жөндөөлөр"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Четке кагуу үчүн төмөн сүйрөңүз"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Меню"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> – сүрөт ичиндеги сүрөт"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Эгер <xliff:g id="NAME">%s</xliff:g> колдонмосу бул функцияны пайдаланбасын десеңиз, жөндөөлөрдү ачып туруп, аны өчүрүп коюңуз."</string>
- <string name="pip_play" msgid="333995977693142810">"Ойнотуу"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Тындыруу"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Кийинкисине өткөрүп жиберүү"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Мурункусуна өткөрүп жиберүү"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Телефон ысыгандыктан өчүрүлдү"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Телефонуңуз кадимкидей иштеп жатат"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Телефонуңуз кадимкидей иштеп жатат.\nКеңири маалымат алуу үчүн таптап коюңуз"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Телефонуңуз өтө ысып кеткендиктен, аны муздатуу үчүн өчүрүлдү. Эми телефонуңуз кадимкидей иштеп жатат.\n\nТелефонуңуз төмөнкү шарттарда ысып кетиши мүмкүн:\n • Ашыкча ресурс короткон колдонмолорду (оюндар, видео же чабыттоо колдонмолору) пайдалансаңыз \n • Ири көлөмдөгү файлдарды жүктөп алсаңыз же берсеңиз\n • Телефонуңузду жогорку температураларда пайдалансаңыз"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Тейлөө кадамдарын көрүңүз"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Телефонуңуз ысып баратат"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Телефон сууганча айрым элементтердин иши чектелген"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Телефон сууганча айрым элементтердин иши чектелген.\nКеңири маалымат алуу үчүн таптап коюңуз"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефонуңуз автоматтык түрдө сууйт. Аны колдоно берсеңиз болот, бирок ал жайыраак иштеп калат.\n\nТелефонуңуз суугандан кийин адаттагыдай эле иштеп баштайт."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Тейлөө кадамдарын көрүңүз"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Кубаттагычты сууруңуз"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Бул түзмөктү кубаттоодо маселе келип чыкты. Кабель ысып кетиши мүмкүн, андыктан кубаттагыч адаптерин сууруп коюңуз."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Тейлөө кадамдарын көрүңүз"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Башкаруу элементтеринин иретин өзгөртүү үчүн, кармап туруп, сүйрөңүз"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Бардык башкаруу элементтери өчүрүлдү"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Өзгөртүүлөр сакталган жок"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Башка колдонмолорду көрүү"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Көзөмөлдөр жүктөлгөн жок. <xliff:g id="APP">%s</xliff:g> колдонмосуна өтүп, колдонмонун жөндөөлөрү өзгөрбөгөнүн текшериңиз."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Шайкеш көзөмөлдөр жеткиликсиз"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Башка"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> түзмөгү үчүн өзгөртүүнү ырастаңыз"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Дагы көрүү үчүн экранды сүрүп коюңуз"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Сунуштар жүктөлүүдө"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Бул медиа сеансын жабуу"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Учурдагы сеансты жашыруу."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Жашыруу"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Жөндөөлөр"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Жигерсиз. Колдонмону текшериңиз"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Ката, дагы аракет жасалууда…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Табылган жок"</string>
diff --git a/packages/SystemUI/res/values-ky/strings_tv.xml b/packages/SystemUI/res/values-ky/strings_tv.xml
index 5b40ca93bac2..b69b1c6e26b5 100644
--- a/packages/SystemUI/res/values-ky/strings_tv.xml
+++ b/packages/SystemUI/res/values-ky/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Сүрөт ичиндеги сүрөт"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Аталышы жок программа)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP\'ти жабуу"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Толук экран"</string>
<string name="mic_active" msgid="5766614241012047024">"Микрофон күйүк"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s микрофонуңузду колдонууда"</string>
</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 2c08925be496..9e1b66f07758 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -44,4 +44,7 @@
<dimen name="biometric_dialog_button_negative_max_width">140dp</dimen>
<dimen name="biometric_dialog_button_positive_max_width">116dp</dimen>
+
+ <dimen name="global_actions_power_dialog_item_height">130dp</dimen>
+ <dimen name="global_actions_power_dialog_item_bottom_margin">35dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 60c8ffea63a8..90b5ad2267ac 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ແຕະ​ອີກ​ຄັ້ງ​ເພື່ອ​ເປີດ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ປັດຂຶ້ນເພື່ອເປີດ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ປັດຂຶ້ນເພື່ອລອງໃໝ່"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ອຸປະກອນນີ້ເປັນຂອງອົງການທ່ານ"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ອຸ​ປະ​ກອນ​ນີ້​ເປັນ​ຂອງ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"ປັດ​ຈາກ​ໄອ​ຄອນ​ສຳ​ລັບ​ໂທ​ລະ​ສັບ"</string>
<string name="voice_hint" msgid="7476017460191291417">"ປັດ​ຈາກ​ໄອ​ຄອນ​ສຳ​ລັບ​ການ​ຊ່ວຍ​ທາງ​ສຽງ"</string>
<string name="camera_hint" msgid="4519495795000658637">"ປັດ​ຈາກ​ໄອ​ຄອນ​ສຳ​ລັບ​ກ້ອງ​ຖ່າຍ​ຮູບ"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ໂປຣ​ໄຟລ໌​ອາດ​ຖືກ​ເຝົ້າ​ຕິດ​ຕາມ​ຢູ່"</string>
<string name="vpn_footer" msgid="3457155078010607471">"​ເຄືອ​ຂ່າຍ​ອາດ​ມີ​ການ​ເຝົ້າ​ຕິດ​ຕາມ"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ການນຳໃຊ້ເຄືອຂ່າຍອາດມີການກວດສອບຕິດຕາມ"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ອົງການຂອງທ່ານເປັນເຈົ້າຂອງອຸປະກອນນີ້ ແລະ ສາມາດຕິດຕາມທຣາບຟິກເຄືອຂ່າຍໄດ້"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ເປັນເຈົ້າຂອງອຸປະກອນນີ້ ແລະ ສາມາດຕິດຕາມທຣາບຟິກເຄືອຂ່າຍໄດ້"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ອຸປະກອນນີ້ເປັນຂອງອົງການທ່ານ ແລະ ເຊື່ອມຕໍ່ຫາ <xliff:g id="VPN_APP">%1$s</xliff:g> ແລ້ວ"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ອຸປະກອນນີ້ເປັນຂອງ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ແລະ ເຊື່ອມຕໍ່ຫາ <xliff:g id="VPN_APP">%2$s</xliff:g> ແລ້ວ"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ອຸປະກອນນີ້ເປັນຂອງອົງການທ່ານ"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ອຸ​ປະ​ກອນ​ນີ້​ເປັນ​ຂອງ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ອຸປະກອນນີ້ເປັນຂອງອົງການທ່ານ ແລະ ເຊື່ອມຕໍ່ຫາ VPN ແລ້ວ"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ອຸປະກອນນີ້ເປັນຂອງ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ແລະ ເຊື່ອມຕໍ່ຫາ VPN ແລ້ວ"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"ອົງກອນຂອງທ່ານສາມາດຕິດຕາມທຣາບຟິກເຄືອຂ່າຍໃນໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານໄດ້"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ສາມາດຕິດຕາມທຣາບຟິກເຄືອຂ່າຍໃນໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານໄດ້"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ເຄືອຂ່າຍອາດຖືກຕິດຕາມ"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ໂປຣໄຟລ໌ສ່ວນຕົວຂອງທ່ານເຊື່ອມຕໍ່ຫາ VPN ແລ້ວ"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານເຊື່ອມຕໍ່ຫາ <xliff:g id="VPN_APP">%1$s</xliff:g> ແລ້ວ"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"ໂປຣໄຟລ໌ສ່ວນຕົວຂອງທ່ານເຊື່ອມຕໍ່ຫາ <xliff:g id="VPN_APP">%1$s</xliff:g> ແລ້ວ"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ອຸປະກອນນີ້ເຊື່ອມຕໍ່ຫາ <xliff:g id="VPN_APP">%1$s</xliff:g> ແລ້ວ"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ການຈັດການອຸປະກອນ"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"ການ​ຕິດ​ຕາມ​ໂປຣ​ໄຟລ໌"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ການກວດ​ສອບ​ຕິດ​ຕາມ​ເຄືອ​ຂ່າຍ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"ປິດ​ການ​ໃຊ້ VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"ຕັດ​ການ​ເຊື່ອມ​ຕໍ່ VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"ເບິ່ງນະໂຍບາຍ"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ອຸປະກອນນີ້ເປັນຂອງ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານສາມາດເຝົ້າຕິດຕາມ ແລະ ຈັດການການຕັ້ງຄ່າ, ສິດເຂົ້າເຖິງອົງກອນ, ແອັບ, ຂໍ້ມູນທີ່ເຊື່ອມໂຍງກັບອຸປະກອນຂອງທ່ານໄດ້.\n\nສຳລັບຂໍ້ມູນເພີ່ມເຕີມ, ກະລຸນາຕິດຕໍ່ຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານ."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ອຸປະກອນນີ້ເປັນຂອງອົງການທ່ານ.\n\nຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານສາມາດເຝົ້າຕິດຕາມ ແລະ ຈັດການການຕັ້ງຄ່າ, ສິດເຂົ້າເຖິງອົງກອນ, ແອັບ, ຂໍ້ມູນທີ່ເຊື່ອມໂຍງກັບອຸປະກອນຂອງທ່ານໄດ້.\n\nສຳລັບຂໍ້ມູນເພີ່ມເຕີມ, ກະລຸນາຕິດຕໍ່ຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານ."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ອົງກອນຂອງທ່ານຕິດຕັ້ງອຳນາດໃບຮັບຮອງໄວ້ໃນອຸປະກອນນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ອົງກອນຂອງທ່ານຕິດຕັ້ງອຳນາດໃບຮັບຮອງໄວ້ໃນໂປຣໄຟລ໌ບ່ອນເຮັດວຽກນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ມີອຳນາດໃບຮັບຮອງຕິດຕັ້ງຢູ່ໃນອຸປະກອນນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"ປິດສຽງ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ຄ່າເລີ່ມຕົ້ນ"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"ຟອງ"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ອັດຕະໂນມັດ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ບໍ່ມີສຽງ ຫຼື ການສັ່ນເຕືອນ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ບໍ່ມີສຽງ ຫຼື ການສັ່ນເຕືອນ ແລະ ປາກົດຢູ່ທາງລຸ່ມຂອງພາກສ່ວນການສົນທະນາ"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ອາດສົ່ງສຽງ ຫຼື ສັ່ນເຕືອນໂດຍອ້າງອີງຈາກການຕັ້ງຄ່າໂທລະສັບ"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ອາດສົ່ງສຽງ ຫຼື ສັ່ນເຕືອນໂດຍອ້າງອີງຈາກການຕັ້ງຄ່າໂທລະສັບ. ການສົນທະນາຈາກ <xliff:g id="APP_NAME">%1$s</xliff:g> ຈະເປັນ bubble ຕາມຄ່າເລີ່ມຕົ້ນ."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ເອົາໃຈໃສ່ທາງລັດແບບລອຍໄປຫາເນື້ອຫານີ້."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ໃຫ້ລະບົບກຳນົດວ່າການແຈ້ງເຕືອນນິ້ຄວນມີສຽງ ຫຼື ສັ່ນເຕືອນຫຼືບໍ່"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"ສະແດງຢູ່ເທິງສຸດຂອງພາກສ່ວນການສົນທະນາ, ປາກົດເປັນ bubble ແບບລອຍ, ສະແດງຮູບໂປຣໄຟລ໌ຢູ່ໜ້າຈໍລັອກ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ຕັ້ງຄ່າ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ສຳຄັນ"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ແອັບນີ້ກຳລັງສະແດງຜົນບັງແອັບອື່ນຢູ່ໜ້າຈໍຂອງທ່ານ ແລະ ກຳລັງໃຊ້ໄມໂຄຣໂຟນ ແລະ ກ້ອງຖ່າຍຮູບຢູ່."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ການຕັ້ງຄ່າ"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ຕົກລົງ"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ການແຈ້ງເຕືອນນີ້ຖືກປີດສຽງໂດຍລະບົບແລ້ວ."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ການແຈ້ງເຕືອນນີ້ຖືກຍົກລະດັບໂດຍລະບົບແລ້ວ."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ການແຈ້ງເຕືອນນີ້ຖືກຫຼຸດລະດັບໂດຍລະບົບແລ້ວ."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"ສິ່ງນີ້ບໍ່ຖືກຕ້ອງບໍ?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"ຂອບໃຈສຳລັບຄຳເຫັນຂອງທ່ານ!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ຕົກລົງ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"ເປີດຕົວຄວບຄຸມການແຈ້ງເຕືອນສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"ປິດຕົວຄວບຄຸມການແຈ້ງເຕືອນສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"ອະນຸຍາດການແຈ້ງເຕືອນຈາກຊ່ອງນີ້"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ແກ້ໄຂລຳດັບການຕັ້ງຄ່າ."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g> ຈາກທັງໝົດ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ໜ້າຈໍລັອກ"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"ຂະຫຍາຍ"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"ຫຍໍ້"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"ປິດ"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ການຕັ້ງຄ່າ"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"ລາກລົງເພື່ອປິດໄວ້"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"ເມນູ"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> ແມ່ນເປັນການສະແດງຜົນຫຼາຍຢ່າງພ້ອມກັນ"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"ຫາກທ່ານບໍ່ຕ້ອງການ <xliff:g id="NAME">%s</xliff:g> ໃຫ້ໃຊ້ຄຸນສົມບັດນີ້, ໃຫ້ແຕະເພື່ອເປີດການຕັ້ງຄ່າ ແລ້ວປິດມັນໄວ້."</string>
- <string name="pip_play" msgid="333995977693142810">"ຫຼິ້ນ"</string>
- <string name="pip_pause" msgid="1139598607050555845">"ຢຸດຊົ່ວຄາວ"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"ຂ້າມໄປລາຍການໜ້າ"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"ຂ້າມໄປລາຍການກ່ອນນີ້"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"ປິດໂທລະສັບເນື່ອງຈາກຮ້ອນເກີນໄປ"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"ໂທລະສັບຂອງທ່ານຕອນນີ້ເຮັດວຽກປົກກະຕິແລ້ວ"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"ໂທລະສັບຂອງທ່ານຕອນນີ້ເຮັດວຽກປົກກະຕິແລ້ວ.\nແຕະເພື່ອເບິ່ງຂໍ້ມູນເພີ່ມເຕີມ"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"ໂທລະສັບຂອງທ່ານຮ້ອນເກີນໄປ, ດັ່ງນັ້ນມັນຈຶ່ງຖືກປິດໄວ້ເພື່ອໃຫ້ເຢັນກ່ອນ. ໂທລະສັບຂອງທ່ານຕອນນີ້ເຮັດວຽກປົກກະຕິແລ້ວ.\n\nໂທລະສັບຂອງທ່ານອາດຮ້ອນຫາກວ່າທ່ານ:\n • ໃຊ້ແອັບທີ່ກິນຊັບພະຍາກອນຫຼາຍ (ເຊັ່ນ: ເກມ, ວິດີໂອ ຫຼື ແອັບການນຳທາງ)\n • ດາວໂຫລດ ຫຼື ອັບໂຫລດຮູບພາບຂະໜາດໃຫຍ່\n • ໃຊ້ໂທລະສັບຂອງທ່ານໃນບ່ອນທີ່ມີອຸນຫະພູມສູງ"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ເບິ່ງຂັ້ນຕອນການເບິ່ງແຍງ"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ໂທລະສັບກຳລັງຮ້ອນຂຶ້ນ"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ຄຸນສົມບັດບາງຢ່າງຖືກຈຳກັດໄວ້ເນື່ອງໃນເວລາຫຼຸດອຸນຫະພູມຂອງໂທລະສັບ"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ຄຸນສົມບັດບາງຢ່າງຖືກຈຳກັດໄວ້ໃນເວລາຫຼຸດອຸນຫະພູມຂອງໂທລະສັບ.\nແຕະເພື່ອເບິ່ງຂໍ້ມູນເພີ່ມເຕີມ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ໂທລະສັບຂອງທ່ານຈະພະຍາຍາມລົດອຸນຫະພູມລົງ. ທ່ານຍັງຄົງສາມາດໃຊ້ໂທລະສັບຂອງທ່ານໄດ້ຢູ່, ແຕ່ມັນຈະເຮັດວຽກຊ້າລົງ.\n\nເມື່ອໂທລະສັບຂອງທ່ານບໍ່ຮ້ອນຫຼາຍແລ້ວ, ມັນຈະກັບມາເຮັດວຽກຕາມປົກກະຕິ."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ເບິ່ງຂັ້ນຕອນການເບິ່ງແຍງ"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ຖອດສາຍສາກອອກ"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"ເກີດບັນຫາໃນການສາກໄຟອຸປະກອນນີ້. ກະລຸນາຖອດສາຍສາກອອກ ແລະ ລະວັງເນື່ອງຈາກສາຍອາດຈະຍັງອຸ່ນຢູ່."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ເບິ່ງຂັ້ນຕອນການເບິ່ງແຍງ"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"ກົດຄ້າງໄວ້ເພື່ອຈັດຮຽງການຄວບຄຸມຄືນໃໝ່"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"ລຶບການຄວບຄຸມທັງໝົດອອກແລ້ວ"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ບໍ່ໄດ້ບັນທຶກການປ່ຽນແປງໄວ້"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ເບິ່ງແອັບອື່ນໆ"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"ບໍ່ສາມາດໂຫຼດການຄວບຄຸມໄດ້. ກວດສອບແອັບ <xliff:g id="APP">%s</xliff:g> ເພື່ອໃຫ້ແນ່ໃຈວ່າຍັງບໍ່ມີການປ່ຽນແປງການຕັ້ງຄ່າແອັບເທື່ອ."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"ບໍ່ມີການຄວບຄຸມທີ່ໃຊ້ຮ່ວມກັນທີ່ສາມາດໃຊ້ໄດ້"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ອື່ນໆ"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"ຢືນຢັນການປ່ຽນແປງສຳລັບ <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"ປັດເພື່ອເບິ່ງເພີ່ມເຕີມ"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ກຳລັງໂຫຼດຄຳແນະນຳ"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ປິດເຊດຊັນມີເດຍນີ້"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"ມີເດຍ"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"ເຊື່ອງເຊດຊັນປັດຈຸບັນ."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ເຊື່ອງ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ບໍ່ເຮັດວຽກ, ກະລຸນາກວດສອບແອັບ"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"ຜິດພາດ, ກໍາລັງ​ລອງ​ໃໝ່…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ບໍ່ພົບ"</string>
diff --git a/packages/SystemUI/res/values-lo/strings_tv.xml b/packages/SystemUI/res/values-lo/strings_tv.xml
index 7e7a4dded891..056612eb49e9 100644
--- a/packages/SystemUI/res/values-lo/strings_tv.xml
+++ b/packages/SystemUI/res/values-lo/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ສະແດງຜົນຫຼາຍຢ່າງພ້ອມກັນ"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(ໂປຣແກຣມບໍ່ມີຊື່)"</string>
- <string name="pip_close" msgid="5775212044472849930">"ປິດ PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"ເຕັມໜ້າຈໍ"</string>
<string name="mic_active" msgid="5766614241012047024">"ໄມໂຄຣໂຟນເປີດໃຊ້ຢູ່"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ເຂົ້າເຖິງໄມໂຄຣໂຟນຂອງທ່ານແລ້ວ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 4da26f89a6fd..5c8e8d9ac8fb 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -458,10 +458,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Palieskite dar kartą, kad atidarytumėte"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Perbraukite aukštyn, kad atidarytumėte"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Jei norite bandyti dar kartą, perbraukite aukštyn"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Šis įrenginys priklauso jūsų organizacijai"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Šis įrenginys priklauso „<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>“"</string>
<string name="phone_hint" msgid="6682125338461375925">"Perbraukite iš telefono piktogramos"</string>
<string name="voice_hint" msgid="7476017460191291417">"Perbraukite iš „Voice Assist“ piktogramos"</string>
<string name="camera_hint" msgid="4519495795000658637">"Perbraukite iš fotoaparato piktogramos"</string>
@@ -529,33 +527,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profilis gali būti stebimas"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Tinklas gali būti stebimas"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Tinklas gali būti stebimas"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Šis įrenginys priklauso jūsų organizacijai ir ji gali stebėti tinklo srautą"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Šis įrenginys priklauso „<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>“ ir ji gali stebėti tinklo srautą"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Šis įrenginys priklauso jūsų organizacijai ir yra susietas su „<xliff:g id="VPN_APP">%1$s</xliff:g>“"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Šis įrenginys priklauso „<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>“ ir yra susietas su „<xliff:g id="VPN_APP">%2$s</xliff:g>“"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Šis įrenginys priklauso jūsų organizacijai"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Šis įrenginys priklauso „<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>“"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Šis įrenginys priklauso jūsų organizacijai ir yra prijungtas prie VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Šis įrenginys priklauso „<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>“ ir yra prijungtas prie VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Jūsų organizacija darbo profilyje gali stebėti tinklo srautą"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"„<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>“ darbo profilyje gali stebėti tinklo srautą"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Tinklas gali būti stebimas"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Šis įrenginys prijungtas prie VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Darbo profilis susietas su „<xliff:g id="VPN_APP">%1$s</xliff:g>“"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Asmeninis profilis susietas su „<xliff:g id="VPN_APP">%1$s</xliff:g>“"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Šis įrenginys susietas su „<xliff:g id="VPN_APP">%1$s</xliff:g>“"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Įrenginio tvarkymas"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profilio stebėjimas"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Tinklo stebėjimas"</string>
@@ -565,10 +551,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Išjungti VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Atjungti VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Žr. politiką"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Šis įrenginys priklauso „<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>“.\n\nIT administratorius gali stebėti ir tvarkyti nustatymus, įmonės prieigą, programas, su įrenginiu susietus duomenis ir įrenginio vietovės informaciją.\n\nDaugiau informacijos galite gauti susisiekę su IT administratoriumi."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Šis įrenginys priklauso jūsų organizacijai.\n\nIT administratorius gali stebėti ir tvarkyti nustatymus, įmonės prieigą, programas, su įrenginiu susietus duomenis ir įrenginio vietovės informaciją.\n\nDaugiau informacijos galite gauti susisiekę su IT administratoriumi."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Jūsų organizacija įdiegė šiame įrenginyje sertifikato įgaliojimą. Jūsų saugaus tinklo srautas gali būti stebimas arba keičiamas."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jūsų organizacija įdiegė darbo profilyje sertifikato įgaliojimą. Jūsų saugaus tinklo srautas gali būti stebimas arba keičiamas."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Šiame įrenginyje įdiegtas sertifikato įgaliojimas. Jūsų saugaus tinklo srautas gali būti stebimas arba keičiamas."</string>
@@ -733,15 +717,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Tylūs"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Numatytasis"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Debesėlis"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatinis"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Neskamba ir nevibruoja"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Neskamba, nevibruoja ir rodoma apatinėje pokalbių skilties dalyje"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Gali skambėti arba vibruoti, atsižvelgiant į telefono nustatymus"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Gali skambėti arba vibruoti, atsižvelgiant į telefono nustatymus. Pokalbiai iš „<xliff:g id="APP_NAME">%1$s</xliff:g>“ debesėlio pagal numatytuosius nustatymus."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Naudojant slankųjį spartųjį klavišą lengviau sutelkti dėmesį į šį turinį."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nustatykite, kad sistema aptiktų, ar šis pranešimas turi skambėti, ar vibruoti"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Rodoma pokalbių skilties viršuje, rodoma kaip slankusis burbulas, pateikiama profilio nuotrauka užrakinimo ekrane"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Nustatymai"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritetiniai"</string>
@@ -762,18 +744,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ši programa rodoma ekrane virš kitų programų, ji naudoja mikrofoną ir fotoaparatą."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Nustatymai"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Gerai"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Sistema nutildė šį pranešimą."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Sistema paaukštino šį pranešimą."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Sistema pažemino šį pranešimą."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Ar tai teisinga?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Dėkojame už atsiliepimą!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Gerai"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai atidaryti"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai uždaryti"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Leisti pranešimus iš šio kanalo"</string>
@@ -950,26 +926,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Redaguoti nustatymų tvarką."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g> psl. iš <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Užrakinimo ekranas"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Išskleisti"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Sumažinti"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Uždaryti"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Nustatymai"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Nuvilkite žemyn, kad atsisakytumėte"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Meniu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> rodom. vaizdo vaizde"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Jei nenorite, kad „<xliff:g id="NAME">%s</xliff:g>“ naudotų šią funkciją, palietę atidarykite nustatymus ir išjunkite ją."</string>
- <string name="pip_play" msgid="333995977693142810">"Leisti"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pristabdyti"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Praleisti ir eiti į kitą"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Praleisti ir eiti į ankstesnį"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefonas išjungt., nes įkaito"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Dabar telefonas veikia įprastai"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefonas dabar veikia normaliai.\nPalietę gausite daugiau informacijos"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonas per daug įkaito, todėl buvo išj., kad atvėstų. Dabar telefonas veikia įprastai.\n\nTelefonas gali per daug įkaisti, jei:\n • esate įjungę daug išteklių naudoj. progr. (pvz., žaidimų, vaizdo įr. arba navig. progr.);\n • atsis. arba įkeliate didelius failus;\n • telefoną naudojate aukštoje temper."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Žr. priežiūros veiksmus"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefonas kaista"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Kai kurios funkcijos gali neveikti, kol telefonas vėsta"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Kai kurios funkcijos gali neveikti, kol telefonas vėsta.\nPalietę gausite daugiau informacijos"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonas automatiškai bandys atvėsti. Telefoną vis tiek galėsite naudoti, tačiau jis gali veikti lėčiau.\n\nKai telefonas atvės, jis veiks įprastai."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Žr. priežiūros veiksmus"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Atjunkite kroviklį"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Įkraunant šį įrenginį iškilo problema. Atjunkite maitinimo adapterį. Būkite atsargūs, nes laidas gali būti įkaitęs."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Žr. priežiūros veiksmus"</string>
@@ -1090,6 +1054,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Norėdami pertvarkyti valdiklius, vilkite laikydami nuspaudę"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Visi valdikliai pašalinti"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Pakeitimai neišsaugoti"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Žr. kitas programas"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Nepavyko įkelti valdiklių. Eikite į programą „<xliff:g id="APP">%s</xliff:g>“ ir įsitikinkite, kad programos nustatymai nepakeisti."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Suderinami valdikliai nepasiekiami"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Kita"</string>
@@ -1107,8 +1072,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Patvirtinti <xliff:g id="DEVICE">%s</xliff:g> pakeitimą"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Perbraukite, kad peržiūrėtumėte daugiau"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Įkeliamos rekomendacijos"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Uždaryti šį medijos seansą"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Medija"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Slėpti dabartinį seansą."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Slėpti"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Tęsti"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Nustatymai"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktyvu, patikrinkite progr."</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Klaida, bandoma iš naujo…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nerasta"</string>
diff --git a/packages/SystemUI/res/values-lt/strings_tv.xml b/packages/SystemUI/res/values-lt/strings_tv.xml
index cb0cb6c2845b..7739680c82b3 100644
--- a/packages/SystemUI/res/values-lt/strings_tv.xml
+++ b/packages/SystemUI/res/values-lt/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Vaizdas vaizde"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programa be pavadinimo)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Uždaryti PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Visas ekranas"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofonas aktyvus"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"„%1$s“ pasiekė jūsų mikrofoną"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 59a46daeba0a..cbe076896d9f 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -294,7 +294,7 @@
<string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Vairāk laika."</string>
<string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Mazāk laika."</string>
<string name="accessibility_quick_settings_flashlight_off" msgid="7606563260714825190">"Apgaismojums ir izslēgts."</string>
- <string name="accessibility_quick_settings_flashlight_unavailable" msgid="7458591827288347635">"Zibspuldze nav pieejama."</string>
+ <string name="accessibility_quick_settings_flashlight_unavailable" msgid="7458591827288347635">"Lukturītis nav pieejams."</string>
<string name="accessibility_quick_settings_flashlight_on" msgid="3785616827729850766">"Apgaismojums ir ieslēgts."</string>
<string name="accessibility_quick_settings_flashlight_changed_off" msgid="3782375441381402599">"Apgaismojums ir izslēgts."</string>
<string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"Apgaismojums ir ieslēgts."</string>
@@ -407,7 +407,7 @@
<item quantity="other">%d ierīces</item>
</plurals>
<string name="quick_settings_notifications_label" msgid="3379631363952582758">"Paziņojumi"</string>
- <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Zibspuldze"</string>
+ <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lukturītis"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera tiek lietota"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobilie dati"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"Datu lietojums"</string>
@@ -456,10 +456,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Pieskarieties vēlreiz, lai atvērtu"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Velciet augšup, lai atvērtu"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Velciet augšup, lai mēģinātu vēlreiz"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Šī ierīce pieder jūsu organizācijai."</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Šī ierīce pieder organizācijai <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
<string name="phone_hint" msgid="6682125338461375925">"Lai lietotu tālruni, velciet no ikonas"</string>
<string name="voice_hint" msgid="7476017460191291417">"Lai lietotu balss palīgu, velciet no ikonas"</string>
<string name="camera_hint" msgid="4519495795000658637">"Lai lietotu kameru, velciet no ikonas"</string>
@@ -526,33 +524,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profilu var pārraudzīt"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Iespējams, tīklā veiktās darbības tiek pārraudzītas."</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Var tikt pārraudzītas tīklā veiktās darbības."</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Šī ierīce pieder jūsu organizācijai, un jūsu organizācija var uzraudzīt tīkla datplūsmu."</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Šī ierīce pieder organizācijai<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>, un šī organizācija var uzraudzīt tīkla datplūsmu."</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Šī ierīce pieder jūsu organizācijai un ir saistīta ar: <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Šī ierīce pieder organizācijai <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> un ir savienota ar: <xliff:g id="VPN_APP">%2$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Šī ierīce pieder jūsu organizācijai."</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Šī ierīce pieder organizācijai <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Šī ierīce pieder jūsu organizācijai un ir savienota ar virtuālajiem privātajiem tīkliem."</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Šī ierīce pieder organizācijai<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> un ir savienota ar virtuālajiem privātajiem tīkliem."</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Jūsu organizācija var uzraudzīt jūsu darba profila tīkla datplūsmu."</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> var uzraudzīt jūsu profila tīkla datplūsmu."</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Var tikt pārraudzītas tīklā veiktās darbības."</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Šī ierīce ir saistīta ar virtuālajiem privātajiem tīkliem."</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Jūsu darba profils ir savienots ar: <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Jūsu personīgais profils ir saistīts ar: <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Šī ierīce ir savienota ar: <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Ierīces pārvaldība"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profila pārraudzība"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Tīkla pārraudzība"</string>
@@ -562,10 +548,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Atspējot VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Atvienot VPN tīklu"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Skatīt politikas"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Šī ierīce pieder organizācijai <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nJūsu IT administrators var pārraudzīt un pārvaldīt iestatījumus, korporatīvo piekļuvi, lietotnes, ar ierīci saistītos datus un ierīces atrašanās vietas informāciju.\n\nLai iegūtu plašāku informāciju, sazinieties ar IT administratoru."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Šī ierīce pieder jūsu organizācijai.\n\nJūsu IT administrators var pārraudzīt un pārvaldīt iestatījumus, korporatīvo piekļuvi, lietotnes, ar ierīci saistītos datus un ierīces atrašanās vietas informāciju.\n\nLai iegūtu plašāku informāciju, sazinieties ar IT administratoru."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Jūsu organizācija instalēja sertifikātu šajā ierīcē. Jūsu drošā tīkla datplūsma var tikt uzraudzīta."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jūsu organizācija instalēja sertifikātu jūsu darba profilā. Jūsu drošā tīkla datplūsma var tikt uzraudzīta."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Šajā ierīcē ir instalēts sertifikāts. Drošā tīkla datplūsma var tikt uzraudzīta."</string>
@@ -730,15 +714,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Klusums"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Noklusējums"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Burbulis"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automātiski"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Nav skaņas signāla vai vibrācijas"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nav skaņas signāla vai vibrācijas, kā arī atrodas zemāk sarunu sadaļā"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt. Sarunas no lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> pēc noklusējuma tiek parādītas burbulī."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Piesaista jūsu uzmanību, rādot peldošu saīsni uz šo saturu."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Iestatiet, lai sistēma noteiktu, vai šim paziņojumam būs skaņa vai vibrācija"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Parādās sarunu sadaļas augšdaļā un kā peldošs burbulis, kā arī bloķēšanas ekrānā tiek rādīts profila attēls"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Iestatījumi"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritārs"</string>
@@ -759,18 +741,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Šī lietotne tiek rādīta ekrānā pāri citām lietotnēm, un tajā tiek izmantots mikrofons un kamera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Iestatījumi"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Labi"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Šim paziņojumam tika izslēgta skaņa sistēmā."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Šis paziņojums tika paaugstināts sistēmā."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Šis paziņojums tika pazemināts sistēmā."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Vai šī informācija ir pareiza?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Paldies par atsauksmēm!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Labi"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumu vadīklas ir atvērtas"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumu vadīklas ir aizvērtas"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Atļaut paziņojumus no šī kanāla"</string>
@@ -945,26 +921,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Rediģēt iestatījumu secību."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. lpp. no <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Bloķēšanas ekrāns"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Izvērst"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizēt"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Aizvērt"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Iestatījumi"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Velciet lejup, lai noraidītu"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Izvēlne"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> ir attēlā attēlā"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ja nevēlaties lietotnē <xliff:g id="NAME">%s</xliff:g> izmantot šo funkciju, pieskarieties, lai atvērtu iestatījumus un izslēgtu funkciju."</string>
- <string name="pip_play" msgid="333995977693142810">"Atskaņot"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Apturēt"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Pāriet uz nākamo"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Pāriet uz iepriekšējo"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Tālrunis izslēgts karstuma dēļ"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Tagad jūsu tālrunis darbojas normāli"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Tagad jūsu tālrunis darbojas normāli.\nPieskarieties, lai uzzinātu vairāk."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Jūsu tālrunis bija pārkarsis un tika izslēgts. Tagad tas darbojas normāli.\n\nTālrunis var sakarst, ja:\n • tiek izmantotas lietotnes, kas patērē daudz enerģijas (piem., spēles, video lietotnes vai navigācija);\n • tiek lejupielādēti/augšupielādēti lieli faili;\n • tālrunis tiek lietots augstā temperatūrā."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Skatīt apkopes norādījumus"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Tālrunis kļūst silts"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Dažas funkcijas ir ierobežotas, kamēr tālrunis mēģina atdzist"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Dažas funkcijas ir ierobežotas, kamēr notiek tālruņa atdzišana.\nPieskarieties, lai uzzinātu vairāk."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Jūsu tālrunis automātiski mēģinās atdzist. Jūs joprojām varat izmantot tālruni, taču tas, iespējams, darbosies lēnāk.\n\nTiklīdz tālrunis būs atdzisis, tas darbosies normāli."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Skatīt apkopes norādījumus"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Lādētāja atvienošana"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Uzlādējot šo ierīci, radās problēma. Atvienojiet strāvas adapteri. Esiet uzmanīgs — vads var būt uzsilis."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Skatīt apkopes norādījumus"</string>
@@ -1084,6 +1048,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Lai pārkārtotu vadīklas, turiet un velciet tās"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Visas vadīklas ir noņemtas"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Izmaiņas nav saglabātas."</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Skatīt citas lietotnes"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Nevarēja ielādēt vadīklas. Lietotnē <xliff:g id="APP">%s</xliff:g> pārbaudiet, vai nav mainīti lietotnes iestatījumi."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Nav pieejamas saderīgas vadīklas"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Cita"</string>
@@ -1101,8 +1066,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Izmaiņu apstiprināšana ierīcei <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Velciet, lai skatītu citus vienumus"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Notiek ieteikumu ielāde"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Aizvērt multivides sesiju"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Multivide"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Paslēpiet pašreizējo sesiju."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Paslēpt"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Atsākt"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Iestatījumi"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktīva, pārbaudiet lietotni"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Radās kļūda. Mēģina vēlreiz…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Netika atrasta"</string>
diff --git a/packages/SystemUI/res/values-lv/strings_tv.xml b/packages/SystemUI/res/values-lv/strings_tv.xml
index e08b0ea4379f..6b841d2c244b 100644
--- a/packages/SystemUI/res/values-lv/strings_tv.xml
+++ b/packages/SystemUI/res/values-lv/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Attēls attēlā"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programma bez nosaukuma)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Aizvērt PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Pilnekrāna režīms"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofons ir aktīvs"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Lietotne %1$s piekļuva jūsu mikrofonam"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index f09c2cdca4cf..5a000de3e843 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Допрете повторно за да се отвори"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Повлечете за да отворите"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Повлечете нагоре за да се обидете повторно"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Уредов е во сопственост на организацијата"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Уредов е во сопственост на <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Повлечете од иконата за телефонот"</string>
<string name="voice_hint" msgid="7476017460191291417">"Повлечете од иконата за гласовна помош"</string>
<string name="camera_hint" msgid="4519495795000658637">"Повлечете од иконата за камерата"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Профилот можеби се следи"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Мрежата може да се следи"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Мрежата може да се следи"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Организацијата е сопственик на уредов и може да го следи мрежниот сообраќај"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> е сопственик на уредов и може да го следи мрежниот сообраќај"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Уредов е во сопственост на организацијата и е поврзан со <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Уредов е во сопственост на <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> и е поврзан со <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Уредов е во сопственост на организацијата"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Уредов е во сопственост на <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Уредов е во сопственост на организацијата и е поврзан со VPN-мрежи"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Уредов е во сопственост на <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> и е поврзан со VPN-мрежи"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Вашата организација може да го следи мрежниот сообраќај на вашиот работен профил"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> може да го следи мрежниот сообраќај на вашиот работен профил"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Мрежата можеби се следи"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Уредов е поврзан со VPN-мрежи"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Вашиот работен профил е поврзан со <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Вашиот личен профил е поврзан со <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Уредов е поврзан со <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Управување со уреди"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Следење профил"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Следење на мрежата"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Оневозможи ВПН"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Исклучи ВПН"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Прикажи „Политики“"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Уредов е во сопственост на <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nIT-администраторот може да ги следи и да управува со поставките, корпоративниот пристап, апликациите, податоците поврзани со уредот и податоците за локацијата на уредот.\n\nЗа повеќе информации, контактирајте со IT-администраторот."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Уредов е во сопственост на организацијата.\n\nIT-администраторот може да ги следи и да управува со поставките, корпоративниот пристап, апликациите, податоците поврзани со уредот и податоците за локацијата на уредот.\n\nЗа повеќе информации, контактирајте со IT-администраторот."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Вашата организација инсталираше авторитет за сертификат на уредов. Сообраќајот на вашата безбедна мрежа можно е да се следи или изменува."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Вашата организација инсталираше авторитет за сертификат на вашиот работен профил. Вашиот безбеден мрежен сообраќај можно е да се следи или изменува."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На уредов е инсталиран авторитет за сертификат. Вашиот безбеден мрежен сообраќај можно е да се следи или изменува."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Безгласно"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Стандардно"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Балонче"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува под делот за разговор"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да ѕвони или вибрира во зависност од поставките на телефонот"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да ѕвони или вибрира во зависност од поставките на телефонот Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Ви го задржува вниманието со лебдечка кратенка на содржинава."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Се појавува на горниот дел од секцијата на разговорот во вид на лебдечко меурче, покажувајќи ја профилната слика на заклучениот екран"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Поставки"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Апликацијава се прикажува врз други апликации на вашиот екран и ги користи микрофонот и камерата."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Поставки"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Во ред"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Системот го стиши известувањево."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Системот ја зголеми приоритетноста на известувањево."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Системот ја намали приоритетноста на известувањево."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Дали ова беше точно?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Фала за повратните информации!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Во ред"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Контролите за известувањата за <xliff:g id="APP_NAME">%1$s</xliff:g> се отворија"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Контролите за известувањата за <xliff:g id="APP_NAME">%1$s</xliff:g> се затворија"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Дозволете известувања од овој канал"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Уредете го редоследот на поставките."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Страница <xliff:g id="ID_1">%1$d</xliff:g> од <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Заклучен екран"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Проширете"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Минимизирај"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Затвори"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Поставки"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Повлечете надолу за да отфрлите"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Мени"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> е во слика во слика"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ако не сакате <xliff:g id="NAME">%s</xliff:g> да ја користи функцијава, допрете за да ги отворите поставките и да ја исклучите."</string>
- <string name="pip_play" msgid="333995977693142810">"Пушти"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Паузирај"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Прескокни до следната"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Прескокни до претходната"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Телефонот се исклучи поради загреаност"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Сега телефонот работи нормално"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Сега телефонот работи нормално.\nДопрете за повеќе информации"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Телефонот беше премногу загреан, така што се исклучи за да се олади. Сега работи нормално.\n\nТелефонот може премногу да се загрее ако:\n • користите апликации што работат со многу ресурси (како што се, на пример, апликациите за видеа, навигација или игри)\n • преземате или поставувате големи датотеки\n •го користите телефонот на високи температури"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Прикажи ги чекорите за грижа за уредот"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Телефонот се загрева"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Некои функции се ограничени додека телефонот се лади"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Некои функции се ограничени додека телефонот се лади.\nДопрете за повеќе информации"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефонот автоматски ќе се обиде да се олади. Вие сепак ќе може да го користите, но тој може да работи побавно.\n\nОткако ќе се олади, ќе работи нормално."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Прикажи ги чекорите за грижа за уредот"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Исклучете го полначот"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Има проблем со полнењето на уредов. Исклучете го адаптерот за напојување и внимавајте зошто кабелот може да е топол."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Прикажи ги чекорите за грижа за уредот"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Задржете и влечете за да ги преуредите контролите"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Сите контроли се отстранети"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Промените не се зачувани"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Видете други апликации"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Контролите не можеше да се вчитаат. Проверете ја апликацијата <xliff:g id="APP">%s</xliff:g> за да се уверите дека поставките за апликацијата не се променети."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Нема компатибилни контроли"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Друга"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Потврдете ја промената за <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Повлечете за да видите повеќе"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Се вчитуваат препораки"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Затвори ја аудиовизуелнава сесија"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Аудиовизуелни содржини"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Сокриј ја тековнава сесија."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Сокриј"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Продолжи"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Поставки"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивна, провери апликација"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Грешка, повторен обид…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не е најдено"</string>
diff --git a/packages/SystemUI/res/values-mk/strings_tv.xml b/packages/SystemUI/res/values-mk/strings_tv.xml
index b4de2156155c..a935cc47b6cf 100644
--- a/packages/SystemUI/res/values-mk/strings_tv.xml
+++ b/packages/SystemUI/res/values-mk/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Слика во слика"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Програма без наслов)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Затвори PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Цел екран"</string>
<string name="mic_active" msgid="5766614241012047024">"Микрофонот е активен"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s пристапи до вашиот микрофон"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index b5adca2e0a9d..22392279a919 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"തുറക്കുന്നതിന് വീണ്ടും ടാപ്പുചെയ്യുക"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"തുറക്കാൻ മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്യുക"</string>
<string name="keyguard_retry" msgid="886802522584053523">"വീണ്ടും ശ്രമിക്കാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റേതാണ്"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ഈ ഉപകരണം <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> എന്ന സ്ഥാപനത്തിന്റേതാണ്"</string>
<string name="phone_hint" msgid="6682125338461375925">"ഫോൺ ഐക്കണിൽ നിന്ന് സ്വൈപ്പുചെയ്യുക"</string>
<string name="voice_hint" msgid="7476017460191291417">"വോയ്‌സ് അസിസ്റ്റിനായുള്ള ഐക്കണിൽ നിന്ന് സ്വൈപ്പുചെയ്യുക"</string>
<string name="camera_hint" msgid="4519495795000658637">"ക്യാമറ ഐക്കണിൽ നിന്ന് സ്വൈപ്പുചെയ്യുക"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"പ്രൊഫൈൽ നിരീക്ഷിക്കപ്പെടാം"</string>
<string name="vpn_footer" msgid="3457155078010607471">"നെറ്റ്‌വർക്ക് നിരീക്ഷിക്കപ്പെടാം"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"നെറ്റ്‌വർക്ക് നിരീക്ഷിക്കപ്പെടാം"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റെ ഉടമസ്ഥതയിലായതിനാൽ നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിച്ചേക്കാം"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"ഈ ഉപകരണം <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> എന്ന സ്ഥാപനത്തിന്റെ ഉടമസ്ഥതയിലായതിനാൽ നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിച്ചേക്കാം"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റേതാണ്, കൂടാതെ <xliff:g id="VPN_APP">%1$s</xliff:g> എന്നതിലേക്ക് കണക്റ്റ് ചെയ്‌തിരിക്കുന്നു"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ഈ ഉപകരണം <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> എന്ന സ്ഥാപനത്തിന്റേതാണ്, കൂടാതെ <xliff:g id="VPN_APP">%2$s</xliff:g> എന്നതിലേക്ക് കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റേതാണ്"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ഈ ഉപകരണം <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> എന്ന സ്ഥാപനത്തിന്റേതാണ്"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റേതാണ്, കൂടാതെ VPN-കളിലേക്ക് കണക്റ്റ് ചെയ്‌തിരിക്കുന്നു"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ഈ ഉപകരണം <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> എന്ന സ്ഥാപനത്തിന്റേതാണ്, കൂടാതെ VPN-കളിലേക്ക് കണക്റ്റ് ചെയ്‌തിരിക്കുന്നു"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിലെ നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കാൻ നിങ്ങളുടെ സ്ഥാപനത്തിന് കഴിഞ്ഞേക്കാം"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിലെ നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> നിരീക്ഷിച്ചേക്കാം"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"നെറ്റ്‌വർക്ക് നിരീക്ഷിക്കപ്പെടാം"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ഈ ഉപകരണം VPN-കളിലേക്ക് കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"<xliff:g id="VPN_APP">%1$s</xliff:g> എന്നതിലേക്ക് നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ കണക്റ്റ് ചെയ്‌തിരിക്കുന്നു"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"നിങ്ങളുടെ വ്യക്തിപരമായ പ്രൊഫൈൽ <xliff:g id="VPN_APP">%1$s</xliff:g> ആപ്പിലേക്ക് കണക്റ്റ് ചെയ്‌തിരിക്കുന്നു"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ഈ ഉപകരണം <xliff:g id="VPN_APP">%1$s</xliff:g> എന്നതിലേക്ക് കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ഉപകരണ മാനേജ്‌മെന്റ്"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"പ്രൊഫൈൽ നിരീക്ഷിക്കൽ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"നെറ്റ്‌വർക്ക് നിരീക്ഷിക്കൽ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN പ്രവർത്തനരഹിതമാക്കുക"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN വിച്‌ഛേദിക്കുക"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"നയങ്ങൾ കാണുക"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ഈ ഉപകരണം <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>എന്ന സ്ഥാപനത്തിന്റേതാണ്.\n\nക്രമീകരണം, കോർപ്പറേറ്റ് ആക്‌സസ്, ആപ്പുകൾ, നിങ്ങളുടെ ഉപകരണവുമായി ബന്ധപ്പെട്ട ഡാറ്റ, ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ‌വിവരങ്ങൾ എന്നിവ നിരീക്ഷിക്കാനും മാനേജ് ചെയ്യാനും നിങ്ങളുടെ ഐടി അഡ്‌മിന് കഴിയും.\n\nകൂടുതൽ വിവരങ്ങൾക്ക് നിങ്ങളുടെ ഐടി അഡ്‌മിനെ ബന്ധപ്പെടുക."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റേതാണ്.\n\nക്രമീകരണം, കോർപ്പറേറ്റ് ആക്‌സസ്, ആപ്പുകൾ, നിങ്ങളുടെ ഉപകരണവുമായി ബന്ധപ്പെട്ട ഡാറ്റ, ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ‌വിവരങ്ങൾ എന്നിവ നിരീക്ഷിക്കാനും മാനേജ് ചെയ്യാനും നിങ്ങളുടെ ഐടി അഡ്‌മിന് കഴിയും.\n\nകൂടുതൽ വിവരങ്ങൾക്ക് നിങ്ങളുടെ ഐടി അഡ്‌മിനെ ബന്ധപ്പെടുക."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ഈ ഉപകരണത്തിൽ നിങ്ങളുടെ സ്ഥാപനമൊരു സർട്ടിഫിക്കറ്റ് അതോറിറ്റി ഇൻസ്റ്റാൾ ചെയ്തിരിക്കുന്നു. നിങ്ങളുടെ സുരക്ഷിത നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കപ്പെടുകയോ പരിഷ്കരിക്കപ്പെടുയോ ചെയ്തേക്കാം."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിൽ നിങ്ങളുടെ സ്ഥാപനമൊരു സർട്ടിഫിക്കറ്റ് അതോറിറ്റി ഇൻസ്റ്റാൾ ചെയ്തിരിക്കുന്നു. നിങ്ങളുടെ സുരക്ഷിത നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കപ്പെടുകയോ പരിഷ്കരിക്കപ്പെടുയോ ചെയ്തേക്കാം."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു സർട്ടിഫിക്കറ്റ് അതോറിറ്റി ഇൻസ്റ്റാൾ ചെയ്തിരിക്കുന്നു. നിങ്ങളുടെ സുരക്ഷിത നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കപ്പെടുകയോ പരിഷ്കരിക്കപ്പെടുയോ ചെയ്തേക്കാം."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"നിശബ്‌ദം"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ഡിഫോൾട്ട്"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"ബബ്ൾ"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"സ്വയമേവ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ല"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ശബ്‌ദമോ വൈബ്രേഷനോ ഇല്ല, സംഭാഷണ വിഭാഗത്തിന് താഴെയായി ദൃശ്യമാകും"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ഫോൺ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ് ചെയ്‌തേക്കാം അല്ലെങ്കിൽ വൈബ്രേറ്റ് ചെയ്‌തേക്കാം"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ഫോൺ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ് ചെയ്‌തേക്കാം അല്ലെങ്കിൽ വൈബ്രേറ്റ് ചെയ്‌തേക്കാം. <xliff:g id="APP_NAME">%1$s</xliff:g>-ൽ നിന്നുള്ള സംഭാഷണങ്ങൾ ഡിഫോൾട്ടായി ബബ്ൾ ആവുന്നു."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ഈ ഉള്ളടക്കത്തിലേക്ക് ഒരു ഫ്ലോട്ടിംഗ് കുറുക്കുവഴി ഉപയോഗിച്ച് നിങ്ങളുടെ ശ്രദ്ധ നിലനിർത്തുന്നു."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ഈ അറിയിപ്പ് വരുമ്പോൾ ശബ്‌ദിക്കുകയാണോ വൈബ്രേറ്റ് ചെയ്യുകയാണോ വേണ്ടതെന്ന് നിർണ്ണയിക്കാൻ സിസ്‌റ്റത്തെ അനുവദിക്കുക"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"സംഭാഷണ വിഭാഗത്തിന് മുകളിലായി കാണിക്കുന്നു, ഫ്ലോട്ടിംഗ് ബബിളായി ദൃശ്യമാകുന്നു, ലോക്ക് സ്ക്രീനിൽ പ്രൊഫൈൽ ചിത്രം പ്രദർശിപ്പിക്കുന്നു"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ക്രമീകരണം"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"മുൻഗണന"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ഈ ആപ്പ് നിങ്ങളുടെ സ്‌ക്രീനിലെ മറ്റ് ആപ്പുകൾക്ക് മുകളിൽ പ്രദർശിപ്പിക്കുന്നു, മൈക്രോഫോണും ക്യാമറയും ഉപയോഗിക്കുകയും ചെയ്യുന്നു."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ക്രമീകരണം"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ശരി"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ഈ അറിയിപ്പ്, സിസ്റ്റം നിശബ്‌ദമാക്കിയിരിക്കുന്നു."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ഈ അറിയിപ്പ്, സിസ്റ്റം പ്രമോട്ട് ചെയ്തിരിക്കുന്നു."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ഈ അറിയിപ്പ്, സിസ്റ്റം താഴേക്ക് മാറ്റിയിരിക്കുന്നു."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"ഇത് ശരിയായിരുന്നോ?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"നിങ്ങളുടെ ഫീഡ്‌ബാക്കിന് നന്ദി!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ശരി"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിന്റെ അറിയിപ്പ് നിയന്ത്രണങ്ങൾ തുറന്നു"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിന്റെ അറിയിപ്പ് നിയന്ത്രണങ്ങൾ അടച്ചു"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"ഈ ചാനലിൽ നിന്നുള്ള അറിയിപ്പുകൾ അനുവദിക്കുക"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ക്രമീകരണ ക്രമം എഡിറ്റുചെയ്യുക."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"പേജ് <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ലോക്ക് സ്‌ക്രീൻ"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"വികസിപ്പിക്കുക"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"ചെറുതാക്കുക‍"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"അവസാനിപ്പിക്കുക"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ക്രമീകരണം"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"തള്ളിക്കളയാൻ താഴേക്ക് വലിച്ചിടുക"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"മെനു"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> ചിത്രത്തിനുള്ളിൽ ചിത്രം രീതിയിലാണ്"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> ഈ ഫീച്ചർ ഉപയോഗിക്കേണ്ടെങ്കിൽ, ടാപ്പ് ചെയ്‌ത് ക്രമീകരണം തുറന്ന് അത് ഓഫാക്കുക."</string>
- <string name="pip_play" msgid="333995977693142810">"പ്ലേ ചെയ്യുക"</string>
- <string name="pip_pause" msgid="1139598607050555845">"താൽക്കാലികമായി നിർത്തുക"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"അടുത്തതിലേക്ക് പോകുക"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"മുമ്പത്തേതിലേക്ക് പോകുക"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"ചൂട് കൂടിയതിനാൽ ഫോൺ ഓഫാക്കി"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"ഫോൺ ഇപ്പോൾ സാധാരണഗതിയിൽ പ്രവർത്തിക്കുന്നു"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"നിങ്ങളുടെ ഫോൺ ഇപ്പോൾ സാധാരണ ഗതിയിൽ പ്രവർത്തിക്കുന്നു.\nകൂടുതൽ വിവരങ്ങൾക്ക് ടാപ്പ് ചെയ്യുക"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"ഫോൺ ചൂടായിരിക്കുന്നതിനാൽ തണുക്കാൻ ഓഫാക്കിയിരിക്കുന്നു. ഫോൺ ഇപ്പോൾ സാധാരണഗതിയിൽ പ്രവർത്തിക്കുന്നു.\n\nഫോണിന് ചൂട് കൂടാൻ കാരണം:\n • ഗെയിമിംഗ്, വീഡിയോ അല്ലെങ്കിൽ നാവിഗേഷൻ തുടങ്ങിയ റിസോഴ്സ്-ഇന്റൻസീവായ ആപ്പുകൾ ഉപയോഗിക്കുന്നത്\n • വലിയ ഫയലുകൾ അപ്‌ലോഡോ ഡൗൺലോഡോ ചെയ്യുന്നത്\n • ഉയർന്ന താപനിലയിൽ ഫോൺ ഉപയോഗിക്കുന്നത്"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"പരിപാലന നിർദ്ദേശങ്ങൾ കാണുക"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ഫോൺ ചൂടായിക്കൊണ്ടിരിക്കുന്നു"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ഫോൺ തണുത്തുകൊണ്ടിരിക്കുമ്പോൾ ചില ഫീച്ചറുകൾ പരിമിതപ്പെടുത്തപ്പെടും"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ഫോൺ തണുത്തുകൊണ്ടിരിക്കുമ്പോൾ ചില ഫീച്ചറുകൾ പരിമിതപ്പെടുത്തപ്പെടും.\nകൂടുതൽ വിവരങ്ങൾക്ക് ടാപ്പ് ചെയ്യുക"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"നിങ്ങളുടെ ഫോൺ സ്വയമേവ തണുക്കാൻ ശ്രമിക്കും. നിങ്ങൾക്ക് അപ്പോഴും ഫോൺ ഉപയോഗിക്കാമെങ്കിലും പ്രവർത്തനം മന്ദഗതിയിലായിരിക്കും.\n\nതണുത്തുകഴിഞ്ഞാൽ, ഫോൺ സാധാരണ ഗതിയിൽ പ്രവർത്തിക്കും."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"പരിപാലന നിർദ്ദേശങ്ങൾ കാണുക"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ചാർജർ അൺപ്ലഗ് ചെയ്യുക"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"ഈ ഉപകരണം ചാർജ് ചെയ്യുന്നതിൽ തടസ്സമുണ്ട്. പവർ അഡാപ്റ്റർ അൺപ്ലഗ് ചെയ്യുക, കേബിളിന് ചൂടുണ്ടായിരിക്കുമെന്നതിനാൽ ശ്രദ്ധിക്കണം."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"മുൻകരുതൽ നടപടികൾ കാണുക"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"നിയന്ത്രണങ്ങൾ പുനഃക്രമീകരിക്കാൻ അമർത്തിപ്പിടിച്ച് വലിച്ചിടുക"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"എല്ലാ നിയന്ത്രണങ്ങളും നീക്കം ചെയ്തു"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"മാറ്റങ്ങൾ സംരക്ഷിച്ചിട്ടില്ല"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"മറ്റ് ആപ്പുകൾ കാണുക"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"നിയന്ത്രണങ്ങൾ ലോഡ് ചെയ്യാനായില്ല. ആപ്പ് ക്രമീകരണം മാറ്റിയിട്ടില്ലെന്ന് ഉറപ്പാക്കാൻ <xliff:g id="APP">%s</xliff:g> ആപ്പ് പരിശോധിക്കുക."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"അനുയോജ്യമായ നിയന്ത്രണങ്ങൾ ലഭ്യമല്ല"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"മറ്റുള്ളവ"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> എന്നതിനുള്ള മാറ്റം സ്ഥിരീകരിക്കുക"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"കൂടുതൽ കാണാൻ സ്വൈപ്പ് ചെയ്യുക"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"നിർദ്ദേശങ്ങൾ ലോഡ് ചെയ്യുന്നു"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ഈ മീഡിയ സെഷൻ അടയ്ക്കുക"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"മീഡിയ"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"നിലവിലെ സെഷൻ മറയ്‌ക്കുക."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"മറയ്‌ക്കുക"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"പുനരാരംഭിക്കുക"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ക്രമീകരണം"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"നിഷ്‌ക്രിയം, ആപ്പ് പരിശോധിക്കൂ"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"പിശക്, വീണ്ടും ശ്രമിക്കുന്നു…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"കണ്ടെത്തിയില്ല"</string>
diff --git a/packages/SystemUI/res/values-ml/strings_tv.xml b/packages/SystemUI/res/values-ml/strings_tv.xml
index bf925c454f6d..97843376a364 100644
--- a/packages/SystemUI/res/values-ml/strings_tv.xml
+++ b/packages/SystemUI/res/values-ml/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ചിത്രത്തിനുള്ളിൽ ചിത്രം"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP അടയ്ക്കുക"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"പൂര്‍ണ്ണ സ്ക്രീന്‍"</string>
<string name="mic_active" msgid="5766614241012047024">"മൈക്രോഫോൺ സജീവമാണ്"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s, നിങ്ങളുടെ മൈക്രോഫോൺ ആക്‌സസ് ചെയ്‌തു"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 3d2308423455..8141ef3a1fc6 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Нээхийн тулд дахин товшино уу"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Нээхийн тулд дээш шударна уу"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Дахин оролдохын тулд дээш шударна уу"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Энэ төхөөрөмж танай байгууллагад харьяалагддаг"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Энэ төхөөрөмж <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>-д харьяалагддаг"</string>
<string name="phone_hint" msgid="6682125338461375925">"Утсыг гаргахын тулд дүрс тэмдгээс шудрах"</string>
<string name="voice_hint" msgid="7476017460191291417">"Дуут туслахыг нээхийн тулд дүрс тэмдгээс шудрах"</string>
<string name="camera_hint" msgid="4519495795000658637">"Камер нээхийн тулд дүрс тэмдгийг шудрах"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Профайлыг хянаж байж болзошгүй"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Сүлжээ хянагдаж байж болзошгүй"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Сүлжээг хянаж байж болзошгүй"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Танай байгууллага энэ төхөөрөмжийг эзэмшдэг бөгөөд сүлжээний ачааллыг хянаж болно"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> энэ төхөөрөмжийг эзэмшдэг бөгөөд сүлжээний ачааллыг хянаж болно"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Энэ төхөөрөмж танай байгууллагад харьяалагддаг бөгөөд <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Энэ төхөөрөмж <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-д харьяалагддаг бөгөөд <xliff:g id="VPN_APP">%2$s</xliff:g>-д холбогдсон байна"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Энэ төхөөрөмж танай байгууллагад харьяалагддаг"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Энэ төхөөрөмж <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-д харьяалагддаг"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Энэ төхөөрөмж танай байгууллагад харьяалагддаг бөгөөд VPN-д холбогдсон байна"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Энэ төхөөрөмж <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-д харьяалагддаг бөгөөд VPN-д холбогдсон байна"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Таны байгууллага таны ажлын профайлын сүлжээний ачааллыг хянадаг"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> таны ажлын профайлын сүлжээний ачааллыг хянадаг"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Сүлжээг хянаж байж болзошгүй"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Энэ төхөөрөмж VPN-д холбогдсон байна"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Таны ажлын профайл <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Таны хувийн профайл <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Энэ төхөөрөмж <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Төхөөрөмжийн удирдлага"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Профайл хяналт"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Сүлжээний хяналт"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN идэвхгүйжүүлэх"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN таслах"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Удирдамж харах"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Энэ төхөөрөмж <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-д харьяалагддаг.\n\nТанай IT админ тохиргоо, байгууллагын хандалт, аппууд, таны төхөөрөмжтэй холбоотой өгөгдөл, таны төхөөрөмжийн байршлын мэдээллийг хянах, удирдах боломжтой.\n\nНэмэлт мэдээлэл авахын тулд IT админтайгаа холбогдоно уу."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Энэ төхөөрөмж танай байгууллагад харьяалагддаг.\n\nТанай IT админ тохиргоо, байгууллагын хандалт, аппууд, таны төхөөрөмжтэй холбоотой өгөгдөл, таны төхөөрөмжийн байршлын мэдээллийг хянах, удирдах боломжтой.\n\nНэмэлт мэдээлэл авахын тулд IT админтайгаа холбогдоно уу."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Таны байгууллага энэ төхөөрөмжид сертификатын зөвшөөрлийг суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Таны байгууллага таны ажлын профайлд сертификатын зөвшөөрөл суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Сертификатын зөвшөөрлийг энэ төхөөрөмжид суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Чимээгүй"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Өгөгдмөл"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Бөмбөлөг"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Автомат"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Дуу эсвэл чичиргээ байхгүй"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дуу эсвэл чичиргээ байхгүй бөгөөд харицан ярианы хэсгийн доод талд харагдана"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй. <xliff:g id="APP_NAME">%1$s</xliff:g>-н харилцан яриаг өгөгдмөл тохиргооны дагуу бөмбөлөг болгоно."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Энэ контентын хөвөн гарч ирэх товчлолтойгоор таны анхаарлыг татдаг."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Энэ мэдэгдэл дуу гаргах эсвэл чичрэх эсэхийг системээр тодорхойлуулаарай"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Харилцан ярианы хэсгийн дээд талд хөвж буй бөмбөлөг хэлбэрээр харагдах бөгөөд профайлын зургийг түгжигдсэн дэлгэцэд үзүүлнэ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Тохиргоо"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Ач холбогдол"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Энэ аппыг таны дэлгэцэд бусад аппын дээр харуулж байгаа бөгөөд микрофон болон камерыг ашиглаж байна."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Тохиргоо"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ОК"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Систем энэ мэдэгдлийг дуугүй болгосон."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Систем энэ мэдэгдлийн зэргийг дэвшүүлсэн."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Систем энэ мэдэгдлийн зэргийг бууруулсан."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Энэ зөв байсан уу?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Санал хүсэлтээ илгээсэнд баярлалаа!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ОК"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н мэдэгдлийн хяналтыг нээсэн"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н мэдэгдлийн хяналтыг хаасан"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Энэ сувгийн мэдэгдлийг зөвшөөрөх"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Тохиргооны дарааллыг өөрчилнө үү."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>-н <xliff:g id="ID_1">%1$d</xliff:g>-р хуудас"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Түгжигдсэн дэлгэц"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Дэлгэх"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Багасгах"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Хаах"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Тохиргоо"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Хаахын тулд доош чирэх"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Цэс"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> дэлгэцэн доторх дэлгэцэд байна"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Та <xliff:g id="NAME">%s</xliff:g>-д энэ онцлогийг ашиглуулахыг хүсэхгүй байвал тохиргоог нээгээд, үүнийг унтраана уу."</string>
- <string name="pip_play" msgid="333995977693142810">"Тоглуулах"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Түр зогсоох"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Дараагийн медиад очих"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Өмнөх медиад очих"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Халснаас үүдэн утас унтарсан"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Таны утас одоо хэвийн ажиллаж байна"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Таны утас одоо хэвийн ажиллаж байна.\nДэлгэрэнгүй мэдээлэл авах бол товшино уу"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Таны утас хэт халсан тул хөргөхөөр унтраасан болно. Таны утас одоо хэвийн ажиллаж байна.\n\nХэрэв та дараахыг хийвэл таны утас хэт халж болзошгүй:\n • Их хэмжээний нөөц хэрэглээний апп (тоглоом, видео эсвэл шилжилтийн апп зэрэг)\n • Багтаамж ихтэй файл татах, байршуулах\n • Утсаа өндөр температурт ашиглах"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Хянамж болгоомжийн алхмыг харах"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Утас халж эхэлж байна"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Таны утас хөрж байх зуур зарим онцлогийг хязгаарласан"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Утсыг хөрөх үед зарим онцлогийг хязгаарлана.\nДэлгэрэнгүй мэдээлэл авах бол товшино уу"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Таны утас автоматаар хөрөх болно. Та утсаа ашиглаж болох хэдий ч удаан ажиллаж болзошгүй.\n\nТаны утас хөрсний дараагаар хэвийн ажиллана."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Хянамж болгоомжийн алхмыг харах"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Цэнэглэгчийг салгана уу"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Энэ төхөөрөмжийг цэнэглэхэд асуудал гарлаа. Тэжээлийн залгуурыг салгана уу. Кабель халсан байж болзошгүй тул болгоомжтой байгаарай."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Хянамж болгоомжийн алхмыг харна уу"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Хяналтуудыг дахин засварлахын тулд дараад чирнэ үү"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Бүх хяналтыг хассан"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Өөрчлөлтийг хадгалаагүй"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Бусад аппыг харах"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Хяналтыг ачаалж чадсангүй. Аппын тохиргоог өөрчлөөгүй эсэхийг нягтлахын тулд <xliff:g id="APP">%s</xliff:g> аппыг шалгана уу."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Тохирох хяналт байхгүй"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Бусад"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g>-н өөрчлөлтийг баталгаажуулна уу"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Илүү ихийг харахын тулд шударна уу"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Зөвлөмжүүдийг ачаалж байна"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Медианы энэ харилцан үйлдлийг хаах"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Одоогийн харилцан үйлдлийг нуугаарай."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Нуух"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Үргэлжлүүлэх"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Тохиргоо"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Идэвхгүй байна, аппыг шалгана уу"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Алдаа, дахин оролдож байна…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Олдсонгүй"</string>
diff --git a/packages/SystemUI/res/values-mn/strings_tv.xml b/packages/SystemUI/res/values-mn/strings_tv.xml
index 6eb4449a9796..3a5ff747a01e 100644
--- a/packages/SystemUI/res/values-mn/strings_tv.xml
+++ b/packages/SystemUI/res/values-mn/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Дэлгэцэн-доторх-Дэлгэц"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Гарчиггүй хөтөлбөр)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP-г хаах"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Бүтэн дэлгэц"</string>
<string name="mic_active" msgid="5766614241012047024">"Микрофон идэвхтэй байна"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s нь таны микрофонд хандcан байна"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 682d881084bc..79b7ae8b12a5 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"उघडण्यासाठी पुन्हा टॅप करा"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"उघडण्यासाठी वर स्वाइप करा"</string>
<string name="keyguard_retry" msgid="886802522584053523">"पुन्हा प्रयत्न करण्यासाठी वर स्‍वाइप करा"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"हे डिव्हाइस तुमच्या संस्थेचे आहे"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> चे आहे"</string>
<string name="phone_hint" msgid="6682125338461375925">"फोनसाठी चिन्हावरून स्वाइप करा"</string>
<string name="voice_hint" msgid="7476017460191291417">"व्हॉइस सहाय्यासाठी चिन्हावरून स्वाइप करा"</string>
<string name="camera_hint" msgid="4519495795000658637">"कॅमेर्‍यासाठी चिन्हावरून स्वाइप करा"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"प्रोफाईलचे परीक्षण केले जाऊ शकते"</string>
<string name="vpn_footer" msgid="3457155078010607471">"नेटवर्कचे परीक्षण केले जाऊ शकते"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"नेटवर्कचे परीक्षण केले जाऊ शकते"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"तुमच्‍या संस्‍थेकडे या डिव्हाइसची मालकी आहे आणि ती नेटवर्क ट्रॅफिकचे परीक्षण करू शकते"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> च्या मालकीचे आहे आणि ती नेटवर्क ट्रॅफिकचे परीक्षण करू शकते"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"हे डिव्हाइस तुमच्या संस्थेचे आहे आणि ते <xliff:g id="VPN_APP">%1$s</xliff:g> ला कनेक्ट केले आहे"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> चे आहे आणि ते <xliff:g id="VPN_APP">%2$s</xliff:g> ला कनेक्ट केले आहे"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"हे डिव्हाइस तुमच्या संस्थेचे आहे"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> चे आहे"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"हे डिव्हाइस तुमच्या संस्थेचे आहे आणि ते VPN ना कनेक्ट केले आहे"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> चे आहे आणि ते VPN ना कनेक्ट केले आहे"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"तुमची संस्था आपल्या कार्य प्रोफाइलमधील नेटवर्क रहदारीचे परीक्षण करू शकते"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> आपल्या कार्य प्रोफाइलमधील नेटवर्क रहदारीचे परीक्षण करू शकते"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"नेटवर्कचे परीक्षण केले जाऊ शकते"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"हे डिव्हाइस VPN ला कनेक्ट केले आहे"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"तुमची कार्य प्रोफाइल <xliff:g id="VPN_APP">%1$s</xliff:g> ला कनेक्ट केली"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"तुमची वैयक्‍तिक प्रोफाइल <xliff:g id="VPN_APP">%1$s</xliff:g> ला कनेक्‍ट केली आहे"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"हे डिव्हाइस <xliff:g id="VPN_APP">%1$s</xliff:g> ला कनेक्ट केले आहे"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"डिव्हाइस व्‍यवस्‍थापन"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"प्रोफाईल परीक्षण"</string>
<string name="monitoring_title" msgid="4063890083735924568">"नेटवर्क परीक्षण"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN अक्षम करा"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN डिस्कनेक्ट करा"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"धोरणे पहा"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> चे आहे.\n\nतुमचा आयटी ॲडमिन सेटिंग्ज, कॉर्पोरेट अ‍ॅक्सेस, ॲप्‍स, तुमच्‍या डिव्‍हाइसशी संबंधित डेटा आणि तुमच्‍या डिव्‍हाइसच्‍या स्‍थानाची माहिती यांचे परीक्षण व व्‍यवस्‍थापन करू शकतो.\n\nअधिक माहितीसाठी तुमच्‍या आयटी ॲडमिनशी संपर्क साधा."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"हे डिव्हाइस तुमच्या संस्थेचे आहे.\n\nतुमचा आयटी ॲडमिन सेटिंग्ज, कॉर्पोरेट अ‍ॅक्सेस, ॲप्‍स, तुमच्‍या डिव्‍हाइसशी संबंधित डेटा आणि तुमच्‍या डिव्‍हाइसच्‍या स्‍थानाची माहिती यांचे परीक्षण व व्‍यवस्‍थापन करू शकतो.\n\nअधिक माहितीसाठी तुमच्‍या आयटी ॲडमिनशी संपर्क साधा."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"आपल्या संस्थेने या डिव्हाइसवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"आपल्या संस्थेने आपल्या कार्य प्रोफाइलवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"या डिव्हाइसवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"सायलंट"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"डीफॉल्ट"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"बबल"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ऑटोमॅटिक"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"आवाज किंवा व्हायब्रेशन नाही"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"आवाज किंवा व्हायब्रेशन नाही आणि संभाषण विभागात सर्वात तळाशी दिसते"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"फोन सेटिंग्जनुसार फोन रिंग किंवा व्हायब्रेट होऊ शकतो"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फोन सेटिंग्जच्या आधारावर रिंग किंवा व्हायब्रेट होऊ शकतो. <xliff:g id="APP_NAME">%1$s</xliff:g> मधील संभाषणे बाय डीफॉल्ट बबल होतात."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"या आशयाच्या फ्लोटिंग शॉर्टकटसह तुमचे लक्ष केंद्रित करते."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ही सूचना मिळाल्‍यावर आवाज व्‍हावा की व्हायब्रेशन व्‍हावे ते सिस्‍टममध्ये नमूद करा"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"संभाषण विभागात सर्वात वरती फ्लोटिंग बबल म्हणून दिसते, लॉक स्क्रीनवर प्रोफाइल पिक्चर दाखवते"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"सेटिंग्ज"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"प्राधान्य"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"हे अ‍ॅप तुमच्या स्क्रीनवरील इतर अ‍ॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"सेटिंग्ज"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ओके"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ही सूचना सिस्‍टमने सायलंट केली होती."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ही सूचना सिस्‍टमने प्रमोट केली होती."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ही सूचना सिस्‍टमने डीमोट केली होती."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"हे बरोबर होते का?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"तुमच्‍या फीडबॅकबद्दल धन्यवाद!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ओके"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे खुली आहेत"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे बंद आहेत"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"या चॅनेलकडील सूचनांना मान्यता द्या"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"सेटिंग्जचा क्रम संपादित करा."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"पृष्ठ <xliff:g id="ID_2">%2$d</xliff:g> पैकी <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"लॉक स्‍क्रीन"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"विस्तृत करा"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"लहान करा"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"बंद करा"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"सेटिंग्ज"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"डिसमिस करण्यासाठी खाली ड्रॅग करा"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"मेनू"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> चित्रामध्ये चित्र मध्ये आहे"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g>ने हे वैशिष्ट्य वापरू नये असे तुम्हाला वाटत असल्यास, सेटिंग्ज उघडण्यासाठी टॅप करा आणि ते बंद करा."</string>
- <string name="pip_play" msgid="333995977693142810">"प्ले करा"</string>
- <string name="pip_pause" msgid="1139598607050555845">"थांबवा"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"डावलून पुढे जा"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"डावलून मागे जा"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"तापल्‍यामुळे फोन बंद झाला"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"तुमचा फोन आता व्‍यवस्थित सुरू आहे"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"तुमचा फोन आता नेहमीप्रमाणे काम करत आहे.\nअधिक माहितीसाठी टॅप करा"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्‍यवस्थित सुरू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n •संसाधन केंद्रित अ‍ॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अ‍ॅप यासारखे)\n •मोठ्या फाइल डाउनलोड किंवा अपलोड करणे\n •उच्च तापमानामध्ये तुमचा फोन वापरणे"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"काय काळजी घ्यावी ते पाहा"</string>
<string name="high_temp_title" msgid="2218333576838496100">"फोन ऊष्ण होत आहे"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"फोन थंड होत असताना काही वैशिष्‍ट्ये मर्यादित असतात"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"फोन थंड होईपर्यंत काही वैशिष्ट्ये मर्यादित केली.\nअधिक माहितीसाठी टॅप करा"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"तुमचा फोन स्वयंचलितपणे थंड होईल. तुम्ही अद्यापही तुमचा फोन वापरू शकता परंतु तो कदाचित धीमेपणे कार्य करेल.\n\nतुमचा फोन एकदा थंड झाला की, तो सामान्यपणे कार्य करेल."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"काय काळजी घ्यावी ते पाहा"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"चार्जर अनप्लग करा"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"हे डिव्हाइस चार्ज करताना समस्या आहे. पॉवर अडॅप्टर अनप्लग करा आणि शक्य तेवढी काळजी घ्या कदाचित केबल गरम असू शकते."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"काय काळजी घ्यावी ते पाहा"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"नियंत्रणांची पुनर्रचना करण्यासाठी धरून ठेवा आणि ड्रॅग करा"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"सर्व नियंत्रणे काढून टाकली आहेत"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"बदल सेव्ह केले गेले नाहीत"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"इतर अ‍ॅप्स पहा"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"नियंत्रणे लोड करता अली नाहीत. ॲपची सेटिंग्ज बदलली नसल्याची खात्री करण्यासाठी <xliff:g id="APP">%s</xliff:g> ॲप तपासा."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"कंपॅटिबल नियंत्रणे उपलब्ध नाहीत"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"इतर"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> च्या बदलांची निश्चिती करा"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"अधिक पाहण्यासाठी स्वाइप करा"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"शिफारशी लोड करत आहे"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"हे मीडिया सेशन बंद करा"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"सध्याचे सेशन लपवा."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"लपवा"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय, ॲप तपासा"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"एरर, पुन्हा प्रयत्न करत आहे…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"आढळले नाही"</string>
diff --git a/packages/SystemUI/res/values-mr/strings_tv.xml b/packages/SystemUI/res/values-mr/strings_tv.xml
index 587832a6bda6..7f58fe779e03 100644
--- a/packages/SystemUI/res/values-mr/strings_tv.xml
+++ b/packages/SystemUI/res/values-mr/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"चित्रा-मध्‍ये-चित्र"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(शीर्षक नसलेला कार्यक्रम)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP बंद करा"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"फुल स्क्रीन"</string>
<string name="mic_active" msgid="5766614241012047024">"मायक्रोफोन ॲक्टिव्ह आहे"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s यांनी तुमचा मायक्रोफोन अ‍ॅक्सेस केला आहे"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 379da507d713..37cbb7edf753 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ketik lagi untuk membuka"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Leret ke atas untuk buka"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Leret ke atas untuk mencuba lagi"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Peranti ini milik organisasi anda"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Peranti ini milik <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Leret dari ikon untuk telefon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Leret dari ikon untuk bantuan suara"</string>
<string name="camera_hint" msgid="4519495795000658637">"Leret dari ikon untuk kamera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil mungkin dipantau"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Rangkaian mungkin dipantau"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Rangkaian mungkin dipantau"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisasi anda memiliki peranti ini dan mungkin memantau trafik rangkaian"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> memiliki peranti ini dan mungkin memantau trafik rangkaian"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Peranti ini milik organisasi anda dan dihubungkan dengan <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Peranti ini milik <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> dan dihubungkan dengan <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Peranti ini milik organisasi anda"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Peranti ini milik <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Peranti ini milik organisasi anda dan dihubungkan dengan VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Peranti ini milik <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> dan dihubungkan dengan VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Organisasi anda mungkin memantau trafik rangkaian dalam profil kerja anda"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> mungkin memantau trafik rangkaian dalam profil kerja anda"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Rangkaian mungkin dipantau"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Peranti ini dihubungkan dengan VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Profil kerja anda dihubungkan dengan <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Profil peribadi anda dihubungkan dengan <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Peranti ini dihubungkan dengan <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Pengurusan peranti"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Pemantauan profil"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Pemantauan rangkaian"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Lumpuhkan VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Putuskan sambungan VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Lihat Dasar"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Peranti ini milik <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nPentadbir IT anda boleh memantau dan mengurus tetapan, akses korporat, apl, data yang dikaitkan dengan peranti anda dan maklumat lokasi peranti anda.\n\nUntuk maklumat lanjut, hubungi pentadbir IT anda."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Peranti ini milik organisasi anda.\n\nPentadbir IT anda boleh memantau dan mengurus tetapan, akses korporat, apl, data yang dikaitkan dengan peranti anda dan maklumat lokasi peranti anda.\n\nUntuk maklumat lanjut, hubungi pentadbir IT anda."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organisasi anda memasang sijil kuasa pada peranti ini. Trafik rangkaian selamat anda mungkin dipantau atau diubah suai."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisasi anda memasang sijil kuasa dalam profil kerja anda. Trafik rangkaian selamat anda mungkin dipantau atau diubah suai."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sijil kuasa dipasang pada peranti ini. Trafik rangkaian selamat anda mungkin dipantau atau diubah suai."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Senyap"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Lalai"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Gelembung"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatik"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Tiada bunyi atau getaran"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Tiada bunyi atau getaran dan muncul di sebelah bawah dalam bahagian perbualan"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Mungkin berbunyi atau bergetar berdasarkan tetapan telefon"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Mungkin berbunyi atau bergetar berdasarkan tetapan telefon. Perbualan daripada gelembung <xliff:g id="APP_NAME">%1$s</xliff:g> secara lalai."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Memastikan anda memberikan perhatian dengan pintasan terapung ke kandungan ini."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Minta sistem menentukan jika pemberitahuan ini patut menghasilkan bunyi atau getaran"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Ditunjukkan di sebelah atas bahagian perbualan, muncul sebagai gelembung terapung, memaparkan gambar profil pada skrin kunci"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Tetapan"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Keutamaan"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Apl ini dipaparkan di atas apl lain pada skrin anda dan sedang menggunakan mikrofon dan kamera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Tetapan"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Pemberitahuan ini disenyapkan oleh sistem."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Pemberitahuan ini dinaik taraf oleh sistem."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Pemberitahuan ini diturun taraf oleh sistem."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Adakah ini betul?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Terima kasih atas maklum balas anda!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kawalan pemberitahuan untuk <xliff:g id="APP_NAME">%1$s</xliff:g> dibuka"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Kawalan pemberitahuan untuk <xliff:g id="APP_NAME">%1$s</xliff:g> ditutup"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Benarkan pemberitahuan daripada saluran ini"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Edit susunan tetapan."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Halaman <xliff:g id="ID_1">%1$d</xliff:g> daripada <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Kunci skrin"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Kembangkan"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimumkan"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Tutup"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Tetapan"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Seret ke bawah untuk mengetepikan"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> terdapat dalam gambar dalam gambar"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Jika anda tidak mahu <xliff:g id="NAME">%s</xliff:g> menggunakan ciri ini, ketik untuk membuka tetapan dan matikan ciri."</string>
- <string name="pip_play" msgid="333995977693142810">"Main"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Jeda"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Langkau ke seterusnya"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Langkau ke sebelumnya"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefon dimatikan kerana panas"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefon anda kini berjalan seperti biasa"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefon anda kini berjalan seperti biasa.\nKetik untuk mendapatkan maklumat lanjut"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon anda terlalu panas, jadi telefon itu telah dimatikan untuk menyejuk. Sekarang, telefon anda berjalan seperti biasa.\n\nTelefon anda mungkin menjadi terlalu panas jika anda:\n • Menggunakan apl intensif sumber (seperti permainan, video atau apl navigasi)\n • Memuat turun atau memuat naik fail besar\n • Menggunakan telefon anda dalam suhu tinggi"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Lihat langkah penjagaan"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon semakin panas"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Sesetengah ciri adalah terhad semasa telefon menyejuk"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Sesetengah ciri adalah terhad semasa telefon menyejuk.\nKetik untuk mendapatkan maklumat lanjut"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon anda akan cuba menyejuk secara automatik. Anda masih dapat menggunakan telefon itu tetapi telefon tersebut mungkin berjalan lebih perlahan.\n\nSetelah telefon anda sejuk, telefon itu akan berjalan seperti biasa."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Lihat langkah penjagaan"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Cabut palam pengejas"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Terdapat isu semasa mengecas peranti ini. Cabut palam penyesuai kuasa. Berhati-hati kerana kabel mungkin hangat."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Lihat langkah penjagaan"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Tahan &amp; seret untuk mengatur semula kawalan"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Semua kawalan dialih keluar"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Perubahan tidak disimpan"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Lihat apl lain"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Kawalan tidak dapat dimuatkan. Semak apl <xliff:g id="APP">%s</xliff:g> untuk memastikan bahawa tetapan apl tidak berubah."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kawalan serasi tidak tersedia"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Lain-lain"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Sahkan perubahan untuk <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Leret untuk melihat selanjutnya"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuatkan cadangan"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Tutup sesi media ini"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Sembunyikan sesi semasa."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sembunyikan"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Sambung semula"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Tetapan"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Tidak aktif, semak apl"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Ralat, mencuba semula…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
diff --git a/packages/SystemUI/res/values-ms/strings_tv.xml b/packages/SystemUI/res/values-ms/strings_tv.xml
index ba6a85e3e546..3c62891c5530 100644
--- a/packages/SystemUI/res/values-ms/strings_tv.xml
+++ b/packages/SystemUI/res/values-ms/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Gambar dalam Gambar"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program tiada tajuk)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Tutup PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Skrin penuh"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon Aktif"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s telah mengakses mikrofon anda"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 9de9440f70f6..376ed2fd2d9a 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ဖွင့်ရန် ထပ်ပြီး ပုတ်ပါ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ဖွင့်ရန် အပေါ်သို့ပွတ်ဆွဲပါ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ထပ်စမ်းကြည့်ရန် အပေါ်သို့ပွတ်ဆွဲပါ"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ဤစက်ကို သင့်အဖွဲ့အစည်းက ပိုင်ဆိုင်သည်"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ဤစက်ကို <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> က ပိုင်ဆိုင်သည်"</string>
<string name="phone_hint" msgid="6682125338461375925">"ဖုန်းအတွက် သင်္ကေတပုံအား ပွတ်ဆွဲပါ"</string>
<string name="voice_hint" msgid="7476017460191291417">"အသံအကူအညီအတွက် သင်္ကေတပုံအား ပွတ်ဆွဲပါ"</string>
<string name="camera_hint" msgid="4519495795000658637">"ကင်မရာအတွက် သင်္ကေတပုံအား ပွတ်ဆွဲပါ"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ပရိုဖိုင်ကို စောင့်ကြပ်နိုင်သည်"</string>
<string name="vpn_footer" msgid="3457155078010607471">"ကွန်ရက်ကို ကို စောင့်ကြပ် နိုင်ပါသည်"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ကွန်ရက်ကို စောင့်ကြည့်စစ်ဆေးမှု ရှိနိုင်ပါသည်"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ဤစက်ကို သင့်အဖွဲ့အစည်းကပိုင်ဆိုင်ပြီး ကွန်ရက်ဒေတာ စီးဆင်းမှုကို စောင့်ကြည့်နိုင်ပါသည်"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"ဤစက်ကို <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> က ပိုင်ဆိုင်ပြီး ကွန်ရက်ဒေတာ စီးဆင်းမှုကို စောင့်ကြည့်နိုင်ပါသည်"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ဤစက်ကို သင့်အဖွဲ့အစည်းကပိုင်ဆိုင်ပြီး <xliff:g id="VPN_APP">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ဤစက်ကို <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> က ပိုင်ဆိုင်ပြီး <xliff:g id="VPN_APP">%2$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ဤစက်ကို သင့်အဖွဲ့အစည်းက ပိုင်ဆိုင်သည်"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ဤစက်ကို <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> က ပိုင်ဆိုင်သည်"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ဤစက်ကို သင့်အဖွဲ့အစည်းကပိုင်ဆိုင်ပြီး VPN များသို့ ချိတ်ဆက်ထားပါသည်"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ဤစက်ကို <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> က ပိုင်ဆိုင်ပြီး VPN များသို့ ချိတ်ဆက်ထားပါသည်"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"သင်၏ အဖွဲ့အစည်းက သင့်အလုပ်ပရိုဖိုင်ရှိ ကွန်ရက်ဒေတာ စီးဆင်းမှုကို စောင့်ကြည့်နိုင်သည်"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> သည် သင်၏ အလုပ်ပရိုဖိုင်ရှိ ကွန်ရက်ဒေတာ စီးဆင်းမှုကို စောင့်ကြည့်နိုင်ပါသည်"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ကွန်ရက်ကို စောင့်ကြည့်နိုင်ပါသည်"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ဤစက်ကို VPN များသို့ ချိတ်ဆက်ထားပါသည်"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"သင်၏အလုပ်ပရိုဖိုင်သည် <xliff:g id="VPN_APP">%1$s</xliff:g> ကို ချိတ်ဆက်ထားပါသည်"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"သင်၏ ကိုယ်ပိုင်ပရိုဖိုင်ကို <xliff:g id="VPN_APP">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ဤစက်ကို <xliff:g id="VPN_APP">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"စက်ပစ္စည်း စီမံခန့်ခွဲမှု"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"ပရိုဖိုင် စောင့်ကြပ်မှု"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ကွန်ရက်ကို စောင့်ကြပ်ခြင်း"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN ကို ပိတ်ထားရန်"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN ကို အဆက်ဖြတ်ရန်"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"မူဝါဒများကို ကြည့်ရန်"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ဤစက်ကို <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> က ပိုင်ဆိုင်ပါသည်။\n\nဆက်တင်များ၊ ကော်ပိုရိတ် သုံးခွင့်၊ အက်ပ်များ၊ သင့်စက်နှင့် ဆက်စပ်နေသော ဒေတာများနှင့် သင့်စက်တည်နေရာတို့ကို သင်၏ IT စီမံခန့်ခွဲသူက စောင့်ကြည့် စီမံနိုင်သည်။\n\nနောက်ထပ်အချက်အလက်များအတွက် သင်၏ IT စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ဤစက်ကို သင့်အဖွဲ့အစည်းက ပိုင်ဆိုင်သည်။\n\nဆက်တင်များ၊ ကော်ပိုရိတ် သုံးခွင့်၊ အက်ပ်များ၊ သင့်စက်နှင့် ဆက်စပ်နေသော ဒေတာများနှင့် သင့်စက်တည်နေရာတို့ကို သင်၏ IT စီမံခန့်ခွဲသူက စောင့်ကြည့် စီမံနိုင်သည်။\n\nနောက်ထပ်အချက်အလက်များအတွက် သင်၏ IT စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"သင်၏ အဖွဲ့အစည်းက ဤစက်ပစ္စည်းတွင် စီမံခန့်ခွဲမှုဆိုင်ရာ အသိအမှတ်ပြုလက်မှတ်ကို ထည့်သွင်းထားပါသည်။ လုံခြုံမှုရှိသော ကွန်ရက်ဒေတာစီးဆင်းမှုကို စောင့်ကြည့်ခြင်း သို့မဟုတ် ပြုပြင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်။"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"သင်၏ အဖွဲ့အစည်းသည် သင်၏ အလုပ်ပရိုဖိုင်တွင် စီမံခန့်ခွဲမှုဆိုင်ရာ အသိအမှတ်ပြုလက်မှတ်ကို ထည့်သွင်းထားပါသည်။ လုံခြုံမှုရှိသော ကွန်ရက်ဒေတာစီးဆင်းမှုကို စောင့်ကြည့်ခြင်း သို့မဟုတ် ပြုပြင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်။"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ဤစက်ပစ္စည်းတွင် စီမံခန့်ခွဲမှုဆိုင်ရာ အသိအမှတ်ပြုလက်မှတ်ကို ထည့်သွင်းထားပါသည်။ လုံခြုံမှုရှိသော ကွန်ရက်ဒေတာစီးဆင်းမှုကို စောင့်ကြည့်ခြင်း သို့မဟုတ် ပြုပြင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်။"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"အသံတိတ်ရန်"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"မူလ"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"ပူဖောင်းဖောက်သံ"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"အလိုအလျောက်"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"အသံ သို့မဟုတ် တုန်ခါမှုမရှိပါ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"အသံ သို့မဟုတ် တုန်ခါမှုမရှိပါ၊ စကားဝိုင်းကဏ္ဍ၏ အောက်ပိုင်းတွင် မြင်ရသည်"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ဖုန်းဆက်တင်များပေါ် အခြေခံပြီး အသံမြည်နိုင်သည် သို့မဟုတ် တုန်ခါနိုင်သည်"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ဖုန်းဆက်တင်များပေါ် အခြေခံပြီး အသံမြည်နိုင်သည် သို့မဟုတ် တုန်ခါနိုင်သည်။ မူရင်းသတ်မှတ်ချက်အဖြစ် <xliff:g id="APP_NAME">%1$s</xliff:g> မှ စကားဝိုင်းများကို ပူဖောင်းကွက်ဖြင့် ပြသည်။"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"အကြောင်းအရာကို floating shortcut ကိုသုံး၍ အာရုံစိုက်လာအောင်လုပ်ပါ။"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ဤအကြောင်းကြားချက်က အသံ သို့မဟုတ် တုန်ခါမှု ပေးရန် သင့်/မသင့်ကို စနစ်က ဆုံးဖြတ်ပါစေ"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"စကားဝိုင်းကဏ္ဍ၏ ထိပ်ပိုင်းတွင် ပြပြီး ပူဖောင်းကွက်အဖြစ် မြင်ရသည်၊ လော့ခ်ချထားချိန် မျက်နှာပြင်တွင် ပရိုဖိုင်ပုံကို ပြသည်"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ဆက်တင်များ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ဦးစားပေး"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ဤအက်ပ်က မိုက်ခရိုဖုန်းနှင့် ကင်မရာကို အသုံးပြု၍ ဖန်သားမျက်နှာပြင်ပေါ်ရှိ အခြားအက်ပ်များ အပေါ်မှ ထပ်ပြီး ပြသနေပါသည်။"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ဆက်တင်များ"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ဤအကြောင်းကြားချက်ကို စနစ်က အသံပိတ်ထားလိုက်သည်။"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ဤအကြောင်းကြားချက်ကို စနစ်က အဆင့်မြှင့်လိုက်သည်။"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ဤအကြောင်းကြားချက်ကို စနစ်က အဆင့်ချလိုက်သည်။"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"ဤအရာက မှန်ပါသလား။"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"အကြံပြုချက်အတွက် ကျေးဇူးတင်ပါသည်"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် အကြောင်းကြားချက်ထိန်းချုပ်မှုများကို ဖွင့်ထားသည်"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် အကြောင်းကြားချက်ထိန်းချုပ်မှုများကို ပိတ်ထားသည်"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"ဤချန်နယ်မှ အကြောင်းကြားချက်များကို ခွင့်ပြုပါ"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ဆက်တင်များ၏ အစီအစဉ်ကို တည်းဖြတ်ပါ။"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"စာမျက်နှာ <xliff:g id="ID_2">%2$d</xliff:g> အနက်မှ စာမျက်နှာ <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"လော့ခ်ချထားချိန် မျက်နှာပြင်"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"ချဲ့ရန်"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"ချုံ့ရန်"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"ပိတ်ရန်"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ဆက်တင်များ"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"ပယ်ရန်အတွက် အောက်သို့ ပွတ်ဆွဲပါ"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"မီနူး"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> သည် တစ်ခုပေါ် တစ်ခုထပ်၍ ဖွင့်ထားသည်"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> အား ဤဝန်ဆောင်မှုကို အသုံးမပြုစေလိုလျှင် ဆက်တင်ကိုဖွင့်ရန် တို့ပြီး ၎င်းဝန်ဆောင်မှုကို ပိတ်လိုက်ပါ။"</string>
- <string name="pip_play" msgid="333995977693142810">"ဖွင့်ရန်"</string>
- <string name="pip_pause" msgid="1139598607050555845">"ခေတ္တရပ်ရန်"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"နောက်တစ်ခုသို့ ကျော်ရန်"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"ယခင်တစ်ခုသို့ ပြန်သွားရန်"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"အပူရှိန်ကြောင့်ဖုန်းပိတ်ထားသည်"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"သင်၏ဖုန်းသည် ပုံမှန် အလုပ်လုပ်နေပါသည်"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"သင့်ဖုန်းသည် ယခု ပုံမှန်အလုပ်လုပ်နေပါပြီ။\nနောက်ထပ်အချက်အလက်များအတွက် တို့ပါ"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"သင့်ဖုန်းအလွန်ပူနေသည့်အတွက် အေးသွားစေရန် ပိတ်ထားပါသည်။ ယခုပုံမှန် အလုပ်လုပ်ပါပြီ။\n\nအောက်ပါတို့ကိုသုံးလျှင် ပူလာပါမည်-\n • အရင်းအမြစ်များသောအက်ပ်ကို သုံးခြင်း (ဥပမာ ဂိမ်းကစားခြင်း၊ ဗီဒီယိုကြည့်ခြင်း (သို့) လမ်းညွှန်အက်ပ်)\n • ကြီးမားသောဖိုင်များ ဒေါင်းလုဒ် (သို့) အပ်လုဒ်လုပ်ခြင်း\n • အပူရှိန်မြင့်သောနေရာတွင် သုံးခြင်း"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ဂရုပြုစရာ အဆင့်များ ကြည့်ရန်"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ဖုန်း ပူနွေးလာပါပြီ"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ဖုန်းကို အေးအောင်ပြုလုပ်နေစဉ်တွင် အချို့ဝန်ဆောင်မှုများကို ကန့်သတ်ထားပါသည်"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ဖုန်းကို အေးအောင်ပြုလုပ်နေစဉ်တွင် အချို့ဝန်ဆောင်မှုများကို ကန့်သတ်ထားပါသည်။\nနောက်ထပ်အချက်အလက်များအတွက် တို့ပါ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"သင့်ဖုန်းသည် အလိုအလျောက် ပြန်အေးသွားပါလိမ့်မည်။ ဖုန်းကို အသုံးပြုနိုင်ပါသေးသည် သို့သော် ပိုနှေးနိုင်ပါသည်။\n\nသင့်ဖုန်း အေးသွားသည်နှင့် ပုံမှန်အတိုင်း ပြန်အလုပ်လုပ်ပါလိမ့်မည်။"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ဂရုပြုစရာ အဆင့်များ ကြည့်ရန်"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"အားသွင်းကိရိယာ ပလပ်ဖြုတ်ပါ"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"ဤစက်ပစ္စည်းကို အားသွင်းရာတွင် ပြဿနာရှိနေသည်။ ပါဝါ ကြားခံကိရိယာကို ပလပ်ဖြုတ်ပါ။ ကေဘယ်ကြိုး ပူနွေးနေနိုင်သဖြင့် သတိထားပါ။"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ဂရုပြုစရာ အဆင့်များ ကြည့်ရန်"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"ထိန်းချုပ်မှုများ ပြန်စီစဉ်ရန် ဖိပြီးဆွဲပါ"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"ထိန်းချုပ်မှုအားလုံး ဖယ်ရှားလိုက်သည်"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"အပြောင်းအလဲများကို သိမ်းမထားပါ"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"အခြားအက်ပ်များကိုကြည့်ပါ"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"ထိန်းချုပ်မှုများကို ဖွင့်၍မရပါ။ အက်ပ်ဆက်တင်များ ပြောင်းမထားကြောင်း သေချာစေရန် <xliff:g id="APP">%s</xliff:g> အက်ပ်ကို စစ်ဆေးပါ။"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"ကိုက်ညီသော ထိန်းချုပ်မှုများကို မရရှိနိုင်ပါ"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"အခြား"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> အတွက် အပြောင်းအလဲကို အတည်ပြုပါ"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"ပိုမိုကြည့်ရှုရန် ပွတ်ဆွဲပါ"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"အကြံပြုချက်များ ဖွင့်နေသည်"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ဤမီဒီယာစက်ရှင်ကို ပိတ်ပါ"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"မီဒီယာ"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"လက်ရှိ စက်ရှင်ကို ဖျောက်ထားမည်။"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ဖျောက်ထားမည်"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ဆက်လုပ်ရန်"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ဆက်တင်များ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ရပ်နေသည်၊ အက်ပ်ကို စစ်ဆေးပါ"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"မှားသွားသည်၊ ပြန်စမ်းနေသည်…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"မတွေ့ပါ"</string>
diff --git a/packages/SystemUI/res/values-my/strings_tv.xml b/packages/SystemUI/res/values-my/strings_tv.xml
index e33a1c32557a..88d7d53d8390 100644
--- a/packages/SystemUI/res/values-my/strings_tv.xml
+++ b/packages/SystemUI/res/values-my/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"တစ်ခုပေါ်တစ်ခု ထပ်၍ ဖွင့်ခြင်း"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(ခေါင်းစဉ်မဲ့ အစီအစဉ်)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP ကိုပိတ်ပါ"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"မျက်နှာပြင် အပြည့်"</string>
<string name="mic_active" msgid="5766614241012047024">"မိုက်ခရိုဖုန်း ဖွင့်ထားသည်"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s က သင့်မိုက်ခရိုဖုန်းကို သုံးထားသည်"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 59525e4555c0..26268301333c 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Trykk på nytt for å åpne"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Sveip opp for å åpne"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Sveip opp for å prøve igjen"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Denne enheten tilhører organisasjonen din"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Denne enheten tilhører <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Sveip ikonet for å åpne telefon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Sveip fra ikonet for å åpne talehjelp"</string>
<string name="camera_hint" msgid="4519495795000658637">"Sveip ikonet for å åpne kamera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profilen kan overvåkes"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Nettverket kan være overvåket"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Nettverket kan bli overvåket"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisasjonen din eier denne enheten og kan overvåke nettverkstrafikken"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> eier denne enheten og kan overvåke nettverkstrafikken"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Denne enheten tilhører organisasjonen din og er koblet til <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Denne enheten tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> og er koblet til <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Denne enheten tilhører organisasjonen din"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Denne enheten tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Denne enheten tilhører organisasjonen din og er koblet til VPN-er"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Denne enheten tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> og er koblet til VPN-er"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Organisasjonen din kan overvåke nettverkstrafikken i jobbprofilen din"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kan overvåke nettverkstrafikken i jobbprofilen din"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Nettverket kan bli overvåket"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Denne enheten er koblet til VPN-er"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Jobbprofilen din er koblet til <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Den personlige profilen din er koblet til <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Denne enheten er koblet til <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Enhetsadministrasjon"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profilovervåking"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Nettverksovervåking"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Deaktiver VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Koble fra VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Se retningslinjer"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Denne enheten tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nIT-administratoren din kan overvåke og administrere innstillinger, bedriftstilgang, apper, data som er tilknyttet denne enheten, og enhetens posisjonsinformasjon.\n\nKontakt IT-administratoren for å få mer informasjon."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Denne enheten tilhører organisasjonen din.\n\nIT-administratoren din kan overvåke og administrere innstillinger, bedriftstilgang, apper, data som er tilknyttet denne enheten, og enhetens posisjonsinformasjon.\n\nKontakt IT-administratoren for å få mer informasjon."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organisasjonen din installerte en sertifiseringsinstans på denne enheten. Den sikre nettverkstrafikken din kan overvåkes eller endres."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisasjonen din installerte en sertifiseringsinstans i jobbprofilen din. Den sikre nettverkstrafikken din kan overvåkes eller endres."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"En sertifiseringsinstans er installert på denne enheten. Den sikre nettverkstrafikken din kan overvåkes eller endres."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Lydløs"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Boble"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ingen lyd eller vibrering"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ingen lyd eller vibrering, og vises lavere i samtaledelen"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan ringe eller vibrere basert på telefoninnstillingene"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan ringe eller vibrere basert på telefoninnstillingene. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> lager bobler som standard."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Holder deg oppmerksom med en svevende snarvei til dette innholdet."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"La systemet velge om dette varselet skal lage lyd eller vibrere"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Vises øverst i samtaledelen, vises som en flytende boble, viser profilbildet på låseskjermen"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Innstillinger"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Denne appen vises over andre apper på skjermen, og bruker mikrofonen og kameraet."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Innstillinger"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Dette varselet ble dempet av systemet."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Dette varselet ble oppgradert av systemet."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Dette varselet ble nedgradert av systemet."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Var det riktig?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Takk for tilbakemeldingen!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Varselinnstillingene for <xliff:g id="APP_NAME">%1$s</xliff:g> er åpnet"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Varselinnstillingene for <xliff:g id="APP_NAME">%1$s</xliff:g> er lukket"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Tillat varsler fra denne kanalen"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Endre rekkefølgen på innstillingene."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Side <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Låseskjerm"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Vis"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimer"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Lukk"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Innstillinger"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Dra ned for å avvise"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Meny"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> er i bilde-i-bilde"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Hvis du ikke vil at <xliff:g id="NAME">%s</xliff:g> skal bruke denne funksjonen, kan du trykke for å åpne innstillingene og slå den av."</string>
- <string name="pip_play" msgid="333995977693142810">"Spill av"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Sett på pause"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Hopp til neste"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Hopp til forrige"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefon ble slått av pga varme"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefonen din kjører nå som normalt"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefonen kjører nå som normalt.\nTrykk for å se mer informasjon"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonen din var for varm, så den ble slått av for å kjøles ned. Telefonen din kjører nå som normalt.\n\nTelefonen kan blir for varm hvis du:\n • bruker ressurskrevende apper (for eksempel spill-, video- eller navigeringsapper)\n • laster store filer opp eller ned\n • bruker telefonen ved høy temperatur"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Se vedlikeholdstrinnene"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefonen begynner å bli varm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Enkelte funksjoner er begrenset mens telefonen kjøles ned"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Enkelte funksjoner er begrenset mens telefonen kjøles ned.\nTrykk for å se mer informasjon"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonen din kommer til å prøve å kjøle seg ned automatisk. Du kan fremdeles bruke telefonen, men den kjører muligens saktere.\n\nTelefonen kommer til å kjøre som normalt, når den har kjølt seg ned."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Se vedlikeholdstrinnene"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Koble fra laderen"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Det oppsto et problem med lading av enheten. Koble fra strømadapteren, og vær forsiktig, kabelen kan være varm."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Se vedlikeholdstrinnene"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Hold og dra for å flytte kontroller"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Alle kontroller er fjernet"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Endringene er ikke lagret"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Se andre apper"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Kunne ikke laste inn kontrollene. Sjekk <xliff:g id="APP">%s</xliff:g>-appen for å sjekke at appinnstillingene ikke er endret."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kompatible kontroller er ikke tilgjengelige"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Annet"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Bekreft endringen for <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Sveip for å se flere"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laster inn anbefalinger"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Lukk denne medieøkten"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Medier"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den nåværende økten."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skjul"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Gjenoppta"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Innstillinger"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Sjekk appen"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Feil. Prøver igjen …"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ikke funnet"</string>
diff --git a/packages/SystemUI/res/values-nb/strings_tv.xml b/packages/SystemUI/res/values-nb/strings_tv.xml
index 22580e645ca0..cd558735315f 100644
--- a/packages/SystemUI/res/values-nb/strings_tv.xml
+++ b/packages/SystemUI/res/values-nb/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Bilde-i-bilde"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program uten tittel)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Lukk PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Fullskjerm"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofonen er aktiv"</string>
- <string name="app_accessed_mic" msgid="2754428675130470196">"%1$s brukte mikrofonen din"</string>
+ <string name="app_accessed_mic" msgid="2754428675130470196">"%1$s fikk tilgang til mikrofonen din"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 0357c8db112b..d962af76ec41 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -166,8 +166,8 @@
<string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"तपाईंले अर्को पटक पनि गलत ढाँचा प्रविष्टि गर्नुभयो भने यो कार्य प्रोफाइल र त्यहाँको डेटा मेटाइने छ।"</string>
<string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"तपाईंले अर्को पटक पनि गलत PIN प्रविष्टि गर्नुभयो भने तपाईंको कार्य प्रोफाइल र त्यहाँको डेटा मेटाइने छ।"</string>
<string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"तपाईंले अर्को पटक पनि गलत पासवर्ड प्रविष्टि गर्नुभयो भने तपाईंको कार्य प्रोफाइल र त्यहाँको डेटा मेटाइने छ।"</string>
- <string name="biometric_dialog_failed_attempts_now_wiping_device" msgid="6585503524026243042">"अनलक गर्ने अत्याधिक गलत प्रयासहरू भए। यो यन्त्रको डेटा मेटाइने छ।"</string>
- <string name="biometric_dialog_failed_attempts_now_wiping_user" msgid="7015008539146949115">"अनलक गर्ने अत्याधिक गलत प्रयासहरू भए। यो प्रयोगकर्तालाई हटाइने छ।"</string>
+ <string name="biometric_dialog_failed_attempts_now_wiping_device" msgid="6585503524026243042">"अनलक गर्ने अत्यधिक गलत प्रयासहरू भए। यो यन्त्रको डेटा मेटाइने छ।"</string>
+ <string name="biometric_dialog_failed_attempts_now_wiping_user" msgid="7015008539146949115">"अनलक गर्ने अत्यधिक गलत प्रयासहरू भए। यो प्रयोगकर्तालाई हटाइने छ।"</string>
<string name="biometric_dialog_failed_attempts_now_wiping_profile" msgid="5239378521440749682">"अनलक गर्ने अत्यधिक गलत प्रयासहरू भए। यो कार्यलयको प्रोफाइल र यसको डेटा मेटाइने छ।"</string>
<string name="biometric_dialog_now_wiping_dialog_dismiss" msgid="7189432882125106154">"हटाउनुहोस्"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"फिंगरप्रिन्ट सेन्सरमा छुनुहोस्‌"</string>
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"खोल्न पुनः ट्याप गर्नुहोस्"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"खोल्न माथितिर स्वाइप गर्नुहोस्"</string>
<string name="keyguard_retry" msgid="886802522584053523">"फेरि प्रयास गर्न माथितिर स्वाइप गर्नुहोस्"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"यो यन्त्र तपाईंको सङ्गठनको स्वामित्वमा छ"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"यो यन्त्र <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> को स्वामित्वमा छ"</string>
<string name="phone_hint" msgid="6682125338461375925">"फोनको लागि आइकनबाट स्वाइप गर्नुहोस्"</string>
<string name="voice_hint" msgid="7476017460191291417">"आवाज सहायताका लागि आइकनबाट स्वाइप गर्नुहोस्"</string>
<string name="camera_hint" msgid="4519495795000658637">"क्यामेराको लागि आइकनबाट स्वाइप गर्नुहोस्"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"प्रोफाइल अनुगमन हुन सक्छ"</string>
<string name="vpn_footer" msgid="3457155078010607471">"सञ्जाल अनुगमित हुन सक्छ"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"नेटवर्कको अनुगमन गरिने सम्भावना छ"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"यो यन्त्र तपाईंको सङ्गठनको स्वामित्वमा छ र उक्त सङ्गठनले यसको नेटवर्क ट्राफिक अनुगमन गर्न सक्छ"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"यो यन्त्र <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> को स्वामित्वमा छ र उक्त सङ्गठनले यसको नेटवर्क ट्राफिक अनुगमन गर्न सक्छ"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"यो यन्त्र तपाईंको सङ्गठनको स्वामित्वमा छ र <xliff:g id="VPN_APP">%1$s</xliff:g> मा कनेक्ट गरिएको छ"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"यो यन्त्र <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> को स्वामित्वमा छ र <xliff:g id="VPN_APP">%2$s</xliff:g> मा कनेक्ट गरिएको छ"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"यो यन्त्र तपाईंको सङ्गठनको स्वामित्वमा छ"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"यो यन्त्र <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> को स्वामित्वमा छ"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"यो यन्त्र तपाईंको सङ्गठनको स्वामित्वमा छ र VPN हरूमा कनेक्ट गरिएको छ"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"यो यन्त्र <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> को स्वामित्वमा छ र VPN हरूमा कनेक्ट गरिएको छ"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"तपाईंको संगठनले तपाईंको कार्य प्रोफाइलमा नेटवर्कको ट्राफिकको अनुगमन गर्न पनि सक्छ"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ले तपाईंको कार्य प्रोफाइलमा नेटवर्क ट्राफिकको अनुगमन गर्न पनि सक्छ"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"नेटवर्कको अनुगमन हुनसक्छ"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"यो यन्त्र VPN हरूमा कनेक्ट गरिएको छ"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"तपाईंको कार्य प्रोफाइल <xliff:g id="VPN_APP">%1$s</xliff:g> मा कनेक्ट गरिएको छ"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"तपाईंको व्यक्तिगत प्रोफाइल <xliff:g id="VPN_APP">%1$s</xliff:g> मा कनेक्ट गरिएको छ"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"यो यन्त्र <xliff:g id="VPN_APP">%1$s</xliff:g> मा कनेक्ट गरिएको छ"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"यन्त्रको व्यवस्थापन"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"प्रोफाइल अनुगमन गर्दै"</string>
<string name="monitoring_title" msgid="4063890083735924568">"सञ्जाल अनुगमन"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN असक्षम गर्नुहोस्"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"विच्छेद VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"नीतिहरू हेर्नुहोस्"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"यो यन्त्र <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> को स्वामित्वमा छ।\n\nतपाईंका IT एड्मिन सेटिङ, संस्थागत पहुँच, एप, तपाईंको यन्त्रसँग सम्बन्धित डेटा र तपाईंको यन्त्रको स्थानसम्बन्धी जानकारीको निगरानी र व्यवस्थापन गर्न सक्नुहुन्छ।\n\nथप जानकारीका लागि आफ्ना IT एड्मिनसँग सम्पर्क गर्नुहोस्।"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"यो यन्त्र तपाईंको सङ्गठनको स्वामित्वमा छ।\n\nतपाईंका IT एड्मिन सेटिङ, संस्थागत पहुँच, एप, तपाईंको यन्त्रसँग सम्बन्धित डेटा र तपाईंको यन्त्रको स्थानसम्बन्धी जानकारीको निगरानी र व्यवस्थापन गर्न सक्नुहुन्छ।\n\nथप जानकारीका लागि आफ्ना IT एड्मिनसँग सम्पर्क गर्नुहोस्।"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"तपाईंको संगठनले तपाईंको कार्य प्रोफाइलमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापित गऱ्यो। तपाईंको सुरक्षित नेटवर्क ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"तपाईंको संगठनले तपाईंको कार्य प्रोफाइलमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापना गरेको छ। तपाईंको सुरक्षित नेटवर्क ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"यस यन्त्रमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापना गरिएको छ। तपाईंको सुरक्षित नेटवर्कको ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"मौन"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"पूर्वनिर्धारित"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"बबल"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"स्वचालित"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"न घन्टी बज्छ न त कम्पन नै हुन्छ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"न घन्टी बज्छ न त कम्पन नै हुन्छ र वार्तालाप खण्डको तलतिर देखा पर्छ"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"फोनको सेटिङका आधारमा घन्टी बज्न वा कम्पन हुन सक्छ"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फोनको सेटिङका आधारमा घन्टी बज्न वा कम्पन हुन सक्छ। <xliff:g id="APP_NAME">%1$s</xliff:g> का वार्तालापहरू पूर्वनिर्धारित रूपमा बबलमा देखाइन्छन्।"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"फ्लोटिङ सर्टकटमार्फत यो सामग्रीतर्फ तपाईंको ध्यान आकर्षित गर्दछ।"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"सिस्टमलाई यो सूचना आउँदा ध्वनि बज्नु पर्छ वा कम्पन हुनु पर्छ भन्ने कुराको निधो गर्न दिनुहोस्"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"वार्तालाप खण्डको सिरानमा देखा पर्छ, तैरने बबलका रूपमा देखा पर्छ, लक स्क्रिनमा प्रोफाइल तस्बिर देखाइन्छ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"सेटिङ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य अनुप्रयोगहरूमाथि प्रदर्शन गर्नुका साथै माइक्रोफोन र क्यामेराको प्रयोग गर्दै छ।"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"सेटिङहरू"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ठिक छ"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"सिस्टमले यो सूचना आउँदा बज्ने ध्वनि बन्द गरेको छ।"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"सिस्टमले यो सूचनालाई दिइने दर्जा बढाएको छ।"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"सिस्टमले यो सूचनालाई दिइने दर्जा घटाएको छ।"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"के यो सही थियो?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"तपाईंको प्रतिक्रियाका लागि धन्यवाद!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ठिक छ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचना सम्बन्धी नियन्त्रणहरूलाई खोलियो"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचना सम्बन्धी नियन्त्रणहरूलाई बन्द गरियो"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"यो च्यानलका सूचनाहरूलाई अनुमति दिनुहोस्"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"सेटिङहरूको क्रमलाई सम्पादन गर्नुहोस्।"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> मध्ये पृष्ठ <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"लक स्क्रिन"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"विस्तृत गर्नुहोस्"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"सानो बनाउनुहोस्"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"बन्द गर्नुहोस्"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"सेटिङहरू"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"खारेज गर्न तल तान्नुहोस्"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"मेनु"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> Picture-in-picture मा छ"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"तपाईं <xliff:g id="NAME">%s</xliff:g> ले सुविधा प्रयोग नगरोस् भन्ने चाहनुहुन्छ भने ट्याप गरेर सेटिङहरू खोल्नुहोस् र यसलाई निष्क्रिय पार्नुहोस्।"</string>
- <string name="pip_play" msgid="333995977693142810">"प्ले गर्नुहोस्"</string>
- <string name="pip_pause" msgid="1139598607050555845">"पज गर्नुहोस्"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"अर्कोमा जानुहोस्"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"अघिल्लोमा जानुहोस्"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"फोन अति नै तातिएकाले चिसिन बन्द भयो"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"तपाईंको फोन अब सामान्य ढंगले चल्दै छ"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"तपाईंको फोन अहिले सामान्य रूपमा चलिरहेको छ।\nथप जानकारीका लागि ट्याप गर्नुहोस्"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"तपाईंको फोन अति नै तातिएकाले चिसिन बन्द भयो। तपाईंको फोन अब सामान्य ढंगले चल्दै छ।\n\nतपाईंले निम्न कुराहरू गर्नुभयो भने तपाईंको फोन अत्यन्त तातो हुनसक्छ:\n • धेरै संसाधन खपत गर्ने एपहरूको प्रयोग (जस्तै गेमिङ, भिडियो वा नेभिगेसन एपहरू)\n • ठूला फाइलहरूको डाउनलोड वा अपलोड\n • उच्च तापक्रममा फोनको प्रयोग"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"यन्त्रको हेरचाह गर्ने तरिका हेर्नुहोस्"</string>
<string name="high_temp_title" msgid="2218333576838496100">"फोन तातो भइरहेको छ"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"फोन चिसो हुँदै गर्दा केही विशेषताहरूलाई सीमित गरिन्छ"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"फोन नचिस्सिँदासम्म केही सुविधाहरू उपलब्ध हुने छैनन्।\nथप जानकारीका लागि ट्याप गर्नुहोस्"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"तपाईंको फोन स्वतः चिसो हुने प्रयास गर्ने छ। तपाईं अझै पनि आफ्नो फोनको प्रयोग गर्न सक्नुहुन्छ तर त्यो अझ ढिलो चल्न सक्छ।\n\nचिसो भएपछि तपाईंको फोन सामान्य गतिमा चल्नेछ।"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"यन्त्रको हेरचाह गर्ने तरिका हेर्नुहोस्"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"चार्जर अनप्लग गर्नुहोस्‌"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"यो यन्त्र चार्ज गर्दा कुनै समस्या भयो। पावर एडाप्टर अनप्लग गर्नुहोस्‌ र केबल तातो हुन सक्ने भएकाले ध्यान दिनुहोस्‌।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"हेरचाहसम्बन्धी चरणहरू हेर्नुहोस्‌"</string>
@@ -1078,7 +1042,8 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"नियन्त्रणहरूको क्रम मिलाउन तिनलाई थिचेर ड्र्याग गर्नुहोस्"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"सबै नियन्त्रणहरू हटाइए"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"परिवर्तनहरू सुरक्षित गरिएका छैनन्"</string>
- <string name="controls_favorite_load_error" msgid="5126216176144877419">"नियन्त्रण सुविधाहरू लोड गर्न सकिएन। <xliff:g id="APP">%s</xliff:g> एपका सेटिङ परिवर्तन गरिएका छैनन् भन्ने कुरा सुनिश्चित गर्न यो एप जाँच्नुहोस्।"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"अन्य एपहरू हेर्नुहोस्"</string>
+ <string name="controls_favorite_load_error" msgid="5126216176144877419">"नियन्त्रण सुविधाहरू लोड गर्न सकिएन। <xliff:g id="APP">%s</xliff:g> एपका सेटिङ परिवर्तन गरिएका छैनन् भन्ने कुरा सुनिश्चित गर्न उक्त एप जाँच्नुहोस्।"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"मिल्दा नियन्त्रण सुविधाहरू उपलब्ध छैनन्"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"अन्य"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"यन्त्र नियन्त्रण गर्ने विजेटहरूको सूचीमा थप्नुहोस्"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> का हकमा गरिएको परिवर्तन पुष्टि गर्नुहोस्"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"थप हेर्न स्वाइप गर्नुहोस्"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"सिफारिसहरू लोड गर्दै"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"यो मिडिया सत्र बन्द गर्नुहोस्"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"मिडिया"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"हालको सत्र लुकाउनुहोस्।"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"लुकाउनुहोस्"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय छ, एप जाँच गर्नु…"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"त्रुटि भयो, फेरि प्रयास गर्दै…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"फेला परेन"</string>
diff --git a/packages/SystemUI/res/values-ne/strings_tv.xml b/packages/SystemUI/res/values-ne/strings_tv.xml
index 20411351b549..22f7f71793fa 100644
--- a/packages/SystemUI/res/values-ne/strings_tv.xml
+++ b/packages/SystemUI/res/values-ne/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-Picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(शीर्षकविहीन कार्यक्रम)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP लाई बन्द गर्नुहोस्"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"फुल स्क्रिन"</string>
<string name="mic_active" msgid="5766614241012047024">"माइक्रोफोन सक्रिय छ"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ले तपाईंको माइक्रोफोनमाथि पहुँच राख्यो"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index c02802a9c3aa..7a4de7516e05 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Schermopname verwerken"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Doorlopende melding voor een schermopname-sessie"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Opname starten?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"Tijdens de opname kan het Android-systeem gevoelige informatie opnemen die zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Dit omvat wachtwoorden, betalingsgegevens, foto\'s, berichten en audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"Tijdens de opname kan het Android-systeem gevoelige informatie opnemen die zichtbaar is op je scherm of wordt afgespeeld op je apparaat, waaronder wachtwoorden, betalingsgegevens, foto\'s, berichten en audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Audio opnemen"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio van apparaat"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Geluid van je apparaat, zoals muziek, gesprekken en ringtones"</string>
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tik nog eens om te openen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Veeg omhoog om te openen"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Veeg omhoog om het opnieuw te proberen"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Dit apparaat is eigendom van je organisatie"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Dit apparaat is eigendom van <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Vegen voor telefoon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Vegen vanaf pictogram voor spraakassistent"</string>
<string name="camera_hint" msgid="4519495795000658637">"Vegen voor camera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profiel kan worden gecontroleerd"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Netwerk kan worden gecontroleerd"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Netwerk kan worden gecontroleerd"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Je organisatie is eigenaar van dit apparaat en kan het netwerkverkeer bijhouden"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> is eigenaar van dit apparaat en kan het netwerkverkeer bijhouden"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Dit apparaat is eigendom van je organisatie en is verbonden met <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Dit apparaat is eigendom van <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> en is verbonden met <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Dit apparaat is eigendom van je organisatie"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Dit apparaat is eigendom van <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Dit apparaat is eigendom van je organisatie en is verbonden met VPN\'s"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Dit apparaat is eigendom van <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> en is verbonden met VPN\'s"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Je organisatie kan het netwerkverkeer in je werkprofiel bijhouden"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kan het netwerkverkeer in je werkprofiel bijhouden"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Netwerk kan worden bijgehouden"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Dit apparaat is verbonden met VPN\'s"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Je werkprofiel is verbonden met <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Je persoonlijke profiel is verbonden met <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Dit apparaat is verbonden met <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Apparaatbeheer"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profielcontrole"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Netwerkcontrole"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN uitschakelen"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Verbinding met VPN verbreken"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Beleid bekijken"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Dit apparaat is eigendom van <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nJe IT-beheerder kan instellingen, zakelijke toegang, apps, aan je apparaat gekoppelde gegevens en de locatiegegevens van je apparaat bekijken en beheren.\n\nNeem contact op met je IT-beheerder voor meer informatie."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Dit apparaat is eigendom van je organisatie.\n\nJe IT-beheerder kan instellingen, zakelijke toegang, apps, aan je apparaat gekoppelde gegevens en de locatiegegevens van je apparaat bekijken en beheren.\n\nNeem contact op met je IT-beheerder voor meer informatie."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Je organisatie heeft een certificeringsinstantie geïnstalleerd op dit apparaat. Je beveiligde netwerkverkeer kan worden bijgehouden of aangepast."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Je organisatie heeft een certificeringsinstantie geïnstalleerd in je werkprofiel. Je beveiligde netwerkverkeer kan worden bijgehouden of aangepast."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Er is een certificeringsinstantie geïnstalleerd op dit apparaat. Je beveiligde netwerkverkeer kan worden bijgehouden of aangepast."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Stil"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standaard"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bubbel"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen geluid of trilling"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt op een lagere positie in het gedeelte met gesprekken weergegeven"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan overgaan of trillen op basis van de telefooninstellingen"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan overgaan of trillen op basis van de telefooninstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels weergegeven."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Trekt de aandacht met een zwevende snelkoppeling naar deze content."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Het systeem laten bepalen of deze melding geluid moet maken of moet trillen"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wordt bovenaan het gedeelte met gesprekken weergegeven, verschijnt als zwevende bubbel, geeft de profielfoto weer op het vergrendelscherm"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Instellingen"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Deze app geeft andere apps op je scherm weer en gebruikt de microfoon en camera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Instellingen"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Deze melding is gedempt door het systeem."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Deze melding is gepromoveerd door het systeem."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Deze melding is verlaagd door het systeem."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Is dit juist?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Bedankt voor je feedback."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Beheeropties voor meldingen voor <xliff:g id="APP_NAME">%1$s</xliff:g> geopend"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Beheeropties voor meldingen voor <xliff:g id="APP_NAME">%1$s</xliff:g> gesloten"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Meldingen van dit kanaal toestaan"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Volgorde van instellingen bewerken."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Vergrendelscherm"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Uitvouwen"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimaliseren"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Sluiten"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Instellingen"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Sleep omlaag om te sluiten"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en schakel je de functie uit."</string>
- <string name="pip_play" msgid="333995977693142810">"Afspelen"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Onderbreken"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Doorgaan naar volgende"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Teruggaan naar vorige"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefoon uitgezet wegens hitte"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Je telefoon presteert nu weer zoals gebruikelijk"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Je telefoon functioneert nu weer zoals gebruikelijk.\nTik voor meer informatie"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Je telefoon was te warm en is uitgeschakeld om af te koelen. Je telefoon presteert nu weer zoals gebruikelijk.\n\nJe telefoon kan warm worden als je:\n • bronintensieve apps gebruikt (zoals game-, video-, of navigatie-apps),\n • grote bestanden up- of downloadt,\n • je telefoon gebruikt bij hoge temperaturen."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Onderhoudsstappen bekijken"</string>
<string name="high_temp_title" msgid="2218333576838496100">"De telefoon wordt warm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Bepaalde functies zijn beperkt terwijl de telefoon afkoelt"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Bepaalde functies zijn beperkt terwijl de telefoon afkoelt.\nTik voor meer informatie"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Je telefoon probeert automatisch af te koelen. Je kunt je telefoon nog steeds gebruiken, maar deze kan langzamer werken.\n\nZodra de telefoon is afgekoeld, werkt deze weer normaal."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Onderhoudsstappen bekijken"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Oplader loskoppelen"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Er is een probleem met het opladen van dit apparaat. Koppel de voedingsadapter los. Wees voorzichtig, want de kabel kan warm zijn."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Onderhoudsstappen bekijken"</string>
@@ -1074,12 +1038,13 @@
<string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"als favoriet verwijderen"</string>
<string name="accessibility_control_move" msgid="8980344493796647792">"Verplaatsen naar positie <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="controls_favorite_default_title" msgid="967742178688938137">"Bedieningselementen"</string>
- <string name="controls_favorite_subtitle" msgid="6604402232298443956">"Kies bedieningselementen die je vanaf het aan/uit-menu wilt kunnen gebruiken"</string>
+ <string name="controls_favorite_subtitle" msgid="6604402232298443956">"Kies bedieningselementen die je via het aan/uit-menu wilt kunnen gebruiken"</string>
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Houd vast en sleep om de bedieningselementen opnieuw in te delen"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Alle bedieningselementen verwijderd"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Wijzigingen zijn niet opgeslagen"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Andere apps bekijken"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Bedieningselementen kunnen niet worden geladen. Check de <xliff:g id="APP">%s</xliff:g>-app om na te gaan of de app-instellingen niet zijn gewijzigd."</string>
- <string name="controls_favorite_load_none" msgid="7687593026725357775">"Geen geschikte bedieningselementen beschikbaar."</string>
+ <string name="controls_favorite_load_none" msgid="7687593026725357775">"Geen geschikte bedieningselementen beschikbaar"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Overig"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"Toevoegen aan apparaatbediening"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"Toevoegen"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Bevestig de wijziging voor <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Swipe om meer te zien"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Aanbevelingen laden"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Deze mediasessie sluiten"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"De huidige sessie verbergen."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Verbergen"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Hervatten"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellingen"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactief, check de app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Fout. Opnieuw proberen…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Niet gevonden"</string>
diff --git a/packages/SystemUI/res/values-nl/strings_tv.xml b/packages/SystemUI/res/values-nl/strings_tv.xml
index c8dd088f8725..3b8e3201e440 100644
--- a/packages/SystemUI/res/values-nl/strings_tv.xml
+++ b/packages/SystemUI/res/values-nl/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Scherm-in-scherm"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Naamloos programma)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP sluiten"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Volledig scherm"</string>
<string name="mic_active" msgid="5766614241012047024">"Microfoon actief"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s heeft toegang tot je microfoon gehad"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 2ef1be280906..e112f446acb1 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ଖୋଲିବା ପାଇଁ ପୁଣି ଟାପ୍‍ କରନ୍ତୁ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ଖୋଲିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ପୁଣି ଚେଷ୍ଟା କରିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ଏହି ଡିଭାଇସଟି ଆପଣଙ୍କ ସଂସ୍ଥାର ଅଟେ"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ଏହି ଡିଭାଇସଟି <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>ର ଅଟେ"</string>
<string name="phone_hint" msgid="6682125338461375925">"ଫୋନ୍‍ ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ"</string>
<string name="voice_hint" msgid="7476017460191291417">"ଭଏସ୍‍ ସହାୟକ ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ"</string>
<string name="camera_hint" msgid="4519495795000658637">"କ୍ୟାମେରା ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ପ୍ରୋଫାଇଲ୍ ନିରୀକ୍ଷଣ କରାଯାଇପାରେ।"</string>
<string name="vpn_footer" msgid="3457155078010607471">"ନେଟ୍‌ୱର୍କ ନୀରିକ୍ଷଣ କରାଯାଇପାରେ"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ନେଟ୍‌ୱର୍କକୁ ନିରୀକ୍ଷଣ କରାଯାଇପାରେ"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ଏହି ଡିଭାଇସର ମାଲିକାନା ଆପଣଙ୍କ ସଂସ୍ଥା ପାଖରେ ଅଛି ଏବଂ ଏହା ନେଟୱାର୍କ ଟ୍ରାଫିକର ନିରୀକ୍ଷଣ କରିପାରେ"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"ଏହି ଡିଭାଇସଟି <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ର ଅଟେ ଏବଂ ଏହା ନେଟୱାର୍କ ଟ୍ରାଫିକକୁ ନିରୀକ୍ଷଣ କରିପାରେ"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ଏହି ଡିଭାଇସଟି ଆପଣଙ୍କ ସଂସ୍ଥାର ଅଟେ ଏବଂ ଏହା <xliff:g id="VPN_APP">%1$s</xliff:g> ସହ ସଂଯୁକ୍ତ ଅଛି"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ଏହି ଡିଭାଇସଟି <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ର ଅଟେ ଏବଂ ଏହା <xliff:g id="VPN_APP">%2$s</xliff:g> ସହ ସଂଯୁକ୍ତ ଅଛି"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ଏହି ଡିଭାଇସଟି ଆପଣଙ୍କ ସଂସ୍ଥାର ଅଟେ"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ଏହି ଡିଭାଇସଟି <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ର ଅଟେ"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ଏହି ଡିଭାଇସଟି ଆପଣଙ୍କ ସଂସ୍ଥାର ଅଟେ ଏବଂ ଏହା VPNଗୁଡ଼ିକ ସହ ସଂଯୁକ୍ତ ଅଛି"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ଏହି ଡିଭାଇସଟି <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ର ଅଟେ ଏବଂ ଏହା VPNଗୁଡ଼ିକ ସହ ସଂଯୁକ୍ତ ଅଛି"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲରେ ଆପଣଙ୍କ ସଂସ୍ଥା ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରିପାରନ୍ତି"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲରେ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରିପାରନ୍ତି"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ନେଟୱର୍କ ନୀରିକ୍ଷଣ କରାଯାଇପାରେ"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ଏହି ଡିଭାଇସଟି VPNଗୁଡ଼ିକ ସହ ସଂଯୁକ୍ତ ଅଛି"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"ଆପଣଙ୍କ ୱାର୍କ ପ୍ରୋଫାଇଲ୍ <xliff:g id="VPN_APP">%1$s</xliff:g> ସହ ସଂଯୁକ୍ତ ଅଛି"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ପ୍ରୋଫାଇଲ୍ <xliff:g id="VPN_APP">%1$s</xliff:g> ସହ ସଂଯୁକ୍ତ ଅଛି"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ଏହି ଡିଭାଇସଟି <xliff:g id="VPN_APP">%1$s</xliff:g> ସହ ସଂଯୁକ୍ତ ଅଛି"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ଡିଭାଇସ୍‌ ପରିଚାଳନା"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"ପ୍ରୋଫାଇଲ୍ ନୀରିକ୍ଷଣ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ନେଟ୍‌ୱର୍କ ନୀରିକ୍ଷଣ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN ଅକ୍ଷମ କରନ୍ତୁ"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN ବିଛିନ୍ନ କରନ୍ତୁ"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"ପଲିସୀ ଦେଖନ୍ତୁ"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ଏହି ଡିଭାଇସଟି <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ର ଅଟେ।\n\nଆପଣଙ୍କ IT ଆଡମିନ୍ ସେଟିଂସ୍, କର୍ପୋରେଟ୍ ଆକ୍ସେସ୍, ଆପ୍ସ, ଆପଣଙ୍କ ଡିଭାଇସ୍ ସହ ସମ୍ବନ୍ଧିତ ଡାଟା ଏବଂ ଆପଣଙ୍କ ଡିଭାଇସର ଲୋକେସନ୍ ସୂଚନାକୁ ନିରୀକ୍ଷଣ ଏବଂ ପରିଚାଳନା କରିପାରିବେ।\n\nଅଧିକ ସୂଚନା ପାଇଁ, ଆପଣଙ୍କ IT ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ଏହି ଡିଭାଇସଟି ଆପଣଙ୍କ ସଂସ୍ଥାର ଅଟେ।\n\nଆପଣଙ୍କ IT ଆଡମିନ୍ ସେଟିଂସ୍, କର୍ପୋରେଟ୍ ଆକ୍ସେସ୍, ଆପ୍ସ, ଆପଣଙ୍କ ଡିଭାଇସ୍ ସହ ସମ୍ବନ୍ଧିତ ଡାଟା ଏବଂ ଆପଣଙ୍କ ଡିଭାଇସର ଲୋକେସନ୍ ସୂଚନାକୁ ନିରୀକ୍ଷଣ ଏବଂ ପରିଚାଳନା କରିପାରିବେ।\n\nଅଧିକ ସୂଚନା ପାଇଁ, ଆପଣଙ୍କ IT ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ଏହି ଡିଭାଇସରେ ଆପଣଙ୍କ ସଂସ୍ଥା ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରିଛନ୍ତି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲରେ ଆପଣଙ୍କ ସଂସ୍ଥା ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରିଛନ୍ତି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ଏହି ଡିଭାଇସରେ ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରାଯାଇଛି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"ନୀରବ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ଡିଫଲ୍ଟ"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"ବବଲ୍"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ସ୍ୱଚାଳିତ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"କୌଣସି ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ନାହିଁ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"କୌଣସି ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ନାହିଁ ଏବଂ ବାର୍ତ୍ତାଳାପ ବିଭାଗର ନିମ୍ନରେ ଦେଖାଯାଏ"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ। <xliff:g id="APP_NAME">%1$s</xliff:g>ରୁ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଡିଫଲ୍ଟ ଭାବରେ ବବଲ୍ ହୁଏ।"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ଏହି ବିଷୟବସ୍ତୁ ପାଇଁ ଏକ ଭାସମାନ ସର୍ଟକଟ୍ ସହ ଆପଣଙ୍କର ଧ୍ୟାନ ଦିଅନ୍ତୁ।"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ଏହି ବିଜ୍ଞପ୍ତି ପ୍ରାପ୍ତ ହେବା ସମୟରେ ସାଉଣ୍ଡ ହେବା ଉଚିତ ନା ଭାଇବ୍ରେସନ୍ ତାହା ସିଷ୍ଟମକୁ ସ୍ଥିର କରିବାକୁ ଦିଅନ୍ତୁ"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"ବାର୍ତ୍ତାଳାପ ବିଭାଗର ଶୀର୍ଷରେ ଦେଖାଏ, ଭାସମାନ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, ଲକ୍ ସ୍କ୍ରିନରେ ପ୍ରୋଫାଇଲ୍ ଛବି ଡିସପ୍ଲେ କରେ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ସେଟିଂସ୍"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ପ୍ରାଥମିକତା"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ଏହି ଆପ୍, ଆପଣଙ୍କର ସ୍କ୍ରୀନ୍ ଉପରେ ଥିବା ଅନ୍ୟ ଆପ୍ ଉପରେ ପ୍ରଦର୍ଶିତ ହେଉଛି ଏବଂ ମାଇକ୍ରୋଫୋନ୍ ଓ କ୍ୟାମେରା ବ୍ୟବହାର କରୁଛି।"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ସେଟିଂସ୍"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ଠିକ୍ ଅଛି"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ଏହି ବିଜ୍ଞପ୍ତିକୁ ସିଷ୍ଟମ୍ ଦ୍ୱାରା ନୀରବ କରାଯାଇଥିଲା।"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ଏହି ବିଜ୍ଞପ୍ତିକୁ ସିଷ୍ଟମ୍ ଦ୍ୱାରା ପ୍ରମୋଟ୍ କରାଯାଇଥିଲା।"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ଏହି ବିଜ୍ଞପ୍ତିକୁ ସିଷ୍ଟମ୍ ଦ୍ୱାରା ଡିମୋଟ୍ କରାଯାଇଥିଲା।"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"ଏହା ଠିକ୍ ଥିଲା କି?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"ଆପଣଙ୍କ ମତାମତ ପାଇଁ ଧନ୍ୟବାଦ!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ଠିକ୍ ଅଛି"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣ ଖୋଲା ଯାଇଛି"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣ ବନ୍ଦ ହୋଇଛି"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"ଏହି ଚ୍ୟାନେଲରୁ ବିଜ୍ଞପ୍ତିକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ସେଟିଙ୍ଗର କ୍ରମ ସଂଶୋଧନ କରନ୍ତୁ।"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ପୃଷ୍ଠା <xliff:g id="ID_1">%1$d</xliff:g> ମୋଟ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ଲକ୍‌ ସ୍କ୍ରୀନ୍‌"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"ବଢ଼ାନ୍ତୁ"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"ଛୋଟ କରନ୍ତୁ"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"ବନ୍ଦ କରନ୍ତୁ"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ସେଟିଂସ୍"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"ଖାରଜ କରିବାକୁ ତଳକୁ ଟାଣନ୍ତୁ"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"ମେନୁ"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> \"ଛବି-ଭିତରେ-ଛବି\"ରେ ଅଛି"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"ଏହି ବୈଶିଷ୍ଟ୍ୟ <xliff:g id="NAME">%s</xliff:g> ବ୍ୟବହାର ନକରିବାକୁ ଯଦି ଆପଣ ଚାହାଁନ୍ତି, ସେଟିଙ୍ଗ ଖୋଲିବାକୁ ଟାପ୍‍ କରନ୍ତୁ ଏବଂ ଏହା ଅଫ୍‍ କରିଦିଅନ୍ତୁ।"</string>
- <string name="pip_play" msgid="333995977693142810">"ପ୍ଲେ କରନ୍ତୁ"</string>
- <string name="pip_pause" msgid="1139598607050555845">"ପଜ୍‍ କରନ୍ତୁ"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"ପରବର୍ତ୍ତୀକୁ ଯାଆନ୍ତୁ"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"ପୂର୍ବବର୍ତ୍ତୀକୁ ଛାଡ଼ନ୍ତୁ"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"ଗରମ ହେତୁ ଫୋନ୍‍ ଅଫ୍‍ କରିଦିଆଗଲା"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"ଆପଣଙ୍କ ଫୋନ୍‍ ବର୍ତ୍ତମାନ ସାମାନ୍ୟ ଅବସ୍ଥାରେ ଚାଲୁଛି"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"ଆପଣଙ୍କ ଫୋନ୍ ବର୍ତ୍ତମାନ ସାମାନ୍ୟ ରୂପେ ଚାଲୁଛି।\nଅଧିକ ସୂଚନା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"ଆପଣଙ୍କ ଫୋନ୍‍ ବହୁତ ଗରମ ଥିଲା, ତେଣୁ ଏହାକୁ ଥଣ୍ଡା କରାଯିବାକୁ ଅଫ୍‍ କରିଦିଆଗଲା। ଆପଣଙ୍କ ଫୋନ୍‍ ବର୍ତ୍ତମାନ ସାମାନ୍ୟ ଅବସ୍ଥାରେ ଚାଲୁଛି।\n\nଆପଣଙ୍କ ଫୋନ୍‍ ଅଧିକ ଗରମ ହୋଇଯାଇପାରେ ଯଦି ଆପଣ:\n • ରିସୋର୍ସ-ଇଣ୍ଟେନସିଭ୍‍ ଆପ୍‍ (ଯେପରିକି ଗେମିଙ୍ଗ, ଭିଡିଓ, କିମ୍ବା ନେଭିଗେସନ୍‍ ଆପ୍‍) ବ୍ୟବହାର କରନ୍ତି\n • ବଡ ଫାଇଲ୍‍ ଡାଉନଲୋଡ୍ କିମ୍ବା ଅପଲୋଡ୍‍ କରନ୍ତି\n • ଅଧିକ ତାପମାତ୍ରାରେ ଆପଣଙ୍କ ଫୋନ୍‍ ବ୍ୟବହାର କରନ୍ତି"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ଯତ୍ନ ନେବା ପାଇଁ ଷ୍ଟେପଗୁଡ଼ିକ ଦେଖନ୍ତୁ"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ଫୋନ୍‍ ଗରମ ହୋଇଯାଉଛି"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ଫୋନ୍‍ ଥଣ୍ଡା ହେବା ସମୟରେ କିଛି ଫିଚର୍ ସୀମିତ ଭାବେ କାମ କରିଥାଏ"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ଫୋନ୍ ଥଣ୍ଡା ହେବା ସମୟରେ କିଛି ଫିଚର୍ ଠିକ ଭାବେ କାମ କରିନଥାଏ।\nଅଧିକ ସୂଚନା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ଆପଣଙ୍କ ଫୋନ୍‍ ସ୍ୱଚାଳିତ ଭାବେ ଥଣ୍ଡା ହେବାକୁ ଚେଷ୍ଟା କରିବ। ଆପଣ ତଥାପି ନିଜ ଫୋନ୍‍ ବ୍ୟବହାର କରିପାରିବେ, କିନ୍ତୁ ଏହା ଧୀରେ ଚାଲିପାରେ।\n\nଆପଣଙ୍କ ଫୋନ୍‍ ଥଣ୍ଡା ହୋଇଯିବାପରେ, ଏହା ସାମାନ୍ୟ ଭାବେ ଚାଲିବ।"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ଯତ୍ନ ନେବା ପାଇଁ ଷ୍ଟେପଗୁଡ଼ିକ ଦେଖନ୍ତୁ"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ଚାର୍ଜର୍‍ ଅନ୍‍ପ୍ଲଗ୍‌ କରନ୍ତୁ"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"ଏହି ଡିଭାଇସ୍ ଚାର୍ଜ କରିବାରେ ଗୋଟିଏ ସମସ୍ୟା ଅଛି। ଯେହେତୁ କେବଳ ଗରମ ହୋଇଯାଇପାରେ, ତେଣୁ ପାୱାର୍ ଆଡପ୍ଟର୍ ଅନ୍‌ପ୍ଲଗ୍‌ କରନ୍ତୁ ଏବଂ ଯତ୍ନ ନିଅନ୍ତୁ।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ସେବା ସମ୍ବନ୍ଧିତ ଷ୍ଟେପ୍‌ଗୁଡ଼ିକ ଦେଖନ୍ତୁ"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ପୁଣି ସଜାଇବାକୁ ସେଗୁଡ଼ିକୁ ଧରି ଟାଣନ୍ତୁ"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"ସମସ୍ତ ନିୟନ୍ତ୍ରଣ କାଢ଼ି ଦିଆଯାଇଛି"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ପରିବର୍ତ୍ତନଗୁଡ଼ିକ ସେଭ୍ କରାଯାଇନାହିଁ"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ଅନ୍ୟ ଆପ୍ ଦେଖନ୍ତୁ"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଲୋଡ୍ କରାଯାଇପାରିଲା ନାହିଁ। ଆପ୍ ସେଟିଂସ୍ ପରିବର୍ତ୍ତନ ହୋଇନାହିଁ ବୋଲି ନିଶ୍ଚିତ କରିବାକୁ <xliff:g id="APP">%s</xliff:g> ଆପ୍ ଯାଞ୍ଚ କରନ୍ତୁ।"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"ସୁସଙ୍ଗତ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ଅନ୍ୟ"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> ପାଇଁ ପରିବର୍ତ୍ତନ ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"ଅଧିକ ଦେଖିବାକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ସୁପାରିଶଗୁଡ଼ିକ ଲୋଡ୍ କରାଯାଉଛି"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ଏହି ମିଡିଆ ସେସନ୍ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"ମିଡିଆ"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"ବର୍ତ୍ତମାନର ସେସନ୍ ଲୁଚାନ୍ତୁ।"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ଲୁଚାନ୍ତୁ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ନିଷ୍କ୍ରିୟ ଅଛି, ଆପ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"ତ୍ରୁଟି, ପୁଣି ଚେଷ୍ଟା କରୁଛି…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ମିଳିଲା ନାହିଁ"</string>
diff --git a/packages/SystemUI/res/values-or/strings_tv.xml b/packages/SystemUI/res/values-or/strings_tv.xml
index 4593d6ad8c6c..b44dc3bffe12 100644
--- a/packages/SystemUI/res/values-or/strings_tv.xml
+++ b/packages/SystemUI/res/values-or/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ପିକଚର୍-ଇନ୍-ପିକଚର୍"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(କୌଣସି ଟାଇଟଲ୍‍ ପ୍ରୋଗ୍ରାମ୍‍ ନାହିଁ)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP ବନ୍ଦ କରନ୍ତୁ"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍"</string>
<string name="mic_active" msgid="5766614241012047024">"ମାଇକ୍ରୋଫୋନ୍ ସକ୍ରିୟ"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ଆପଣଙ୍କର ମାଇକ୍ରୋଫୋନ୍‌କୁ ଆକ୍ସେସ୍ କରିଛି"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 030939c2019a..6ac824b59e6b 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"ਖੋਲ੍ਹਣ ਲਈ ਦੁਬਾਰਾ ਟੈਪ ਕਰੋ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ਖੋਲ੍ਹਣ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="keyguard_retry" msgid="886802522584053523">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਉੱਤੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string>
<string name="phone_hint" msgid="6682125338461375925">"ਫ਼ੋਨ ਲਈ ਪ੍ਰਤੀਕ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="voice_hint" msgid="7476017460191291417">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ ਲਈ ਪ੍ਰਤੀਕ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="camera_hint" msgid="4519495795000658637">"ਕੈਮਰੇ ਲਈ ਪ੍ਰਤੀਕ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ਪ੍ਰੋਫਾਈਲ ਦਾ ਨਿਰੀਖਣ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ"</string>
<string name="vpn_footer" msgid="3457155078010607471">"ਨੈੱਟਵਰਕ ਦਾ ਨਿਰੀਖਣ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ਹੋ ਸਕਦਾ ਹੈ ਨੈੱਟਵਰਕ ਦੀ ਨਿਗਰਾਨੀ ਹੋ ਰਹੀ ਹੋਵੇ"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਕੋਲ ਇਸ ਡੀਵਾਈਸ ਦੀ ਮਲਕੀਅਤ ਹੈ ਅਤੇ ਇਹ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਕੋਲ ਇਸ ਡੀਵਾਈਸ ਦੀ ਮਲਕੀਅਤ ਹੈ ਅਤੇ ਇਹ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ ਅਤੇ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ ਅਤੇ <xliff:g id="VPN_APP">%2$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ ਅਤੇ VPN ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ ਅਤੇ VPN ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ਨੈੱਟਵਰਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ਇਹ ਡੀਵਾਈਸ VPN ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"ਤੁਹਾਡਾ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"ਤੁਹਾਡਾ ਨਿੱਜੀ ਪ੍ਰੋਫਾਈਲ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਨ"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"ਪ੍ਰੋਫਾਈਲ ਦਾ ਨਿਰੀਖਣ ਕਰਨਾ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ਨੈੱਟਵਰਕ ਨਿਰੀਖਣ ਕਰ ਰਿਹਾ ਹੈ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"ਨੀਤੀਆਂ ਦੇਖੋ"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ।\n\nਤੁਹਾਡਾ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਸੰਬੰਧਿਤ ਸੈਟਿੰਗਾਂ, ਕਾਰਪੋਰੇਟ ਪਹੁੰਚ, ਐਪਾਂ, ਡਾਟੇ ਅਤੇ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਟਿਕਾਣਾ ਜਾਣਕਾਰੀ ਦੀ ਨਿਗਰਾਨੀ ਅਤੇ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦਾ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ।\n\nਤੁਹਾਡਾ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਸੰਬੰਧਿਤ ਸੈਟਿੰਗਾਂ, ਕਾਰਪੋਰੇਟ ਪਹੁੰਚ, ਐਪਾਂ, ਡਾਟੇ ਅਤੇ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਟਿਕਾਣਾ ਜਾਣਕਾਰੀ ਦੀ ਨਿਗਰਾਨੀ ਅਤੇ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦਾ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਇੱਕ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਥਾਰਟੀ ਸਥਾਪਤ ਕੀਤੀ ਗਈ ਹੈ। ਤੁਹਾਡੇ ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ ਜਾਂ ਉਸਨੂੰ ਸੋਧਿਆ ਜਾ ਸਕਦਾ ਹੈ।"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਇੱਕ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਥਾਰਟੀ ਸਥਾਪਤ ਕੀਤੀ ਗਈ ਹੈ। ਤੁਹਾਡੇ ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ ਜਾਂ ਉਸਨੂੰ ਸੋਧਿਆ ਜਾ ਸਕਦਾ ਹੈ।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ਇੱਕ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਥਾਰਟੀ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਥਾਪਤ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਤੁਹਾਡੇ ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ ਜਾਂ ਉਸਨੂੰ ਸੋਧਿਆ ਜਾ ਸਕਦਾ ਹੈ।"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"ਸ਼ਾਂਤ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"ਬੁਲਬੁਲਾ"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ਸਵੈਚਲਿਤ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਗੱਲਬਾਤ ਸੈਕਸ਼ਨ ਵਿੱਚ ਹੇਠਲੇ ਪਾਸੇ ਦਿਸਦੀਆਂ ਹਨ"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ਫ਼ੋਨ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ਫ਼ੋਨ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ। ਪੂਰਵ-ਨਿਰਧਾਰਤ ਤੌਰ \'ਤੇ <xliff:g id="APP_NAME">%1$s</xliff:g> ਬਬਲ ਤੋਂ ਗੱਲਾਂਬਾਤਾਂ।"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ਇਸ ਸਮੱਗਰੀ ਦੇ ਅਸਥਿਰ ਸ਼ਾਰਟਕੱਟ ਨਾਲ ਆਪਣਾ ਧਿਆਨ ਕੇਂਦਰਿਤ ਰੱਖੋ।"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ਸਿਸਟਮ ਨੂੰ ਨਿਰਧਾਰਤ ਕਰਨ ਦਿਓ ਕਿ ਇਸ ਸੂਚਨਾ ਲਈ ਕੋਈ ਧੁਨੀ ਵਜਾਉਣੀ ਚਾਹੀਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਕਰਨੀ ਚਾਹੀਦੀ ਹੈ"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"ਗੱਲਬਾਤ ਸੈਕਸ਼ਨ ਦੇ ਸਿਖਰ \'ਤੇ ਦਿਖਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ, ਬਬਲ ਵਜੋਂ ਦਿਸਦੀਆਂ ਹਨ, ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਦਿਖਾਈ ਜਾਂਦੀ ਹੈ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ਸੈਟਿੰਗਾਂ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ਤਰਜੀਹ"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ਇਹ ਐਪ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਹੋਰਾਂ ਐਪਾਂ ਉੱਪਰ ਦਿਖਾਈ ਜਾ ਰਹੀ ਹੈ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਅਤੇ ਕੈਮਰੇ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ।"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ਸੈਟਿੰਗਾਂ"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ਠੀਕ ਹੈ"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ਸਿਸਟਮ ਵੱਲੋਂ ਇਹ ਸੂਚਨਾ ਸ਼ਾਂਤ ਕਰ ਦਿੱਤੀ ਗਈ ਸੀ।"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ਸਿਸਟਮ ਵੱਲੋਂ ਇਸ ਸੂਚਨਾ ਦਾ ਦਰਜਾ ਵਧਾ ਦਿੱਤਾ ਗਿਆ ਸੀ।"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ਸਿਸਟਮ ਵੱਲੋਂ ਇਸ ਸੂਚਨਾ ਦਾ ਦਰਜਾ ਘਟਾ ਦਿੱਤਾ ਗਿਆ ਸੀ।"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"ਕੀ ਇਹ ਸਹੀ ਸੀ?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"ਤੁਹਾਡੇ ਵਿਚਾਰ ਲਈ ਧੰਨਵਾਦ!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ਠੀਕ ਹੈ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨੂੰ ਖੋਲ੍ਹਿਆ ਗਿਆ"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"ਇਸ ਚੈਨਲ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ਸੈਟਿੰਗਾਂ ਦੇ ਕ੍ਰਮ ਦਾ ਸੰਪਾਦਨ ਕਰੋ।"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ਦਾ <xliff:g id="ID_1">%1$d</xliff:g> ਪੰਨਾ"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">" ਲਾਕ ਸਕ੍ਰੀਨ"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"ਵਿਸਤਾਰ ਕਰੋ"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"ਛੋਟਾ ਕਰੋ"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"ਬੰਦ ਕਰੋ"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ਸੈਟਿੰਗਾਂ"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"ਖਾਰਜ ਕਰਨ ਲਈ ਹੇਠਾਂ ਘਸੀਟੋ"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"ਮੀਨੂ"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> ਤਸਵੀਰ-ਅੰਦਰ-ਤਸਵੀਰ ਵਿੱਚ ਹੈ"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"ਜੇਕਰ ਤੁਸੀਂ ਨਹੀਂ ਚਾਹੁੰਦੇ ਕਿ <xliff:g id="NAME">%s</xliff:g> ਐਪ ਇਸ ਵਿਸ਼ੇਸ਼ਤਾ ਦੀ ਵਰਤੋਂ ਕਰੇ, ਤਾਂ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਇਸਨੂੰ ਬੰਦ ਕਰੋ।"</string>
- <string name="pip_play" msgid="333995977693142810">"ਚਲਾਓ"</string>
- <string name="pip_pause" msgid="1139598607050555845">"ਵਿਰਾਮ ਦਿਓ"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"ਅਗਲੇ \'ਤੇ ਜਾਓ"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"ਪਿਛਲੇ \'ਤੇ ਜਾਓ"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"ਗਰਮ ਹੋਣ ਕਾਰਨ ਫ਼ੋਨ ਬੰਦ ਹੋ ਗਿਆ"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"ਤੁਹਾਡਾ ਫ਼ੋਨ ਹੁਣ ਸਹੀ ਚੱਲ ਰਿਹਾ ਹੈ"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"ਤੁਹਾਡਾ ਫ਼ੋਨ ਹੁਣ ਸਹੀ ਚੱਲ ਰਿਹਾ ਹੈ।\nਵਧੇਰੇ ਜਾਣਕਾਰੀ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">\n"ਤੁਹਾਡਾ ਫ਼ੋਨ ਬਹੁਤ ਗਰਮ ਸੀ, ਇਸ ਲਈ ਇਹ ਠੰਡਾ ਹੋਣ ਵਾਸਤੇ ਬੰਦ ਹੋ ਗਿਆ ਸੀ। ਤੁਹਾਡਾ ਫ਼ੋਨ ਹੁਣ ਸਹੀ ਚੱਲ ਰਿਹਾ ਹੈ।\n\nਤੁਹਾਡਾ ਫ਼ੋਨ ਬਹੁਤ ਗਰਮ ਹੋ ਸਕਦਾ ਹੈ ਜੇ:\n • ਤੁਸੀਂ ਸਰੋਤਾਂ ਦੀ ਵੱਧ ਵਰਤੋਂ ਵਾਲੀਆਂ ਐਪਾਂ (ਜਿਵੇਂ ਗੇਮਿੰਗ, ਵੀਡੀਓ, ਜਾਂ ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ ਐਪਾਂ) ਵਰਤਦੇ ਹੋ • ਵੱਡੀਆਂ ਫ਼ਾਈਲਾਂ ਡਾਊਨਲੋਡ ਜਾਂ ਅੱਪਲੋਡ ਕਰਦੇ ਹੋ\n • ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਉੱਚ ਤਾਪਮਾਨਾਂ ਵਿੱਚ ਵਰਤਦੇ ਹੋ"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ਦੇਖਭਾਲ ਦੇ ਪੜਾਅ ਦੇਖੋ"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ਫ਼ੋਨ ਗਰਮ ਹੋ ਰਿਹਾ ਹੈ"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ਫ਼ੋਨ ਦੇ ਠੰਡਾ ਹੋਣ ਦੇ ਦੌਰਾਨ ਕੁਝ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਸੀਮਿਤ ਹੁੰਦੀਆਂ ਹਨ"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ਫ਼ੋਨ ਦੇ ਠੰਡਾ ਹੋਣ ਦੇ ਦੌਰਾਨ ਕੁਝ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਸੀਮਤ ਹੁੰਦੀਆਂ ਹਨ।\nਵਧੇਰੇ ਜਾਣਕਾਰੀ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ਤੁਹਾਡਾ ਫ਼ੋਨ ਸਵੈਚਲਿਤ ਰੂਪ ਵਿੱਚ ਠੰਡਾ ਹੋਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੇਗਾ। ਤੁਸੀਂ ਹਾਲੇ ਵੀ ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਵਰਤ ਸਕਦੇ ਹੋ, ਪਰੰਤੂ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਹ ਵਧੇਰੇ ਹੌਲੀ ਚੱਲੇ।\n\nਇੱਕ ਵਾਰ ਠੰਡਾ ਹੋਣ ਤੋਂ ਬਾਅਦ ਤੁਹਾਡਾ ਫ਼ੋਨ ਸਧਾਰਨ ਤੌਰ \'ਤੇ ਚੱਲੇਗਾ।"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ਦੇਖਭਾਲ ਦੇ ਪੜਾਅ ਦੇਖੋ"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ਚਾਰਜਰ ਨੂੰ ਕੱਢੋ"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"ਇਸ ਡੀਵਾਈਸ ਨੂੰ ਚਾਰਜ ਕਰਨ ਵਿੱਚ ਕੋਈ ਸਮੱਸਿਆ ਆ ਗਈ ਹੈ। ਪਾਵਰ ਅਡਾਪਟਰ ਨੂੰ ਕੱਢੋ ਅਤੇ ਧਿਆਨ ਰੱਖੋ ਸ਼ਾਇਦ ਕੇਬਲ ਗਰਮ ਹੋਵੇ।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ਦੇਖਭਾਲ ਦੇ ਪੜਾਅ ਦੇਖੋ"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"ਕੰਟਰੋਲਾਂ ਨੂੰ ਮੁੜ-ਵਿਵਸਥਿਤ ਕਰਨ ਲਈ ਫੜ੍ਹ ਕੇ ਘਸੀਟੋ"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"ਸਾਰੇ ਕੰਟਰੋਲ ਹਟਾਏ ਗਏ"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ਤਬਦੀਲੀਆਂ ਨੂੰ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ਹੋਰ ਐਪਾਂ ਦੇਖੋ"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"ਕੰਟਰੋਲਾਂ ਨੂੰ ਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਇਹ ਪੱਕਾ ਕਰਨ ਲਈ <xliff:g id="APP">%s</xliff:g> ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ ਕਿ ਐਪ ਸੈਟਿੰਗਾਂ ਨਹੀਂ ਬਦਲੀਆਂ ਹਨ।"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"ਕੋਈ ਅਨੁਰੂਪ ਕੰਟਰੋਲ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ਹੋਰ"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> ਲਈ ਤਬਦੀਲੀ ਦੀ ਤਸਦੀਕ ਕਰੋ"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"ਹੋਰ ਦੇਖਣ ਲਈ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ਸਿਫ਼ਾਰਸ਼ਾਂ ਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ਇਸ ਮੀਡੀਆ ਸੈਸ਼ਨ ਨੂੰ ਬੰਦ ਕਰੋ"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"ਮੀਡੀਆ"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਓ।"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ਲੁਕਾਓ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ਸੈਟਿੰਗਾਂ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ਅਕਿਰਿਆਸ਼ੀਲ, ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"ਗੜਬੜ, ਮੁੜ ਕੋਸ਼ਿਸ਼ ਹੋ ਰਹੀ ਹੈ…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ਨਹੀਂ ਮਿਲਿਆ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings_tv.xml b/packages/SystemUI/res/values-pa/strings_tv.xml
index fd567698088b..f5300b318d10 100644
--- a/packages/SystemUI/res/values-pa/strings_tv.xml
+++ b/packages/SystemUI/res/values-pa/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(ਸਿਰਲੇਖ-ਰਹਿਤ ਪ੍ਰੋਗਰਾਮ)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP ਬੰਦ ਕਰੋ"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
<string name="mic_active" msgid="5766614241012047024">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਕਿਰਿਆਸ਼ੀਲ"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ਨੇ ਤੁਹਾਡੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਤੱਕ ਪਹੁੰਚ ਕੀਤੀ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 8dae56eede0b..f5ef1a045bdd 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -458,10 +458,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Kliknij ponownie, by otworzyć"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Przesuń w górę, by otworzyć"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Przesuń w górę, by spróbować ponownie"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"To urządzenie należy do Twojej organizacji"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Właściciel tego urządzenia: <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Aby włączyć telefon, przesuń palcem od ikony"</string>
<string name="voice_hint" msgid="7476017460191291417">"Aby uzyskać pomoc głosową, przesuń palcem od ikony"</string>
<string name="camera_hint" msgid="4519495795000658637">"Przesuń palcem od ikony, by włączyć aparat"</string>
@@ -529,33 +527,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil może być monitorowany"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Sieć może być monitorowana"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Sieć może być monitorowana"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Twoja organizacja jest właścicielem tego urządzenia i może monitorować ruch w sieci"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Organizacja <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> jest właścicielem tego urządzenia i może monitorować ruch w sieci"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"To urządzenie należy do Twojej organizacji i jest połączone z siecią <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"To urządzenie należy do organizacji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i jest połączone z siecią <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"To urządzenie należy do Twojej organizacji"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Właściciel tego urządzenia: <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"To urządzenie należy do Twojej organizacji i jest połączone z sieciami VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"To urządzenie należy do organizacji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i jest połączone z sieciami VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Twoja organizacja może monitorować ruch w sieci w Twoim profilu do pracy"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Organizacja <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> może monitorować ruch w sieci w Twoim profilu do pracy"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Sieć może być monitorowana"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"To urządzenie jest połączone z sieciami VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Twój profil służbowy jest połączony z siecią <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Twój profil osobisty jest połączony z siecią <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"To urządzenie jest połączone z siecią <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Zarządzanie urządzeniami"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Monitorowanie profilu"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Monitorowanie sieci"</string>
@@ -565,10 +551,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Wyłącz VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Rozłącz z VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Zobacz zasady"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"To urządzenie należy do organizacji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministrator IT może monitorować ustawienia, firmowe uprawnienia dostępu, aplikacje, dane dotyczące urządzenia i lokalizacji oraz nimi zarządzać.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem IT."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"To urządzenie należy do Twojej organizacji.\n\nAdministrator IT może monitorować ustawienia, firmowe uprawnienia dostępu, aplikacje, dane dotyczące urządzenia i lokalizacji oraz nimi zarządzać.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Twoja organizacja zainstalowała urząd certyfikacji na tym urządzeniu. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Twoja organizacja zainstalowała urząd certyfikacji w Twoim profilu do pracy. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Urząd certyfikacji zainstalowany na tym urządzeniu. Twój zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string>
@@ -733,15 +717,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Bez dźwięku"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Domyślne"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Dymek"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatycznie"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Brak dźwięku i wibracji"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Brak dźwięku i wibracji, wyświetla się niżej w sekcji rozmów"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Może włączyć dzwonek lub wibracje w zależności od ustawień telefonu"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Może włączyć dzwonek lub wibracje w zależności od ustawień telefonu. Rozmowy z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> są domyślnie wyświetlane jako dymki."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Przyciąga uwagę dzięki pływającym skrótom do treści."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Pozwól systemowi decydować, czy o powiadomieniu powinien informować dźwięk czy wibracja"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wyświetla się jako pływający dymek u góry sekcji rozmów, pokazuje zdjęcie profilowe na ekranie blokady"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ustawienia"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priorytet"</string>
@@ -762,18 +744,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ta aplikacja wyświetla się nad innymi aplikacjami na ekranie i używa mikrofonu oraz aparatu."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Ustawienia"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"System wyciszył to powiadomienie."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"System zmienił ważność tego powiadomienia na wyższą."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"System zmienił ważność tego powiadomienia na niższą."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Czy to było prawidłowe?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Dziękujemy za opinię"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Sterowanie powiadomieniami aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> otwarte"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Sterowanie powiadomieniami aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> zamknięte"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Zezwól na powiadomienia z tego kanału"</string>
@@ -950,26 +926,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Edytuj kolejność ustawień."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Strona <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ekran blokady"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Rozwiń"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimalizuj"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Zamknij"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Ustawienia"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Przeciągnij w dół, by zamknąć"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"Aplikacja <xliff:g id="NAME">%s</xliff:g> działa w trybie obraz w obrazie"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Jeśli nie chcesz, by aplikacja <xliff:g id="NAME">%s</xliff:g> korzystała z tej funkcji, otwórz ustawienia i wyłącz ją."</string>
- <string name="pip_play" msgid="333995977693142810">"Odtwórz"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Wstrzymaj"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Dalej"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Wstecz"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefon wyłączony: przegrzanie"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefon działa teraz normalnie"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefon działa teraz normalnie\nKliknij, by dowiedzieć się więcej"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon był zbyt gorący i wyłączył się, by obniżyć temperaturę. Urządzenie działa teraz normalnie.\n\nTelefon może się przegrzać, gdy:\n • Używasz aplikacji zużywających dużo zasobów (np. gier, nawigacji czy odtwarzaczy filmów)\n • Pobierasz lub przesyłasz duże pliki\n • Używasz telefonu w wysokiej temperaturze"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Zobacz instrukcję postępowania"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon się nagrzewa"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Podczas obniżania temperatury telefonu niektóre funkcje są ograniczone"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Podczas obniżania temperatury telefonu niektóre funkcje są ograniczone\nKliknij, by dowiedzieć się więcej"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon automatycznie podejmie próbę obniżenia temperatury. Możesz go wciąż używać, ale telefon może działać wolniej.\n\nGdy temperatura się obniży, telefon będzie działał normalnie."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Zobacz instrukcję postępowania"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Odłącz ładowarkę"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Podczas ładowania tego urządzenia wystąpił błąd. Odłącz zasilacz, zwracając uwagę na kabel, który może być gorący."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Zobacz instrukcję postępowania"</string>
@@ -1025,7 +989,7 @@
<string name="slice_permission_deny" msgid="6870256451658176895">"Odmów"</string>
<string name="auto_saver_title" msgid="6873691178754086596">"Kliknij, by zaplanować działanie oszczędzania baterii"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Oszczędzanie baterii włącza się, jeśli bateria jest prawie wyczerpana"</string>
- <string name="no_auto_saver_action" msgid="7467924389609773835">"Nie"</string>
+ <string name="no_auto_saver_action" msgid="7467924389609773835">"Nie, dziękuję"</string>
<string name="auto_saver_enabled_title" msgid="4294726198280286333">"Harmonogram oszczędzania baterii jest aktywny"</string>
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"Oszczędzanie baterii włączy się automatycznie, gdy poziom naładowania baterii spadnie poniżej <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Ustawienia"</string>
@@ -1090,6 +1054,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Przytrzymaj i przeciągnij, aby przestawić elementy sterujące"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Usunięto wszystkie elementy sterujące"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Zmiany nie zostały zapisane"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Wyświetl pozostałe aplikacje"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Nie udało się wczytać elementów sterujących. Sprawdź aplikację <xliff:g id="APP">%s</xliff:g>, aby upewnić się, że jej ustawienia się nie zmieniły."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Zgodne elementy sterujące niedostępne"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Inne"</string>
@@ -1107,8 +1072,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Potwierdź zmianę dotyczącą urządzenia <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Przesuń palcem, by zobaczyć więcej"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Wczytuję rekomendacje"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Zamknij tę sesję multimediów"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Ukryj bieżącą sesję."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ukryj"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Wznów"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Ustawienia"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Nieaktywny, sprawdź aplikację"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Błąd, próbuję jeszcze raz…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nie znaleziono"</string>
diff --git a/packages/SystemUI/res/values-pl/strings_tv.xml b/packages/SystemUI/res/values-pl/strings_tv.xml
index 852ea5056460..b060141b0275 100644
--- a/packages/SystemUI/res/values-pl/strings_tv.xml
+++ b/packages/SystemUI/res/values-pl/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Obraz w obrazie"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program bez tytułu)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Zamknij PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Pełny ekran"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon aktywny"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Aplikacja %1$s uzyskała dostęp do mikrofonu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 3050ccc794eb..c8eaa0ddc4fb 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Toque novamente para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Deslize para cima para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Deslize para cima para tentar novamente"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua organização"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Deslize a partir do ícone do telefone"</string>
<string name="voice_hint" msgid="7476017460191291417">"Deslize a partir do ícone de assistência de voz"</string>
<string name="camera_hint" msgid="4519495795000658637">"Deslize a partir do ícone da câmera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"O perfil pode ser monitorado"</string>
<string name="vpn_footer" msgid="3457155078010607471">"A rede pode ser monitorada"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"A rede pode ser monitorada"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Sua organização é dona deste dispositivo e pode monitorar o tráfego de rede"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"A organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> é dona deste dispositivo e pode monitorar o tráfego de rede"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Este dispositivo pertence à sua organização e está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está conectado a <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Este dispositivo pertence à sua organização"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Este dispositivo pertence à sua organização e está conectado a VPNs"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Este dispositivo pertence á organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está conectado a VPNs"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Sua organização pode monitorar o tráfego de rede no seu perfil de trabalho"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> pode monitorar o tráfego de rede no seu perfil de trabalho"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"A rede pode ser monitorada"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Este dispositivo está conectado a VPNs"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Seu perfil de trabalho está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Seu perfil pessoal está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Este dispositivo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gerenciamento de dispositivos"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Monitoramento de perfis"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Monitoramento de rede"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Desativar VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Desconectar VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Ver políticas"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nO administrador de TI pode monitorar e gerenciar configurações, acesso corporativo, apps, dados associados ao dispositivo e informações de local do dispositivo.\n\nPara saber mais, entre em contato com seu administrador de TI."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Este dispositivo pertence à sua organização.\n\nO administrador de TI pode monitorar e gerenciar configurações, acesso corporativo, apps, dados associados ao dispositivo e informações de local do dispositivo.\n\nPara saber mais, entre em contato com seu administrador de TI."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Sua organização instalou uma autoridade de certificação neste dispositivo. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Sua organização instalou uma autoridade de certificação no seu perfil de trabalho. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Uma autoridade de certificação foi instalada neste dispositivo. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
@@ -727,19 +711,17 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silencioso"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Padrão"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bolha"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém sua atenção com um atalho flutuante para esse conteúdo."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior de uma seção de conversa, em forma de balão, mostrando a foto do perfil na tela de bloqueio"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configurações"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
- <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversão"</string>
+ <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Os balões recentes e dispensados aparecerão aqui"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Não é possível modificar essas notificações."</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Este app está sobreposto a outros apps na sua tela e está usando o microfone e a câmera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Configurações"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Esta notificação foi silenciada pelo sistema."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Esta notificação foi promovida pelo sistema."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Esta notificação foi rebaixada pelo sistema."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Isso está correto?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Agradecemos seu feedback."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Permitir notificações desse canal"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editar ordem das configurações."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Tela de bloqueio"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Expandir"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizar"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Fechar"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Configurações"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Arraste para baixo para dispensar"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Se você não quer que o app <xliff:g id="NAME">%s</xliff:g> use este recurso, toque para abrir as configurações e desativá-lo."</string>
- <string name="pip_play" msgid="333995977693142810">"Reproduzir"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pausar"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Pular para a próxima"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Pular para a anterior"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"O smartphone foi desligado devido ao aquecimento"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"O smartphone está sendo executado normalmente agora"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"O smartphone está funcionando normalmente agora.\nToque para saber mais"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"O smartphone estava muito quente e foi desligado para resfriar. Agora, ele está sendo executado normalmente.\n\nO smartphone pode ficar quente demais se você:\n • usar apps que consomem muitos recursos (como apps de jogos, vídeos ou navegação);\n • fizer o download ou upload de arquivos grandes;\n • usar o smartphone em temperaturas altas."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver etapas de cuidado"</string>
<string name="high_temp_title" msgid="2218333576838496100">"O smartphone está esquentando"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Alguns recursos ficam limitados enquanto o smartphone é resfriado"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Alguns recursos ficam limitados enquanto o smartphone é resfriado.\nToque para saber mais"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Seu smartphone tentará se resfriar automaticamente. Você ainda poderá usá-lo, mas talvez ele fique mais lento.\n\nQuando o smartphone estiver resfriado, ele voltará ao normal."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver etapas de cuidado"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Desconecte o carregador"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Ocorreu um problema com o carregamento deste dispositivo. Desconecte o adaptador de energia com cuidado, já que o cabo pode estar quente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver etapas de cuidado"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Toque no controle, mantenha-o pressionado e arraste para reorganizar as posições."</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Todos os controles foram removidos"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"As mudanças não foram salvas"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Ver outros apps"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Não foi possível carregar os controles. Verifique o app <xliff:g id="APP">%s</xliff:g> para garantir que as configurações não tenham sido modificadas."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Controles compatíveis indisponíveis"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Outro"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirme a mudança para <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Deslize para ver mais"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Encerrar esta sessão de mídia"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Erro. Tentando novamente…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings_tv.xml b/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
index a0cbeafb9c9a..19cb4eacf402 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(programa sem título)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Fechar PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Tela cheia"</string>
<string name="mic_active" msgid="5766614241012047024">"Microfone ativado"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s acessou seu microfone"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 71ca29f0a2d5..341efff0e4cd 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Toque novamente para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Deslize rapidamente para cima para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Deslize rapidamente para cima para tentar novamente."</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua entidade."</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
<string name="phone_hint" msgid="6682125338461375925">"Deslize rapid. a partir do ícone para aceder ao telemóvel"</string>
<string name="voice_hint" msgid="7476017460191291417">"Deslize rapid. a partir do ícone para aceder ao assist. voz"</string>
<string name="camera_hint" msgid="4519495795000658637">"Deslize rapidamente a partir do ícone para aceder à câmara"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"O perfil pode ser monitorizado"</string>
<string name="vpn_footer" msgid="3457155078010607471">"A rede pode ser monitorizada"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"A rede pode ser monitorizada"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"A sua entidade gere este dispositivo e pode monitorizar o tráfego de rede."</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"A entidade <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> é proprietária deste dispositivo e pode monitorizar o tráfego de rede."</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Este dispositivo pertence à sua entidade e está ligado a <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está ligado a <xliff:g id="VPN_APP">%2$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Este dispositivo pertence à sua entidade."</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Este dispositivo pertence à sua entidade e está ligado a VPNs."</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está ligado a VPNs."</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"A sua entidade pode monitorizar o tráfego de rede no seu perfil de trabalho"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"A <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> pode monitorizar o tráfego de rede no seu perfil de trabalho"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"A rede pode ser monitorizada"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Este dispositivo está ligado a VPNs."</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"O seu perfil de trabalho está ligado a <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"O seu perfil pessoal está ligado a <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Este dispositivo está ligado a <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gestão de dispositivos"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Monitorização de perfis"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Monitorização da rede"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Desativar a VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Desligar VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Ver Políticas"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nO administrador de TI pode monitorizar e gerir as definições, o acesso empresarial, as apps, os dados associados ao dispositivo e as informações de localização do mesmo.\n\nContacte o administrador de TI para obter mais informações."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Este dispositivo pertence à sua entidade.\n\nO administrador de TI pode monitorizar e gerir as definições, o acesso empresarial, as apps, os dados associados ao dispositivo e as informações de localização do mesmo.\n\nContacte o administrador de TI para obter mais informações."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"A sua entidade instalou uma autoridade de certificação neste dispositivo. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"A sua entidade instalou uma autoridade de certificação no seu perfil de trabalho. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Está instalada uma autoridade de certificação neste dispositivo. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silencioso"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predefinição"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Balão"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sem som ou vibração"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sem som ou vibração e aparece na parte inferior na secção de conversas."</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode tocar ou vibrar com base nas definições do telemóvel."</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode tocar ou vibrar com base nas definições do telemóvel. As conversas da app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem como um balão por predefinição."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém a sua atenção com um atalho flutuante para este conteúdo."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se esta notificação deve emitir um som ou uma vibração"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior da secção de conversas, surge como um balão flutuante e apresenta a imagem do perfil no ecrã de bloqueio."</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Definições"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Esta app está a sobrepor-se a outras aplicações no ecrã e a utilizar o microfone e a câmara."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Definições"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"O sistema silenciou esta notificação."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"O sistema promoveu esta notificação."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"O sistema despromoveu esta notificação."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Estava correto?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Obrigado pelo seu feedback!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controlos de notificações da app <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Controlos de notificações da app <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Permitir notificações deste canal"</string>
@@ -835,7 +811,7 @@
<string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificações"</string>
<string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Atalhos de teclado"</string>
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Alterar esquema de teclado"</string>
- <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Aplicações"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Apps"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"Assistência"</string>
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Contactos"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editar a ordem das definições."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ecrã de bloqueio"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Expandir"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizar"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Fechar"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Definições"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Arrastar para baixo para ignorar"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"A app <xliff:g id="NAME">%s</xliff:g> está no modo de ecrã no ecrã"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Se não pretende que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string>
- <string name="pip_play" msgid="333995977693142810">"Reproduzir"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pausar"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Mudar para o seguinte"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Mudar para o anterior"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telem. deslig. devido ao calor"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"O telemóvel está a funcionar normalmente"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"O seu telemóvel já está a funcionar normalmente.\nToque para obter mais informações."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"O telemóvel estava muito quente, por isso desligou-se para arrefecer. Agora funciona normalmente.\n\nO telemóvel pode sobreaquecer se:\n • Utilizar aplicações que utilizam mais recursos (jogos, vídeo ou aplicações de navegação)\n • Transferir ou carregar ficheiros grandes\n • Utilizar em altas temperaturas"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Veja os passos de manutenção"</string>
<string name="high_temp_title" msgid="2218333576838496100">"O telemóvel está a aquecer"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Algumas funcionalidades são limitadas enquanto o telemóvel arrefece"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Algumas funcionalidades são limitadas enquanto o telemóvel arrefece.\nToque para obter mais informações."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"O telemóvel tenta arrefecer automaticamente. Pode continuar a utilizá-lo, mas este poderá funcionar mais lentamente.\n\nAssim que o telemóvel tiver arrefecido, funcionará normalmente."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Veja os passos de manutenção"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Desligar o carregador"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Ocorreu um problema ao carregar este dispositivo. Desligue o transformador e tenha cuidado porque o cabo pode estar quente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver os passos a ter em consideração"</string>
@@ -1001,7 +965,7 @@
<string name="qs_dnd_until" msgid="7844269319043747955">"Até à(s) <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="qs_dnd_keep" msgid="3829697305432866434">"Manter"</string>
<string name="qs_dnd_replace" msgid="7712119051407052689">"Substituir"</string>
- <string name="running_foreground_services_title" msgid="5137313173431186685">"Aplicações em execução em segundo plano"</string>
+ <string name="running_foreground_services_title" msgid="5137313173431186685">"Apps em execução em segundo plano"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Pretende desativar os dados móveis?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string>
@@ -1058,7 +1022,7 @@
<string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Controlos da janela de ampliação"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controlos de dispositivos"</string>
- <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adicione controlos para os seus dispositivos associados."</string>
+ <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adicione controlos para os dispositivos associados."</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configure os controlos de dispositivos"</string>
<string name="quick_controls_setup_subtitle" msgid="1681506617879773824">"Prima sem soltar o botão ligar/desligar para aceder aos controlos."</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Escolha uma app para adicionar controlos"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Toque sem soltar e arraste para reorganizar os controlos."</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Todos os controlos foram removidos."</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Alterações não guardadas."</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Ver outras apps"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Não foi possível carregar os controlos. Verifique a app <xliff:g id="APP">%s</xliff:g> para se certificar de que as definições da mesma não foram alteradas."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Controlos compatíveis indisponíveis"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Outro"</string>
@@ -1086,7 +1051,7 @@
<string name="controls_dialog_message" msgid="342066938390663844">"Sugerido por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="controls_dialog_confirmation" msgid="586517302736263447">"Controlos atualizados"</string>
<string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"O PIN contém letras ou símbolos."</string>
- <string name="controls_pin_verify" msgid="3452778292918877662">"Valide <xliff:g id="DEVICE">%s</xliff:g>"</string>
+ <string name="controls_pin_verify" msgid="3452778292918877662">"Validar <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_pin_wrong" msgid="6162694056042164211">"PIN incorreto"</string>
<string name="controls_pin_verifying" msgid="3755045989392131746">"A validar…"</string>
<string name="controls_pin_instructions" msgid="6363309783822475238">"Introduzir PIN"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirme a alteração para <xliff:g id="DEVICE">%s</xliff:g>."</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Deslize rapidamente para ver mais."</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"A carregar recomendações…"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Fechar esta sessão multimédia"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Oculte a sessão atual."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Definições"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inativa. Consulte a app."</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Erro. A tentar novamente…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado."</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings_tv.xml b/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
index 65a6f03ede5a..2e60421c9008 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Ecrã no ecrã"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Sem título do programa)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Fechar PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Ecrã inteiro"</string>
<string name="mic_active" msgid="5766614241012047024">"Microfone ativado"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s acedeu ao microfone"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 3050ccc794eb..c8eaa0ddc4fb 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Toque novamente para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Deslize para cima para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Deslize para cima para tentar novamente"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua organização"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Deslize a partir do ícone do telefone"</string>
<string name="voice_hint" msgid="7476017460191291417">"Deslize a partir do ícone de assistência de voz"</string>
<string name="camera_hint" msgid="4519495795000658637">"Deslize a partir do ícone da câmera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"O perfil pode ser monitorado"</string>
<string name="vpn_footer" msgid="3457155078010607471">"A rede pode ser monitorada"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"A rede pode ser monitorada"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Sua organização é dona deste dispositivo e pode monitorar o tráfego de rede"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"A organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> é dona deste dispositivo e pode monitorar o tráfego de rede"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Este dispositivo pertence à sua organização e está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está conectado a <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Este dispositivo pertence à sua organização"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Este dispositivo pertence à sua organização e está conectado a VPNs"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Este dispositivo pertence á organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está conectado a VPNs"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Sua organização pode monitorar o tráfego de rede no seu perfil de trabalho"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> pode monitorar o tráfego de rede no seu perfil de trabalho"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"A rede pode ser monitorada"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Este dispositivo está conectado a VPNs"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Seu perfil de trabalho está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Seu perfil pessoal está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Este dispositivo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gerenciamento de dispositivos"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Monitoramento de perfis"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Monitoramento de rede"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Desativar VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Desconectar VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Ver políticas"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nO administrador de TI pode monitorar e gerenciar configurações, acesso corporativo, apps, dados associados ao dispositivo e informações de local do dispositivo.\n\nPara saber mais, entre em contato com seu administrador de TI."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Este dispositivo pertence à sua organização.\n\nO administrador de TI pode monitorar e gerenciar configurações, acesso corporativo, apps, dados associados ao dispositivo e informações de local do dispositivo.\n\nPara saber mais, entre em contato com seu administrador de TI."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Sua organização instalou uma autoridade de certificação neste dispositivo. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Sua organização instalou uma autoridade de certificação no seu perfil de trabalho. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Uma autoridade de certificação foi instalada neste dispositivo. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
@@ -727,19 +711,17 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silencioso"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Padrão"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bolha"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém sua atenção com um atalho flutuante para esse conteúdo."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior de uma seção de conversa, em forma de balão, mostrando a foto do perfil na tela de bloqueio"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configurações"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
- <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversão"</string>
+ <string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Os balões recentes e dispensados aparecerão aqui"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Não é possível modificar essas notificações."</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Este app está sobreposto a outros apps na sua tela e está usando o microfone e a câmera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Configurações"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Esta notificação foi silenciada pelo sistema."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Esta notificação foi promovida pelo sistema."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Esta notificação foi rebaixada pelo sistema."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Isso está correto?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Agradecemos seu feedback."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Permitir notificações desse canal"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editar ordem das configurações."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Tela de bloqueio"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Expandir"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizar"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Fechar"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Configurações"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Arraste para baixo para dispensar"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Se você não quer que o app <xliff:g id="NAME">%s</xliff:g> use este recurso, toque para abrir as configurações e desativá-lo."</string>
- <string name="pip_play" msgid="333995977693142810">"Reproduzir"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pausar"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Pular para a próxima"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Pular para a anterior"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"O smartphone foi desligado devido ao aquecimento"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"O smartphone está sendo executado normalmente agora"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"O smartphone está funcionando normalmente agora.\nToque para saber mais"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"O smartphone estava muito quente e foi desligado para resfriar. Agora, ele está sendo executado normalmente.\n\nO smartphone pode ficar quente demais se você:\n • usar apps que consomem muitos recursos (como apps de jogos, vídeos ou navegação);\n • fizer o download ou upload de arquivos grandes;\n • usar o smartphone em temperaturas altas."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver etapas de cuidado"</string>
<string name="high_temp_title" msgid="2218333576838496100">"O smartphone está esquentando"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Alguns recursos ficam limitados enquanto o smartphone é resfriado"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Alguns recursos ficam limitados enquanto o smartphone é resfriado.\nToque para saber mais"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Seu smartphone tentará se resfriar automaticamente. Você ainda poderá usá-lo, mas talvez ele fique mais lento.\n\nQuando o smartphone estiver resfriado, ele voltará ao normal."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver etapas de cuidado"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Desconecte o carregador"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Ocorreu um problema com o carregamento deste dispositivo. Desconecte o adaptador de energia com cuidado, já que o cabo pode estar quente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver etapas de cuidado"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Toque no controle, mantenha-o pressionado e arraste para reorganizar as posições."</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Todos os controles foram removidos"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"As mudanças não foram salvas"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Ver outros apps"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Não foi possível carregar os controles. Verifique o app <xliff:g id="APP">%s</xliff:g> para garantir que as configurações não tenham sido modificadas."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Controles compatíveis indisponíveis"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Outro"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirme a mudança para <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Deslize para ver mais"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Encerrar esta sessão de mídia"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Erro. Tentando novamente…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings_tv.xml b/packages/SystemUI/res/values-pt/strings_tv.xml
index a0cbeafb9c9a..19cb4eacf402 100644
--- a/packages/SystemUI/res/values-pt/strings_tv.xml
+++ b/packages/SystemUI/res/values-pt/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(programa sem título)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Fechar PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Tela cheia"</string>
<string name="mic_active" msgid="5766614241012047024">"Microfone ativado"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s acessou seu microfone"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 1d57a8236500..08542c02c2e0 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -456,10 +456,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Atingeți din nou pentru a deschide"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Glisați în sus pentru a deschide"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Glisați pentru a încerca din nou"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Dispozitivul aparține organizației dvs."</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Glisați dinspre telefon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Glisați dinspre pictogramă pentru asistentul vocal"</string>
<string name="camera_hint" msgid="4519495795000658637">"Glisați pentru a fotografia"</string>
@@ -526,33 +524,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profilul poate fi monitorizat"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Rețeaua poate fi monitorizată"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Este posibil ca rețeaua să fie monitorizată"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organizația dvs. deține acest dispozitiv și poate monitoriza traficul de rețea"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> deține acest dispozitiv și poate monitoriza traficul din rețea"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Dispozitivul aparține organizației dvs. și este conectat la <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Dispozitivul aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> și este conectat la <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Dispozitivul aparține organizației dvs."</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Dispozitivul aparține organizației dvs. și este conectat la VPN-uri"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Dispozitivul aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> și este conectat la VPN-uri"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Este posibil ca organizația dvs. să monitorizeze traficul de rețea în profilul dvs. de serviciu"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Este posibil ca <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> să monitorizeze traficul de rețea din profilul dvs. de serviciu"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Este posibil ca rețeaua să fie monitorizată"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Dispozitivul este conectat la VPN-uri"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Profilul dvs. de serviciu este conectat la <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Profilul dvs. personal este conectat la <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Dispozitivul este conectat la <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gestionarea dispozitivului"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Monitorizarea profilului"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Monitorizarea rețelei"</string>
@@ -562,10 +548,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Dezactivați conexiunea prin VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Deconectați rețeaua VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Afișați politicile"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Dispozitivul aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministratorul dvs. IT poate să monitorizeze și să gestioneze setările, accesul la nivelul companiei, aplicațiile, datele asociate dispozitivului și informațiile despre locația dispozitivului.\n\nPentru mai multe informații, contactați administratorul IT."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Dispozitivul aparține organizației dvs.\n\nAdministratorul dvs. IT poate să monitorizeze și să gestioneze setările, accesul la nivelul companiei, aplicațiile, datele asociate dispozitivului și informațiile despre locația dispozitivului.\n\nPentru mai multe informații, contactați administratorul IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizația dvs. a instalat un certificat CA pe acest dispozitiv. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizația dvs. a instalat un certificat CA în profilul dvs. de serviciu. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Pe acest dispozitiv este instalat un certificat CA. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string>
@@ -730,15 +714,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Silențios"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Prestabilite"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Balon"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automat"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Fără sunet sau vibrații"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Fără sunet sau vibrații și apare în partea de jos a secțiunii de conversație"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Poate să sune sau să vibreze, în funcție de setările telefonului"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Poate să sune sau să vibreze, în funcție de setările telefonului. Conversațiile din balonul <xliff:g id="APP_NAME">%1$s</xliff:g> în mod prestabilit."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Vă atrage atenția printr-o comandă rapidă flotantă la acest conținut."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Solicitați-i sistemului să stabilească dacă această notificare este sonoră sau cu vibrații."</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Se afișează în partea de sus a secțiunii de conversație, apare ca un balon flotant, afișează fotografia de profil pe ecranul de blocare"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Setări"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritate"</string>
@@ -759,18 +741,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Această aplicație se afișează peste alte aplicații de pe ecran și folosește microfonul și camera foto."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Setări"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Notificarea a fost dezactivată de sistem."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Notificarea a fost promovată de sistem."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Notificarea a fost mutată în jos de sistem."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Este corect?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Mulțumim pentru feedback!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> sunt afișate"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> nu sunt afișate"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Permiteți notificările de la acest canal"</string>
@@ -945,26 +921,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editați ordinea setărilor."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> din <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ecran de blocare"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Extindeți"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizați"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Închideți"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Setări"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Trageți în jos pentru a închide"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Meniu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> este în modul picture-in-picture"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Dacă nu doriți ca <xliff:g id="NAME">%s</xliff:g> să utilizeze această funcție, atingeți pentru a deschide setările și dezactivați-o."</string>
- <string name="pip_play" msgid="333995977693142810">"Redați"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Întrerupeți"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Treceți la următorul"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Treceți la cel anterior"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefonul s-a oprit din cauza încălzirii"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Acum telefonul funcționează normal"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Acum telefonul funcționează normal.\nAtingeți pentru mai multe informații"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonul se încălzise prea mult și s-a oprit pentru a se răci. Acum telefonul funcționează normal.\n\nTelefonul s-ar putea încălzi prea mult dacă:\n • folosiți aplicații care consumă multe resurse (de ex., jocuri, aplicații video/de navigare);\n • descărcați/încărcați fișiere mari;\n • folosiți telefonul la temperaturi ridicate."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Vedeți pașii pentru îngrijire"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefonul se încălzește"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Anumite funcții sunt limitate în timp ce telefonul se răcește"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Anumite funcții sunt limitate în timp ce telefonul se răcește.\nAtingeți pentru mai multe informații"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonul va încerca automat să se răcească. Puteți folosi telefonul în continuare, dar este posibil să funcționeze mai lent.\n\nDupă ce se răcește, telefonul va funcționa normal."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Vedeți pașii pentru îngrijire"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Deconectați încărcătorul"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Există o problemă la încărcarea acestui dispozitiv. Deconectați adaptorul de curent și aveți grijă, deoarece cablul poate fi cald."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Vedeți pașii pentru îngrijire"</string>
@@ -1084,6 +1048,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Țineți apăsat și trageți pentru a rearanja comenzile"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Au fost șterse toate comenzile"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Modificările nu au fost salvate"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Vedeți alte aplicații"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Comenzile nu au putut fi încărcate. Accesați aplicația <xliff:g id="APP">%s</xliff:g> pentru a vă asigura că setările aplicației nu s-au schimbat."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Nu sunt disponibile comenzi compatibile"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Altul"</string>
@@ -1101,8 +1066,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Confirmați schimbarea pentru <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Glisați pentru a vedea mai multe"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Închideți această sesiune media"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Ascunde sesiunea actuală."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ascunde"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verificați aplicația"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Eroare, se încearcă din nou…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nu s-a găsit"</string>
diff --git a/packages/SystemUI/res/values-ro/strings_tv.xml b/packages/SystemUI/res/values-ro/strings_tv.xml
index 1bfbd71fef9b..f4349ff91b05 100644
--- a/packages/SystemUI/res/values-ro/strings_tv.xml
+++ b/packages/SystemUI/res/values-ro/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-Picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program fără titlu)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Închideți PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Ecran complet"</string>
<string name="mic_active" msgid="5766614241012047024">"Microfon activ"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s a accesat microfonul"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 3565f051d0eb..12c489e08e41 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -458,10 +458,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Нажмите ещё раз, чтобы открыть"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Проведите вверх, чтобы открыть"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Чтобы повторить попытку, проведите вверх"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Это устройство принадлежит вашей организации"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Этим устройством владеет организация \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string>
<string name="phone_hint" msgid="6682125338461375925">"Телефон: проведите от значка"</string>
<string name="voice_hint" msgid="7476017460191291417">"Аудиоподсказки: проведите от значка"</string>
<string name="camera_hint" msgid="4519495795000658637">"Камера: проведите от значка"</string>
@@ -529,33 +527,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Действия в профиле могут отслеживаться"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Сеть может отслеживаться"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Сеть может отслеживаться"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ваша организация управляет этим устройством и может отслеживать сетевой трафик"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Организация \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\" управляет этим устройством и может отслеживать сетевой трафик"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Это устройство принадлежит вашей организации и подключено к приложению \"<xliff:g id="VPN_APP">%1$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Это устройство принадлежит организации \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\" и подключено к приложению \"<xliff:g id="VPN_APP">%2$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Это устройство принадлежит вашей организации"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Это устройство принадлежит организации \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Это устройство принадлежит вашей организации и подключено к приложениям для VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Это устройство принадлежит организации \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\" и подключено к приложениям для VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Ваша организация может отслеживать сетевой трафик в рабочем профиле"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Организация \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\" может отслеживать сетевой трафик в вашем рабочем профиле"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Сеть может отслеживаться"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Это устройство подключено к приложениям для VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Ваш рабочий профиль подключен к приложению \"<xliff:g id="VPN_APP">%1$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Ваш личный профиль подключен к приложению \"<xliff:g id="VPN_APP">%1$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Это устройство подключено к приложению \"<xliff:g id="VPN_APP">%1$s</xliff:g>\""</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Управление устройством"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Мониторинг профиля"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Отслеживание сетей"</string>
@@ -565,10 +551,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Отключить VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Отключить VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Просмотреть политику"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Это устройство принадлежит организации \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\".\n\nВаш системный администратор может управлять настройками, приложениями и параметрами доступа к корпоративным ресурсам на этом устройстве, а также связанными с ним данными (например, сведениями о местоположении).\n\nЗа подробной информацией обращайтесь к системному администратору."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Это устройство принадлежит вашей организации.\n\nСистемный администратор может управлять настройками, приложениями и параметрами доступа к корпоративным ресурсам на этом устройстве, а также связанными с ним данными (например, сведениями о местоположении).\n\nЗа подробной информацией обращайтесь к системному администратору."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Ваша организация установила сертификат ЦС на устройство. Она может отслеживать и изменять защищенный сетевой трафик."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ваша организация установила сертификат ЦС в рабочем профиле. Она может отслеживать и изменять защищенный сетевой трафик."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На устройстве установлен сертификат ЦС. Ваш защищенный сетевой трафик могут отслеживать и изменять."</string>
@@ -733,15 +717,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Без звука"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"По умолчанию"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Всплывающая подсказка"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматически"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звука или вибрации"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звука или вибрации, появляется в нижней части списка разговоров"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Звонок или вибрация в зависимости от настроек телефона"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Звонок или вибрация в зависимости от настроек телефона. Разговоры из приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" по умолчанию появляются в виде всплывающего чата."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Привлекает ваше внимание к контенту с помощью плавающего ярлыка"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Система будет сама определять, включать ли звуковой сигнал или вибрацию для уведомления"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Появляется в верхней части списка разговоров и как всплывающий чат, а также показывает фото профиля на заблокированном экране"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Настройки"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
@@ -762,18 +744,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Это приложение располагается поверх других приложений, а также использует микрофон и камеру."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Настройки"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ОК"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Это уведомление отключено системой."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Статус этого уведомления повышен системой."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Статус этого уведомления понижен системой."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Все верно?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Спасибо!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ОК"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Настройки уведомлений для приложения <xliff:g id="APP_NAME">%1$s</xliff:g> открыты"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Настройки уведомлений для приложения <xliff:g id="APP_NAME">%1$s</xliff:g> закрыты"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Показывать уведомления с этого канала"</string>
@@ -950,26 +926,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Изменить порядок быстрых настроек."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Страница <xliff:g id="ID_1">%1$d</xliff:g> из <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Заблокированный экран"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Развернуть"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Свернуть"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Закрыть"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Настройки"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Чтобы закрыть, потяните вниз"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Меню"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> находится в режиме \"Картинка в картинке\""</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Чтобы отключить эту функцию для приложения \"<xliff:g id="NAME">%s</xliff:g>\", перейдите в настройки."</string>
- <string name="pip_play" msgid="333995977693142810">"Воспроизвести"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Приостановить"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Перейти к следующему"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Перейти к предыдущему"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Телефон выключился из-за перегрева"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Сейчас телефон работает нормально"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Сейчас телефон работает нормально.\nНажмите, чтобы получить дополнительную информацию"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Ваш телефон выключился из-за перегрева. Сейчас он работает нормально.\n\nВозможные причины перегрева телефона:\n • использование ресурсоемких игр и приложений, связанных с видео или навигацией);\n • скачивание или загрузка больших файлов;\n • высокая температура окружающей среды."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Подробнее о действиях при перегреве…"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Телефон нагревается"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Пока телефон не остынет, некоторые функции могут быть недоступны."</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Пока телефон не остынет, некоторые функции могут быть недоступны.\nНажмите, чтобы получить дополнительную информацию"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ваш телефон остынет автоматически.\n\nОбратите внимание, что до тех пор он может работать медленнее, чем обычно."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Подробнее о действиях при перегреве…"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Отключите зарядное устройство"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Во время зарядки возникла проблема. Отключите адаптер питания. Будьте осторожны, кабель может быть горячим."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Подробнее о действиях при перегреве…"</string>
@@ -1071,7 +1035,7 @@
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Добавьте виджеты для управления устройствами."</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Настройте виджеты управления устройствами"</string>
<string name="quick_controls_setup_subtitle" msgid="1681506617879773824">"Чтобы перейти к элементам управления, удерживайте кнопку питания."</string>
- <string name="controls_providers_title" msgid="6879775889857085056">"Чтобы добавить элементы управления, выберите приложение"</string>
+ <string name="controls_providers_title" msgid="6879775889857085056">"Чтобы добавить виджеты управления, выберите приложение"</string>
<plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
<item quantity="one">Добавлен <xliff:g id="NUMBER_1">%s</xliff:g> элемент управления.</item>
<item quantity="few">Добавлено <xliff:g id="NUMBER_1">%s</xliff:g> элемента управления.</item>
@@ -1086,12 +1050,13 @@
<string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"удалить из избранного"</string>
<string name="accessibility_control_move" msgid="8980344493796647792">"Переместить на позицию <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="controls_favorite_default_title" msgid="967742178688938137">"Элементы управления"</string>
- <string name="controls_favorite_subtitle" msgid="6604402232298443956">"Выберите элементы управления, которые будут доступны в меню кнопки питания."</string>
- <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Чтобы изменить порядок элементов управления, перетащите их"</string>
- <string name="controls_favorite_removed" msgid="5276978408529217272">"Все элементы управления удалены"</string>
+ <string name="controls_favorite_subtitle" msgid="6604402232298443956">"Выберите виджеты управления, которые будут доступны в меню кнопки питания."</string>
+ <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Чтобы изменить порядок виджетов, перетащите их."</string>
+ <string name="controls_favorite_removed" msgid="5276978408529217272">"Все виджеты управления удалены."</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Изменения не сохранены."</string>
- <string name="controls_favorite_load_error" msgid="5126216176144877419">"Не удалось загрузить список доступных для управления устройств. Проверьте, не изменились ли настройки приложения \"<xliff:g id="APP">%s</xliff:g>\"."</string>
- <string name="controls_favorite_load_none" msgid="7687593026725357775">"Управление недоступно"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Показать другие приложения"</string>
+ <string name="controls_favorite_load_error" msgid="5126216176144877419">"Не удалось загрузить список виджетов для управления устройствами. Проверьте, не изменились ли настройки приложения \"<xliff:g id="APP">%s</xliff:g>\"."</string>
+ <string name="controls_favorite_load_none" msgid="7687593026725357775">"Управление недоступно."</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Другое"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"Добавьте виджеты управления устройствами"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"Добавить"</string>
@@ -1107,8 +1072,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Подтвердите изменения для устройства \"<xliff:g id="DEVICE">%s</xliff:g>\""</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Проведите по экрану, чтобы увидеть больше"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загрузка рекомендаций…"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Закрыть этот мультимедийный сеанс"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Скрыть текущий сеанс?"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Скрыть"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Возобновить"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Нет ответа. Проверьте приложение."</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Ошибка. Повторная попытка…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не найдено."</string>
@@ -1119,6 +1087,6 @@
<string name="controls_error_failed" msgid="960228639198558525">"Ошибка. Повторите попытку."</string>
<string name="controls_in_progress" msgid="4421080500238215939">"Выполняется"</string>
<string name="controls_added_tooltip" msgid="4842812921719153085">"Удерживайте кнопку питания, чтобы увидеть новые элементы управления"</string>
- <string name="controls_menu_add" msgid="4447246119229920050">"Добавить эл-ты управления"</string>
- <string name="controls_menu_edit" msgid="890623986951347062">"Изменить эл-ты управления"</string>
+ <string name="controls_menu_add" msgid="4447246119229920050">"Добавить виджеты"</string>
+ <string name="controls_menu_edit" msgid="890623986951347062">"Изменить виджеты"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings_tv.xml b/packages/SystemUI/res/values-ru/strings_tv.xml
index b668cf96908b..b8638b711176 100644
--- a/packages/SystemUI/res/values-ru/strings_tv.xml
+++ b/packages/SystemUI/res/values-ru/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Картинка в картинке"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Без названия)"</string>
- <string name="pip_close" msgid="5775212044472849930">"\"Кадр в кадре\" – выйти"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Во весь экран"</string>
<string name="mic_active" msgid="5766614241012047024">"Микрофон включен"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Приложение \"%1$s\" использовало доступ к микрофону."</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index d8d67cc3753e..412050f4bb7f 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"විවෘත කිරීමට නැවත තට්ටු කරන්න"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"විවෘත කිරීමට ස්වයිප් කරන්න"</string>
<string name="keyguard_retry" msgid="886802522584053523">"නැවත උත්සාහ කිරීමට ඉහළට ස්වයිප් කරන්න"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"මෙම උපාංගය ඔබේ සංවිධානයට අයිතිය"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"මෙම උපාංගය <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> සංවිධානයට අයිතිය"</string>
<string name="phone_hint" msgid="6682125338461375925">"දුරකථනය සඳහා නිරූපකය වෙතින් ස්වයිප් කරන්න"</string>
<string name="voice_hint" msgid="7476017460191291417">"හඬ සහාය සඳහා නිරූපකය වෙතින් ස්වයිප් කරන්න"</string>
<string name="camera_hint" msgid="4519495795000658637">"කැමරාව සඳහා නිරූපකය වෙතින් ස්වයිප් කරන්න"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ඇතැම් විට පැතිකඩ නිරීක්ෂණය කරන ලදි"</string>
<string name="vpn_footer" msgid="3457155078010607471">"ඇතැම් විට ජාලය නිරීක්ෂණය විය හැක"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ඇතැම් විට ජාලය නිරීක්ෂණය විය හැක"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ඔබේ සංවිධානයට මෙම උපාංගය අයිති අතර ජාල තදබදය නිරීක්ෂණය කළ හැකිය"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> සංවිධානයට මෙම උපාංගය අයිති අතර ජාල තදබදය නිරීක්ෂණය කළ හැකිය"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"මෙම උපාංගය ඔබේ සංවිධානයට අයිති අතර <xliff:g id="VPN_APP">%1$s</xliff:g> වෙත සම්බන්ධ කර ඇත"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"මෙම උපාංගය <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> සංවිධානයට අයිති අතර <xliff:g id="VPN_APP">%2$s</xliff:g> වෙත සම්බන්ධ කර ඇත"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"මෙම උපාංගය ඔබේ සංවිධානයට අයිතිය"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"මෙම උපාංගය <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> සංවිධානයට අයිතිය"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"මෙම උපාංගය ඔබේ සංවිධානයට අයිති අතර VPNs වෙත සම්බන්ධ කර ඇත"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"මෙම උපාංගය <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> සංවිධානයට අයිති අතර VPNs වෙත සම්බන්ධ කර ඇත"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"ඔබගේ කාර්යාල පැතිකඩ තුළ ඔබේ සංවිධානය ජාල තදබදය නිරීක්ෂණය කිරීමට හැක"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ඔබේ කාර්යාල පැතිකඩ තුළ ජාල තදබදය නිරීක්ෂණය කළ හැක"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"ඇතැම් විට ජාලය නිරීක්ෂණය විය හැක"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"මෙම උපාංගය VPNs වෙත සම්බන්ධ කර ඇත"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"ඔබේ කාර්යාල පැතිකඩ <xliff:g id="VPN_APP">%1$s</xliff:g> වෙත සම්බන්ධ කර ඇත"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"ඔබේ පෞද්ගලික පැතිකඩ <xliff:g id="VPN_APP">%1$s</xliff:g> වෙත සම්බන්ධ කර ඇත"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"මෙම උපාංගය <xliff:g id="VPN_APP">%1$s</xliff:g> වෙත සම්බන්ධ කර ඇත"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"උපාංග කළමනාකරණය"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"පැතිකඩ නිරීක්ෂණය කිරීම"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ජාල නිරීක්ෂණය"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN අබල කරන්න."</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN විසන්ධි කරන්න"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"ප්‍රතිපත්ති පෙන්වන්න"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"මෙම උපාංගය <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> සංවිධානයට අයිතිය.\n\nඔබේ IT පරිපාලකට ඔබේ උපාංගය හා සම්බන්ධිත සැකසීම්, ආයතනික ප්‍රවේශය, යෙදුම්, දත්ත සහ ඔබේ උපාංගයේ ස්ථාන තොරතුරු නිරීක්ෂණය කර කළමනාකරණය කිරීමට හැකිය.\n\nවැඩිදුර තොරතුරු සඳහා, ඔබේ IT අමතන්න."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"මෙම උපාංගය ඔබේ සංවිධානයට අයිතිය.\n\nඔබේ IT පරිපාලකට ඔබේ උපාංගය හා සම්බන්ධිත සැකසීම්, ආයතනික ප්‍රවේශය, යෙදුම්, දත්ත සහ ඔබේ උපාංගයේ ස්ථාන තොරතුරු නිරීක්ෂණය කර කළමනාකරණය කිරීමට හැකිය.\n\nවැඩිදුර තොරතුරු සඳහා, ඔබේ IT අමතන්න."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ඔබගේ සංවිධානය ඔබගේ උපාංගය තුළ සහතික අධිකාරියක් ස්ථාපනය කර තිබේ. ඔබගේ ආරක්ෂක ජාල තදබදය නිරීක්ෂණය හෝ වෙනස් කිරීමට පුළුවනි."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ඔබගේ සංවිධානය ඔබගේ කාර්යාල පැතිකඩ තුළ සහතික අධිකාරියක් ස්ථාපනය කර තිබේ. ඔබගේ ආරක්ෂක ජාල තදබදය නිරීක්ෂණය හෝ වෙනස් කිරීමට පුළුවනි."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"මෙම උපාංගය තුළ සහතික අධිකාරියක් ස්ථාපනය කර තිබේ. ඔබගේ ආරක්ෂක ජාල තදබදය නිරීක්ෂණය හෝ වෙනස් කිරීමට පුළුවනි."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"නිහඬ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"පෙරනිමි"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"බුබුළු"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ස්වයංක්‍රිය"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"හඬක් හෝ කම්පනයක් නැත"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"හඬක් හෝ කම්පනයක් නැති අතර සංවාද කොටසේ පහළම දිස් වේ"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"දුරකථන සැකසීම් මත පදනම්ව නාද කිරීමට හෝ කම්පනය කිරීමට හැකිය"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"දුරකථන සැකසීම් මත පදනම්ව නාද කිරීමට හෝ කම්පනය කිරීමට හැකිය. <xliff:g id="APP_NAME">%1$s</xliff:g> වෙතින් සංවාද පෙරනිමියෙන් බුබුළු දමයි"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"පාවෙන කෙටිමගක් සමග ඔබේ අවධානය මෙම අන්තර්ගතය වෙත තබා ගන්න."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"මෙම දැනුම් දීම ශබ්දයක් හෝ කම්පනයක් ඇති කළ යුතු ද යන්න පද්ධතිය මගින් තීරණය කර තිබේද"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"සංවාද කොටසේ ඉහළම පෙන්වයි, බුබුළක් ලෙස දිස් වේ, අගුලු තිරයේ පැතිකඩ පින්තූරය සංදර්ශනය වේ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"සැකසීම්"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ප්‍රමුඛතාව"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"මෙම යෙදුම් ඔබගේ තිරය මත අනෙකුත් යෙදුම්වලට උඩින් සංදර්ශනය වන අතර මයික්‍රෆෝනය සහ කැමරාව භාවිතා කරයි."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"සැකසීම්"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"හරි"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"මෙම දැනුම් දීම පද්ධතිය මගින් නිහඬ කරන ලදී."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"මෙම දැනුම් දීම පද්ධතිය මගින් ඉහළ දමන ලදී."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"මෙම දැනුම් දීම පද්ධතිය මගින් පහත දමන ලදී."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"මෙය නිවැරදි වුයේද?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"ඔබේ ප්‍රතිපෝෂණයට ස්තූතියි!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"හරි"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා දැනුම්දීම් පාලන විවෘත කරන ලදී"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා දැනුම්දීම් පාලන වසන ලදී"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"මෙම නාලිකාව වෙතින් දැනුම්දීම් සඳහා ඉඩ දෙන්න"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"සැකසීම්වල අනුපිළිවෙළ සංංස්කරණය කරන්න."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> න් <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"අගුලු තිරය"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"දිග හරින්න"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"කුඩා කරන්න"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"වසන්න"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"සැකසීම්"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"ඉවත ලෑමට පහළට ඇදගෙන යන්න"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"මෙනුව"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> පින්තූරය-තුළ-පින්තූරය තුළ වේ"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"ඔබට <xliff:g id="NAME">%s</xliff:g> මෙම විශේෂාංගය භාවිත කිරීමට අවශ්‍ය නැති නම්, සැකසීම් විවෘත කිරීමට තට්ටු කර එය ක්‍රියාවිරහිත කරන්න."</string>
- <string name="pip_play" msgid="333995977693142810">"ධාවනය කරන්න"</string>
- <string name="pip_pause" msgid="1139598607050555845">"විරාම කරන්න"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"ඊළඟ එකට පනින්න"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"පෙර එකට පනින්න"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"දුරකථනය රත් වීම නිසා ක්‍රියාවිරහිත කරන ලදී"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"ඔබගේ දුරකථනය දැන් සාමාන්‍ය ලෙස ධාවනය වේ"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"ඔබගේ දුරකථනය දැන් සාමාන්‍ය ලෙස ධාවනය වේ.\nතව තතු සඳහා තට්ටු කරන්න"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"ඔබේ දුරකථනය ඉතාම උණුසුම්ය, එම නිසා එය සිසිල් වීමට ක්‍රියාවිරහිත කරන ලදී. ධැන් ඔබේ දුරකථනය සාමාන්‍ය පරිදි ධාවනය වේ.\n\nඔබ පහත දේවල් සිදු කළහොත් ඔබේ දුරකථනය ඉතාම උණුසුම් විය හැකිය:\n • සම්පත්-දැඩි සත්කාරක යෙදුම් භාවිතය (ක්‍රීඩා, වීඩියෝ, හෝ සංචලන යෙදුම් යනාදී)\n • විශාල ගොනු බාගැනීම හෝ උඩුගත කිරීම\n • ඔබේ දුරකථනය අධික උෂ්ණත්වයේදී භාවිත කිරීම"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"රැකවරණ පියවර බලන්න"</string>
<string name="high_temp_title" msgid="2218333576838496100">"දුරකථනය උණුසුම් වෙමින්"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"දුරකථනය සිසිල් වන අතරතුර සමහර විශේෂාංග සීමිත විය හැකිය"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"දුරකථනය සිසිල් වන අතරතුර සමහර විශේෂාංග සීමිත විය හැකිය.\nතව තතු සඳහා තට්ටු කරන්න"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ඔබගේ දුරකථනය ස්වයංක්‍රියව සිසිල් වීමට උත්සාහ කරනු ඇත. ඔබට තවම ඔබේ දුරකථනය භාවිත කළ හැකිය, නමුත් එය සෙමින් ධාවනය විය හැකිය.\n\nඔබේ දුරකථනය සිසිල් වූ පසු, එය සාමාන්‍ය ලෙස ධාවනය වනු ඇත."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"රැකවරණ පියවර බලන්න"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"චාජරය පේනුවෙන් ඉවත් කරන්න"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"මෙම උපාංගය ආරෝපණ කිරීමේ ගැටලුවක් තිබේ බල ඇඩැප්ටරය ගලවා කේබලය උණුසුම් විය හැකි බැවින් පරෙස්සම් වන්න."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"රැකවරණ පියවර බලන්න"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"පාලන නැවත පිළියෙළ කිරීමට අල්ලාගෙන සිට අදින්න"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"සියලු පාලන ඉවත් කර ඇත"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"වෙනස් කිරීම් නොසුරැකිණි"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"වෙනත් යෙදුම් බලන්න"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"පාලන පූරණය කළ නොහැකි විය. යෙදුම් සැකසීම් වෙනස් වී නැති බව සහතික කර ගැනීමට <xliff:g id="APP">%s</xliff:g> යෙදුම පරීක්ෂා කරන්න."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"ගැළපෙන පාලන ලබා ගත නොහැකිය"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"වෙනත්"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> සඳහා වෙනස තහවුරු කරන්න"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"තව බැලීමට ස්වයිප් කරන්න"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"නිර්දේශ පූරණය කරමින්"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"මෙම මාධ්‍ය සැසිය වසන්න"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"මාධ්‍ය"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"වත්මන් සැසිය සඟවන්න."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"සඟවන්න"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"නැවත පටන් ගන්න"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"සැකසීම්"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"අක්‍රියයි, යෙදුම පරීක්ෂා කරන්න"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"දෝෂයකි, නැවත උත්සාහ කරමින්…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"හමු නොවිණි"</string>
diff --git a/packages/SystemUI/res/values-si/strings_tv.xml b/packages/SystemUI/res/values-si/strings_tv.xml
index 167d10578f16..411c0a075914 100644
--- a/packages/SystemUI/res/values-si/strings_tv.xml
+++ b/packages/SystemUI/res/values-si/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"පින්තූරය-තුළ-පින්තූරය"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(මාතෘකාවක් නැති වැඩසටහන)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP වසන්න"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"සම්පූර්ණ තිරය"</string>
<string name="mic_active" msgid="5766614241012047024">"මයික්‍රොෆෝනය සක්‍රියයි"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ඔබේ මයික්‍රොෆෝනයට ප්‍රවේශ වී ඇත"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 9fc6a0da79a9..f657d4fe71e7 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -458,10 +458,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Upozornenie otvoríte opätovným klepnutím"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Otvorte potiahnutím prstom nahor"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Potiahnutím nahor to skúste znova"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Toto zariadenie patrí vašej organizácii"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Toto zariadení patrí organizácii <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefón otvoríte prejdením prstom od ikony"</string>
<string name="voice_hint" msgid="7476017460191291417">"Hlasového asistenta otvoríte prejdením prstom od ikony"</string>
<string name="camera_hint" msgid="4519495795000658637">"Fotoaparát otvoríte prejdením prstom od ikony"</string>
@@ -529,33 +527,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil môže byť monitorovaný"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Sieť môže byť sledovaná"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Sieť môže byť monitorovaná"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Vaša organizácia spravuje toto zariadenie a môže sledovať sieťovú premávku"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> vlastní toto zariadenie a môže sledovať sieťovú premávku"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Toto zariadenie patrí vašej organizácii a je pripojené k sieti <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Toto zariadenie patrí organizácii <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> a je pripojené k sieti <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Toto zariadenie patrí vašej organizácii"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Toto zariadení patrí organizácii <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Toto zariadenie patrí vašej organizácii a je pripojené k sieťam VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Toto zariadenie patrí organizácii <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> a je pripojené k sieťam VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Organizácia môže sledovať sieťovú premávku vo vašom pracovnom profile"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Organizácia <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> môže sledovať sieťovú premávku vo vašom pracovnom profile"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Sieť môže byť sledovaná"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Toto zariadenie je pripojené k sieťam VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Váš pracovný profil je pripojený k sieti <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Váš osobný profil je pripojený k sieti <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Toto zariadenie je pripojené k sieti <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Správa zariadení"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Monitorovanie profilu"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Sledovanie siete"</string>
@@ -565,10 +551,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Deaktivovať VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Odpojiť sieť VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Zobraziť pravidlá"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Toto zariadenie patrí organizácii <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nVáš správca IT môže sledovať a spravovať nastavenia, podnikový prístup, aplikácie, údaje spojené s vaším zariadení a informácie o jeho polohe.\n\nViac sa dozviete od správcu IT."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Toto zariadenie patrí vašej organizácii.\n\nVáš správca IT môže sledovať a spravovať nastavenia, podnikový prístup, aplikácie, údaje spojené s vaším zariadením a informácie o jeho polohe.\n\n. Viac sa dozviete od správcu IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizácia nainštalovala pre toto zariadenie certifikačnú autoritu. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizácia nainštalovala pre váš pracovný profil certifikačnú autoritu. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V tomto zariadení je nainštalovaná certifikačná autorita. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string>
@@ -733,15 +717,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Tiché"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predvolené"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bublina"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Žiadny zvuk ani vibrácie"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Žiadny zvuk ani vibrácie a zobrazuje sa nižšie v sekcii konverzácií"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Zvoní či vibruje podľa nastavení telefónu"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Môže zvoniť alebo vibrovať podľa nastavení telefónu. Predvolene sa zobrazia konverzácie z bubliny <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Upúta vás plávajúcim odkazom na tento obsah."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nechajte systém určiť, či má toto upozornenie vydávať zvuk alebo vibrovať"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Nájdete ju hore v sekcii konverzácií ako plávajúcu bublinu, zobrazuje profilovú fotku na uzamknutej obrazovke"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Nastavenia"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priorita"</string>
@@ -762,18 +744,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Táto aplikácia sa zobrazuje cez ďalšie aplikácie na obrazovke a používa mikrofón aj fotoaparát."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Nastavenia"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Zvuk tohto upozornenia bol vypnutý systémom."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Toto upozornenie bolo povýšené systémom."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Toto upozornenie bolo systémom preradené nižšie."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Bolo toto správne?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Ďakujeme za váš názor."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Ovládanie upozornení pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> je otvorené"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Ovládanie upozornení pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> je zatvorené"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Povoliť upozornenia z tohto kanála"</string>
@@ -950,26 +926,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Upraviť poradie nastavení"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Strana <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Uzamknutá obrazovka"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Rozbaliť"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimalizovať"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Zavrieť"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Nastavenia"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Zrušíte presunutím nadol"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Ponuka"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> je v režime obraz v obraze"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ak nechcete, aby aplikácia <xliff:g id="NAME">%s</xliff:g> používala túto funkciu, klepnutím otvorte nastavenia a vypnite ju."</string>
- <string name="pip_play" msgid="333995977693142810">"Prehrať"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pozastaviť"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Preskočiť na ďalšie"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Preskočiť na predchádzajúce"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefón sa vypol z dôvodu prehriatia"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Teraz telefón funguje ako obvykle"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Teraz telefón funguje ako obvykle.\nViac sa dozviete po klepnutí."</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefón bol príliš horúci, preto sa vypol, aby vychladol. Teraz funguje ako obvykle.\n\nTelefón sa môže príliš zahriať v týchto prípadoch:\n • používanie náročných aplikácií (napr. hier, videí alebo navigácie);\n • sťahovanie alebo nahrávanie veľkých súborov;\n • používanie telefónu pri vysokých teplotách."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Zobraziť opatrenia"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Teplota telefónu stúpa"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Niektoré funkcie budú obmedzené, dokým neklesne teplota telefónu"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Niektoré funkcie budú obmedzené, dokým neklesne teplota telefónu.\nViac sa dozviete po klepnutí."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Váš telefón sa automaticky pokúsi schladiť. Môžete ho naďalej používať, ale môže fungovať pomalšie.\n\nPo poklese teploty bude telefón fungovať ako normálne."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Zobraziť opatrenia"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Odpojte nabíjačku"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Vyskytol sa problém s nabíjaním tohto zariadenia. Odpojte nabíjačku a postupujte opatrne, pretože kábel môže byť horúci."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Zobraziť opatrenia"</string>
@@ -1090,6 +1054,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Polohu každého ovládača môžete zmeniť jeho pridržaním a presunutím"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Všetky ovládače boli odstránené"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Zmeny neboli uložené"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Zobraziť ďalšie aplikácie"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Ovládacie prvky sa nepodarilo načítať. V aplikácii <xliff:g id="APP">%s</xliff:g> skontrolujte, či sa nezmenili nastavenia."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kompatibilné ovládacie prvky nie sú k dispozícii"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Iné"</string>
@@ -1107,8 +1072,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Potvrdenie zmeny zariadenia <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Potiahnutím zobrazíte ďalšie položky"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítavajú sa odporúčania"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Zavrieť túto reláciu média"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Médiá"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Skryť aktuálnu reláciu."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skryť"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Pokračovať"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavenia"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktívne, preverte aplikáciu"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Chyba, skúša sa znova…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nenájdené"</string>
diff --git a/packages/SystemUI/res/values-sk/strings_tv.xml b/packages/SystemUI/res/values-sk/strings_tv.xml
index 562742ae5f1b..b52dada595c7 100644
--- a/packages/SystemUI/res/values-sk/strings_tv.xml
+++ b/packages/SystemUI/res/values-sk/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Obraz v obraze"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program bez názvu)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Zavrieť režim PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Celá obrazovka"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofón je aktívny"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Aplikácia %1$s použila váš mikrofón"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index ca07c27db8e5..e1e5852098b9 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -458,10 +458,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Znova se dotaknite, da odprete"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Povlecite navzgor, da odprete"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Povlecite navzgor za vnovičen poskus"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Ta naprava pripada vaši organizaciji"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ta naprava pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Povlecite z ikone za telefon"</string>
<string name="voice_hint" msgid="7476017460191291417">"Povlecite z ikone za glasovnega pomočnika"</string>
<string name="camera_hint" msgid="4519495795000658637">"Povlecite z ikone za fotoaparat"</string>
@@ -529,33 +527,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil je morda nadziran"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Omrežje je lahko nadzorovano"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Omrežje je morda nadzorovano"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Vaša organizacija je lastnica te naprave in lahko nadzira omrežni promet"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Organizacija <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> je lastnica te naprave in lahko nadzira omrežni promet"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Ta naprava pripada vaši organizaciji in je povezana v aplikacijo <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Ta naprava pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> in je povezana v aplikacijo <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Ta naprava pripada vaši organizaciji"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Ta naprava pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Ta naprava pripada vaši organizaciji in je povezana v omrežja VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Ta naprava pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> in je povezana v omrežja VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Vaša organizacija lahko nadzira omrežni promet v vašem delovnem profilu"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> lahko nadzira omrežni promet v vašem delovnem profilu"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Omrežje je morda nadzorovano"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Ta naprava je povezana v omrežja VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Delovni profil je povezan v aplikacijo <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Osebni profil je povezan v aplikacijo <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Ta naprava je povezava v aplikacijo <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Upravljanje naprav"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Nadzor nad profilom"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Nadzor omrežja"</string>
@@ -565,10 +551,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Onemogoči VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Prekini povezavo z VPN-jem"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Prikaži pravilnike"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Ta naprava pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nSkrbnik za IT lahko nadzira in upravlja nastavitve, dostop za podjetje, aplikacije, z napravo povezane podatke in podatke o lokaciji naprave.\n\nZa več informacij se obrnite na skrbnika za IT."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Ta naprava pripada vaši organizaciji.\n\nSkrbnik za IT lahko nadzira in upravlja nastavitve, dostop za podjetje, aplikacije, z napravo povezane podatke in podatke o lokaciji naprave.\n\nZa več informacij se obrnite na skrbnika za IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Vaša organizacija je v to napravo namestila overitelja potrdil. Varni omrežni promet se lahko nadzira ali spreminja."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Vaša organizacija je v vaš delovni profil namestila overitelja potrdil. Varni omrežni promet se lahko nadzira ali spreminja."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V tej napravi je nameščen overitelj potrdil. Varni omrežni promet se lahko nadzira ali spreminja."</string>
@@ -733,15 +717,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Tiho"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Privzeto"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Mehurček"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Samodejno"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Brez zvočnega opozarjanja ali vibriranja"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Brez zvočnega opozarjanja ali vibriranja, prikaz nižje v razdelku s pogovorom"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Zvonjenje ali vibriranje je omogočeno na podlagi nastavitev telefona"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Zvonjenje ali vibriranje je omogočeno na podlagi nastavitev telefona. Pogovori v aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> so privzeto prikazani v oblačkih."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Zadrži vašo pozornost z lebdečo bližnjico do te vsebine."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Naj sistem določi, ali ob prejemu tega obvestila naprava predvaja zvok ali zavibrira"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Prikaz na vrhu razdelka s pogovorom in v plavajočem oblačku, prikaz profilne slike na zaklenjenem zaslonu"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Nastavitve"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prednost"</string>
@@ -762,18 +744,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ta aplikacija prekriva druge aplikacije na zaslonu ter uporablja mikrofon in fotoaparat."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Nastavitve"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"V redu"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Sistem je utišal to obvestilo."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Sistem je zvišal prednost tega obvestila."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Sistem je znižal prednost tega obvestila."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Je bilo to prav?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Hvala za povratne informacije."</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"V redu"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g> so odprti"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g> so zaprti"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Dovoli obvestila iz tega kanala"</string>
@@ -950,26 +926,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Uredi vrstni red nastavitev."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. stran od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Zaklenjen zaslon"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Razširi"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimiraj"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Zapri"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Nastavitve"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Povlecite navzdol, da opustite"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Meni"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> je v načinu slika v sliki"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Če ne želite, da aplikacija <xliff:g id="NAME">%s</xliff:g> uporablja to funkcijo, se dotaknite, da odprete nastavitve, in funkcijo izklopite."</string>
- <string name="pip_play" msgid="333995977693142810">"Predvajaj"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Začasno ustavi"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Preskoči na naslednjega"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Preskoči na prejšnjega"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Tel. izklopljen zaradi vročine"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Zdaj telefon normalno deluje"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefon zdaj deluje normalno.\nDotaknite se za več informacij"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon je bil prevroč, zato se je izklopil, da se ohladi. Zdaj normalno deluje.\n\nTelefon lahko postane prevroč ob:\n • uporabi aplikacij, ki intenzivno porabljajo sredstva (npr. za igranje iger, videoposnetke ali navigacijo)\n • prenosu ali nalaganju velikih datotek\n • uporabi telefona pri visokih temp."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Oglejte si navodila za ukrepanje"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon se segreva"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Nekatere funkcije bodo med ohlajanjem omejene."</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Nekatere funkcije bodo med ohlajanjem telefona omejene.\nDotaknite se za več informacij"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon se bo samodejno poskusil ohladiti. Še naprej ga lahko uporabljate, vendar bo morda deloval počasneje.\n\nKo se telefon ohladi, bo zopet deloval kot običajno."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Oglejte si navodila za ukrepanje"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Odklopite polnilnik"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Pri polnjenju te naprave je prišlo do težave. Previdno odklopite napajalnik, ker se je kabel morda segrel."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Oglejte si navodila za ukrepanje"</string>
@@ -1090,6 +1054,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Držite in povlecite, da prerazporedite kontrolnike"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Vsi kontrolniki so bili odstranjeni"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Spremembe niso shranjene"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Prikaz drugih aplikacij"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontrolnikov ni bilo mogoče naložiti. Preverite aplikacijo <xliff:g id="APP">%s</xliff:g> in se prepričajte, da se njene nastavitve niso spremenile."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Združljivi kontrolniki niso na voljo"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Drugo"</string>
@@ -1107,8 +1072,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Potrdite spremembo za napravo <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Če si želite ogledati več, povlecite"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nalaganje priporočil"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Zapri to sejo predstavnosti"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Predstavnost"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Skrije trenutno sejo."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skrij"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nadaljuj"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavitve"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, poglejte aplikacijo"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Napaka, vnovični poskus …"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ni mogoče najti"</string>
diff --git a/packages/SystemUI/res/values-sl/strings_tv.xml b/packages/SystemUI/res/values-sl/strings_tv.xml
index d3e4f259f881..109d797ebcf9 100644
--- a/packages/SystemUI/res/values-sl/strings_tv.xml
+++ b/packages/SystemUI/res/values-sl/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Slika v sliki"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program brez naslova)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Zapri način PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Celozaslonsko"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon je aktiven"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Aplikacija %1$s je dostopala do mikrofona"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index f13ac68d74c0..64d7a2ecac52 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Trokit përsëri për ta hapur"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Rrëshqit lart për ta hapur"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Rrëshqit lart për të provuar përsëri"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Kjo pajisje i përket organizatës sate"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Rrëshqit për të hapur telefonin"</string>
<string name="voice_hint" msgid="7476017460191291417">"Rrëshqit për të hapur ndihmën zanore"</string>
<string name="camera_hint" msgid="4519495795000658637">"Rrëshqit për të hapur kamerën"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profili mund të monitorohet"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Rrjeti mund të jetë i monitoruar"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Rrjeti mund të jetë i monitoruar"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organizata jote e zotëron këtë pajisje dhe mund të monitorojë trafikun e rrjetit"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e zotëron këtë pajisje dhe mund të monitorojë trafikun e rrjetit"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Kjo pajisje i përket organizatës sate dhe është e lidhur me <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> dhe është e lidhur me <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Kjo pajisje i përket organizatës sate"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Kjo pajisje i përket organizatës sate dhe është e lidhur me rrjetet VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> dhe është e lidhur me rrjetet VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Organizata jote mund të monitorojë trafikun e rrjetit në profilin tënd të punës"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> mund të monitorojë trafikun e rrjetit në profilin tënd të punës"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Rrjeti mund të jetë i monitoruar"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Kjo pajisje është e lidhur me rrjetet VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Profili yt i punës është i lidhur me <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Profili yt personal është i lidhur me <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Kjo pajisje është e lidhur me <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Menaxhimi i pajisjes"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Monitorimi i profilit"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Monitorimi i rrjetit"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Çaktivizo VPN-në"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Shkëput VPN-në"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Shiko politikat"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministratori i teknologjisë së informacionit mund të monitorojë dhe menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat e lidhura me pajisjen tënde, si dhe informacionet e vendndodhjes së pajisjes tënde.\n\nPër më shumë informacione, kontakto me administratorin e teknologjisë së informacionit."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Kjo pajisje i përket organizatës sate.\n\nAdministratori i teknologjisë së informacionit mund të monitorojë dhe menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat e lidhura me pajisjen tënde, si dhe informacionet e vendndodhjes së pajisjes tënde.\n\nPër më shumë informacione, kontakto me administratorin e teknologjisë së informacionit."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizata jote instaloi një autoritet certifikate në këtë pajisje. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizata jote instaloi një autoritet certifikate në profilin tënd të punës. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Në këtë pajisje është instaluar një autoritet certifikate. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Në heshtje"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"E parazgjedhur"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Flluskë"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatike"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Asnjë tingull ose dridhje"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Asnjë tingull ose dridhje dhe shfaqet më poshtë në seksionin e bisedave"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Mund të bjerë zilja ose të dridhet në bazë të cilësimeve të telefonit"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Mund të bjerë zilja ose të dridhet në bazë të cilësimeve të telefonit. Bisedat nga flluska e <xliff:g id="APP_NAME">%1$s</xliff:g> si parazgjedhje."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mban vëmendjen tënde me një shkurtore pluskuese te kjo përmbajtje."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Kërkoji sistemit të përcaktojë nëse ky njoftim duhet të lëshojë tingull apo dridhje"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Shfaqet në krye të seksionit të bisedës dhe shfaqet si flluskë pluskuese, shfaq fotografinë e profilit në ekranin e kyçjes"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Cilësimet"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Përparësia"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ky aplikacion po shfaqet mbi aplikacionet e tjera në ekran dhe po përdor mikrofonin dhe kamerën."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Cilësimet"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Në rregull"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Ky njoftim është vendosur në heshtje nga sistemi."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Ky njoftim është rritur në nivel nga sistemi."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Ky njoftim është ulur në nivel nga sistemi."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"A ishte e saktë kjo?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Faleminderit për komentin!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Në rregull"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrollet e njoftimeve për <xliff:g id="APP_NAME">%1$s</xliff:g> janë hapur"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Kontrollet e njoftimeve për <xliff:g id="APP_NAME">%1$s</xliff:g> janë mbyllur"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Lejo njoftimet nga ky kanal"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Modifiko rendin e cilësimeve."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Faqja <xliff:g id="ID_1">%1$d</xliff:g> nga <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ekrani i kyçjes"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Zgjero"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimizo"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Mbyll"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Cilësimet"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Zvarrit poshtë për të larguar"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menyja"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> është në figurë brenda figurës"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Nëse nuk dëshiron që <xliff:g id="NAME">%s</xliff:g> ta përdorë këtë funksion, trokit për të hapur cilësimet dhe për ta çaktivizuar."</string>
- <string name="pip_play" msgid="333995977693142810">"Luaj"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Ndërprit"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Kalo te tjetra"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Kalo tek e mëparshmja"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefoni u fik për shkak të nxehtësisë"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefoni tani punon normalisht"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefoni tani punon normalisht.\nTrokit për më shumë informacione"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefoni yt ishte tepër i nxehtë, prandaj u fik për t\'u ftohur. Telefoni tani punon normalisht.\n\nTelefoni mund të nxehet së tepërmi nëse ti:\n • Përdor aplikacione intensive për burimet (siç janë aplikacionet e lojërave, videove apo aplikacionet e navigimit)\n • Shkarkon ose ngarkon skedarë të mëdhenj\n • E përdor telefonin në temperatura të larta"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Shiko hapat për kujdesin"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefoni po bëhet i ngrohtë"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Disa funksione janë të kufizuara kur telefoni është duke u ftohur"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Disa veçori janë të kufizuara kur telefoni është duke u ftohur.\nTrokit për më shumë informacione"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefoni yt do të përpiqet automatikisht që të ftohet. Mund ta përdorësh përsëri telefonin, por ai mund të punojë më ngadalë.\n\nPasi telefoni të jetë ftohur, ai do të punojë si normalisht."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Shiko hapat për kujdesin"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Shkëput karikuesin"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Ka një problem me karikimin e kësaj pajisjeje. Hiqe spinën dhe trego kujdes pasi kablloja mund të jetë e ngrohtë."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Shiko hapat për kujdesin"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Mbaje të shtypur dhe zvarrit për të risistemuar kontrollet"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Të gjitha kontrollet u hoqën"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Ndryshimet nuk u ruajtën"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Shiko aplikacionet e tjera"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontrollet nuk mund të ngarkoheshin. Kontrollo aplikacionin <xliff:g id="APP">%s</xliff:g> për t\'u siguruar që cilësimet e aplikacionit nuk janë ndryshuar."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Kontrollet e përputhshme nuk ofrohen"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Tjetër"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Konfirmo ndryshimin për <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Rrëshqit shpejt për të shikuar më shumë"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Po ngarkon rekomandimet"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Mbyll këtë sesion të medias"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Fshih sesionin aktual."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Fshih"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Vazhdo"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Cilësimet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Joaktive, kontrollo aplikacionin"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Gabim, po provohet përsëri"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nuk u gjet"</string>
diff --git a/packages/SystemUI/res/values-sq/strings_tv.xml b/packages/SystemUI/res/values-sq/strings_tv.xml
index 624be930cc92..6cecdb6e38f4 100644
--- a/packages/SystemUI/res/values-sq/strings_tv.xml
+++ b/packages/SystemUI/res/values-sq/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Figurë brenda figurës"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Program pa titull)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Mbyll PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Ekrani i plotë"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofoni aktiv"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s pati qasje te mikrofoni yt"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 82df6289e027..fd6f57e64113 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -456,10 +456,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Додирните поново да бисте отворили"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Превуците нагоре да бисте отворили"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Превуците нагоре да бисте пробали поново"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Овај уређај припада организацији"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Овај уређај припада организацији <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Превуците од иконе за телефон"</string>
<string name="voice_hint" msgid="7476017460191291417">"Превуците од иконе за гласовну помоћ"</string>
<string name="camera_hint" msgid="4519495795000658637">"Превуците од иконе за камеру"</string>
@@ -526,33 +524,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Профил се можда надгледа"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Мрежа се можда надгледа"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Мрежа се можда надгледа"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Организација је власник уређаја и може да надгледа мрежни саобраћај"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> је власник овог уређаја и може да надгледа мрежни саобраћај"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Овај уређај припада организацији и повезан је са апликацијом <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Овај уређај припада организацији <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> и повезан је са апликацијом <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Овај уређај припада организацији"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Овај уређај припада организацији <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Овај уређај припада организацији и повезан је са VPN-овима"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Овај уређај припада организацији <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> и повезан је са VPN-овима"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Организација може да прати мрежни саобраћај на пословном профилу"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> може да надгледа мрежни саобраћај на пословном профилу"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Мрежа се можда надгледа"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Овај уређај је повезан са VPN-овима"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Пословни профил је повезан са апликацијом <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Ваш лични профил је повезан са апликацијом <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Овај уређај је повезан са апликацијом <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Управљање уређајима"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Надгледање профила"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Надгледање мреже"</string>
@@ -562,10 +548,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Онемогући VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Прекини везу са VPN-ом"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Прикажи смернице"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Овај уређај припада организацији <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nИТ администратор може да надгледа подешавања, корпоративни приступ, апликације, податке повезане са уређајем и информације о локацији уређаја, као и да управља њима.\n\nВише информација потражите од ИТ администратора."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Овај уређај припада организацији.\n\nИТ администратор може да надгледа подешавања, корпоративни приступ, апликације, податке повезане са уређајем и информације о локацији уређаја, као и да управља њима.\n\nВише информација потражите од ИТ администратора."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Организација је на овом уређају инсталирала ауторитет за издавање сертификата. Безбедни мрежни саобраћај може да се прати или мења."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Организација је на пословном профилу инсталирала ауторитет за издавање сертификата. Безбедни мрежни саобраћај може да се прати или мења."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На овом уређају је инсталиран ауторитет за издавање сертификата. Безбедни мрежни саобраћај може да се прати или мења."</string>
@@ -730,15 +714,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Нечујно"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Подразумевано"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Облачић"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Аутоматска"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звука и вибрирања"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звука и вибрирања и приказује се у наставку одељка за конверзације"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да звони или вибрира у зависности од подешавања телефона"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да звони или вибрира у зависности од подешавања телефона. Конверзације из апликације <xliff:g id="APP_NAME">%1$s</xliff:g> се подразумевано приказују у облачићима."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Привлачи вам пажњу помоћу плутајуће пречице до овог садржаја."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Нека систем утврди да ли ово обавештење треба да емитује звук или да вибрира"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Приказује се у врху одељка за конверзације као плутајући облачић, приказује слику профила на закључаном екрану"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Подешавања"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
@@ -759,18 +741,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ова апликација се приказује преко других апликација на екрану и користи микрофон и камеру."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Подешавања"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Потврди"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Систем је искључио ово обавештење."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Систем је промовисао ово обавештење."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Систем је поставио ово обавештење на дно."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Да ли је то тачно?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Хвала вам на повратним информацијама!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Потврди"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Контроле обавештења за отварање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Контроле обавештења за затварање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Дозволи обавештења са овог канала"</string>
@@ -945,26 +921,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Измени редослед подешавања."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. страна од <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Закључан екран"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Прошири"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Умањи"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Затвори"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Подешавања"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Превуците надоле да бисте одбили"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Мени"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> је слика у слици"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ако не желите да <xliff:g id="NAME">%s</xliff:g> користи ову функцију, додирните да бисте отворили подешавања и искључили је."</string>
- <string name="pip_play" msgid="333995977693142810">"Пусти"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Паузирај"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Пређи на следеће"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Пређи на претходно"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Телефон се искључио због топлоте"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Телефон сада нормално ради"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Телефон сада нормално ради.\nДодирните за више информација"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Телефон је био преврућ, па се искључио да се охлади. Сада ради нормално.\n\nТелефон може превише да се угреје ако:\n • Користите апликације које захтевају пуно ресурса (нпр. видео игре, видео или апликације за навигацију)\n • Преузимате/отпремате велике датотеке\n • Користите телефон на високој температури"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Погледајте упозорења"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Телефон се загрејао"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Неке функције су ограничене док се телефон не охлади"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Неке функције су ограничене док се телефон не охлади.\nДодирните за више информација"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефон ће аутоматски покушати да се охлади. И даље ћете моћи да користите телефон, али ће спорије реаговати.\n\nКада се телефон охлади, нормално ће радити."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Погледајте упозорења"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Искључите пуњач из напајања"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Дошло је до проблема са пуњењем овог уређаја. Искључите адаптер из напајања и будите пажљиви јер кабл може да буде топао."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Погледајте упозорења"</string>
@@ -1084,6 +1048,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Задржите и превуците да бисте променили распоред контрола"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Све контроле су уклоњене"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Промене нису сачуване"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Погледајте друге апликације"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Учитавање контрола није успело. Погледајте апликацију <xliff:g id="APP">%s</xliff:g> да бисте се уверили да се подешавања апликације нису променила."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Компатибилне контроле нису доступне"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Друго"</string>
@@ -1101,8 +1066,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Потврдите промену за: <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Превуците да бисте видели још"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Учитавају се препоруке"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Затворите ову сесију медија"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Медији"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Сакријте актуелну сесију."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Сакриј"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Настави"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Подешавања"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно. Видите апликацију"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Грешка, покушава се поново…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Није пронађено"</string>
diff --git a/packages/SystemUI/res/values-sr/strings_tv.xml b/packages/SystemUI/res/values-sr/strings_tv.xml
index 96846e702002..322938f4f787 100644
--- a/packages/SystemUI/res/values-sr/strings_tv.xml
+++ b/packages/SystemUI/res/values-sr/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Слика у слици"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Програм без наслова)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Затвори PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Цео екран"</string>
<string name="mic_active" msgid="5766614241012047024">"Микрофон је активан"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Апликација %1$s је приступила микрофону"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c8b221b94fcf..f96dd27aba6d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Tryck igen för att öppna"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Öppna genom att svepa uppåt"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Svep uppåt om du vill försöka igen"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Den här enheten tillhör organisationen"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Den här enheten tillhör <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Svep från ikonen och öppna telefonen"</string>
<string name="voice_hint" msgid="7476017460191291417">"Svep från ikonen och öppna röstassistenten"</string>
<string name="camera_hint" msgid="4519495795000658637">"Svep från ikonen och öppna kameran"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Det kan hända att profilen övervakas"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Nätverket kan vara övervakat"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Nätverket kan vara övervakat"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisationen äger den här enheten och kan övervaka nätverkstrafiken"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> äger den här enheten och kan övervaka nätverkstrafiken"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Den här enheten tillhör organisationen och är ansluten till <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Den här enheten tillhör <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> och är ansluten till <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Den här enheten tillhör organisationen"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Den här enheten tillhör <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Den här enheten tillhör organisationen och är ansluten till VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Den här enheten tillhör <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> och är ansluten till VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Organisationen kan övervaka nätverkstrafik i jobbprofilen"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kan övervaka nätverkstrafiken i jobbprofilen"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Nätverket kan vara övervakat"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Den här enheten är ansluten till VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Jobbprofilen är ansluten till <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Din personliga profil är ansluten till <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Den här enheten är ansluten till <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Enhetshantering"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profilövervakning"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Nätverksövervakning"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Inaktivera VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Koppla från VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Visa policyer"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Den här enheten tillhör <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nIT-administratören kan övervaka och hantera inställningar, företagsåtkomst, appar, data med koppling till enheten och enhetens plats.\n\nKontakta IT-administratören om du vill veta mer."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Den här enheten tillhör organisationen.\n\nIT-administratören kan övervaka och hantera inställningar, företagsåtkomst, appar, data med koppling till enheten och enhetens plats.\n\nKontakta IT-administratören om du vill veta mer."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organisationen har installerat en certifikatutfärdare på enheten. Din säkra nätverkstrafik kan övervakas och ändras."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisationen har installerat en certifikatutfärdare i jobbprofilen. Din säkra nätverkstrafik kan övervakas och ändras."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"En certifikatutfärdare är installerad på enheten. Din säkra nätverkstrafik kan övervakas och ändras."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Tyst"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bubbla"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Automatiskt"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Inga ljud eller vibrationer"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Inga ljud eller vibrationer och visas längre ned bland konversationerna"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan ringa eller vibrera beroende på inställningarna på telefonen"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan ringa eller vibrera beroende på inställningarna på telefonen. Konversationer från <xliff:g id="APP_NAME">%1$s</xliff:g> visas i bubblor som standard."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Behåller din uppmärksamhet med en flytande genväg till innehållet."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Låt systemet avgöra om den här aviseringen ska låta eller vibrera"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Visas högst upp bland konversationerna som en flytande bubbla, visar profilbilden på låsskärmen"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Inställningar"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Appen visas över andra appar på skärmen och den använder mikrofonen och kameran."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Inställningar"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Den här aviseringen har tystats av systemet."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Den här aviseringen har flyttats upp av systemet."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Den här aviseringen har flyttats ned av systemet."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Stämmer detta?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Tack för din feedback!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Aviseringsinställningarna för <xliff:g id="APP_NAME">%1$s</xliff:g> är öppna"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Aviseringsinställningarna för <xliff:g id="APP_NAME">%1$s</xliff:g> har stängts"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Tillåt aviseringar från den här kanalen"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Ändra ordning på inställningarna."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Sida <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Låsskärm"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Utöka"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Minimera"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Stäng"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Inställningar"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Tryck och dra nedåt för att ta bort"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Meny"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> visas i bild-i-bild"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Om du inte vill att den här funktionen används i <xliff:g id="NAME">%s</xliff:g> öppnar du inställningarna genom att trycka. Sedan inaktiverar du funktionen."</string>
- <string name="pip_play" msgid="333995977693142810">"Spela upp"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pausa"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Hoppa till nästa"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Hoppa till föregående"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Mobilen stängdes av pga. värme"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Mobilen fungerar nu som vanligt"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefonen fungerar nu som vanligt.\nTryck för mer information"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Mobilen var för varm och stängdes av för att kylas ned. Den fungerar nu som vanligt.\n\nMobilen kan bli för varm om du\n • använder resurskrävande appar (till exempel spel-, video- eller navigeringsappar)\n • laddar ned eller laddar upp stora filer\n • använder mobilen vid höga temperaturer."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Visa alla skötselråd"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Mobilen börjar bli varm"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Vissa funktioner är begränsade medan mobilen svalnar"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Vissa funktioner är begränsade medan telefonen svalnar.\nTryck för mer information"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Mobilen försöker svalna automatiskt. Du kan fortfarande använda mobilen, men den kan vara långsammare än vanligt.\n\nMobilen fungerar som vanligt när den har svalnat."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Visa alla skötselråd"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Koppla ur laddaren"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Det går inte att ladda denna enhet. Koppla ur nätadaptern, men var försiktig eftersom kabeln kan vara varm."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Visa alla skötselråd"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Ändra ordning på kontrollerna genom att trycka och dra"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Alla kontroller har tagits bort"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Ändringarna har inte sparats"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Visa andra appar"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Det gick inte att läsa in enhetsstyrning. Kontrollera att inställningarna inte har ändrats i <xliff:g id="APP">%s</xliff:g>-appen."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Ingen kompatibel enhetsstyrning tillgänglig"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Övrigt"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Bekräfta ändring av <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Svep om du vill se mer"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Rekommendationer läses in"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Stäng den här sessionen"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Dölj den aktuella sessionen."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Dölj"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Återuppta"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Inställningar"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv, kolla appen"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Fel, försöker igen …"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hittades inte"</string>
diff --git a/packages/SystemUI/res/values-sv/strings_tv.xml b/packages/SystemUI/res/values-sv/strings_tv.xml
index cf40057a005a..fb28af449fb7 100644
--- a/packages/SystemUI/res/values-sv/strings_tv.xml
+++ b/packages/SystemUI/res/values-sv/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Bild-i-bild"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Namnlöst program)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Stäng PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Helskärm"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofonen är aktiv"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s har fått åtkomst till mikrofonen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 8046bc64fdc5..8b8c24983e98 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Gusa tena ili ufungue"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Telezesha kidole juu ili ufungue"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Telezesha kidole juu ili ujaribu tena"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Kifaa hiki kinamilikiwa na shirika lako"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Kifaa hiki kinamilikiwa na <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telezesha kidole kutoka kwa aikoni ili ufikie simu"</string>
<string name="voice_hint" msgid="7476017460191291417">"Telezesha kidole kutoka aikoni ili upate mapendekezo ya sauti"</string>
<string name="camera_hint" msgid="4519495795000658637">"Telezesha kidole kutoka aikoni ili ufikie kamera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Huenda wasifu ukafuatiliwa"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Huenda mtandao unafuatiliwa"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Huenda mtandao unafuatiliwa"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Shirika lako linamiliki kifaa hiki na huenda likafuatilia trafiki ya mtandao"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> inamiliki kifaa hiki na huenda ikafuatilia trafiki ya mtandao"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Kifaa hiki kinamilikiwa na shirika lako na kimeunganishwa kwenye <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Kifaa hiki kinamilikiwa na <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> na kimeunganishwa kwenye <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Kifaa hiki kinamilikiwa na shirika lako"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Kifaa hiki kinamilikiwa na <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Kifaa hiki kinamilikiwa na shirika lako na kimeunganishwa kwenye VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Kifaa hiki kinamilikiwa na <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> na kimeunganishwa kwenye VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Huenda shirika lako likafuatilia shughuli kwenye mtandao katika wasifu wako wa kazini"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Huenda <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ikafuatilia shughuli kwenye mtandao katika wasifu wako wa kazini"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Huenda mtandao unafuatiliwa"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Kifaa hiki kimeunganishwa kwenye VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Wasifu wako wa kazini umeunganishwa kwenye <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Wasifu wako wa binafsi umeunganishwa kwenye <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Kifaa hiki kimeunganishwa kwenye <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Udhibiti wa kifaa"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Ufuatiliaji wasifu"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Ufuatiliaji wa mtandao"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Zima VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Ondoa VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Angalia Sera"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Kifaa hiki kinamilikiwa na <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nMsimamizi wako wa TEHAMA anaweza kufuatilia na kudhibiti mipangilio, ufikiaji wa maudhui ya shirika, programu, data inayohusiana na kifaa chako na maelezo kuhusu mahali kifaa chako kilipo.\n\nKwa maelezo zaidi, wasiliana na msimamizi wako wa TEHAMA."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Kifaa hiki kinamilikiwa na shirika lako.\n\nMsimamizi wako wa TEHAMA anaweza kufuatilia na kudhibiti mipangilio, ufikiaji wa maudhui ya shirika, programu, data inayohusiana na kifaa chako na maelezo kuhusu mahali kifaa chako kilipo.\n\nKwa maelezo zaidi, wasiliana na msimamizi wako wa TEHAMA."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Shirika lako limesakinisha mamlaka ya cheti kwenye kifaa hiki. Huenda shughuli kwenye mtandao wako salama zikafuatiliwa au kubadilishwa."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Shirika lako limesakinisha mamlaka ya cheti katika wasifu wako wa kazini. Huenda shughuli kwenye mtandao wako salama zikafuatiliwa au kubadilishwa."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Mamlaka ya cheti imesakinishwa kwenye kifaa hiki. Huenda shughuli kwenye mtandao wako salama zikafuatiliwa au kubadilishwa."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Kimya"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Chaguomsingi"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Kiputo"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatiki"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Hakuna sauti wala mtetemo"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Hakuna sauti wala mtetemo na huonekana upande wa chini katika sehemu ya mazungumzo"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Huenda ikalia au kutetema kulingana na mipangilio ya simu"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Huenda ikalia au kutetema kulingana na mipangilio ya simu. Mazungumzo kutoka kiputo cha <xliff:g id="APP_NAME">%1$s</xliff:g> kwa chaguomsingi."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Huweka umakinifu wako kwenye maudhui haya kwa kutumia njia ya mkato ya kuelea."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ruhusu mfumo ubainishe iwapo arifa hii inapaswa kutoa sauti au mtetemo"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Huonyeshwa kwenye sehemu ya juu ya mazungumzo, huonekana kama kiputo, huonyesha picha ya wasifu kwenye skrini iliyofungwa"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Mipangilio"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Kipaumbele"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Programu hii inachomoza kwenye programu zingine zilizo katika skrini yako na inatumia maikrofoni na kamera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Mipangilio"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Sawa"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Arifa hii ilizimwa na mfumo."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Arifa hii ilipandishwa hadhi na mfumo."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Arifa hii ilishushwa hadhi na mfumo."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Je, hatua hii ilikuwa sahihi?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Asante kwa maoni yako!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Sawa"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Vidhibiti vya arifa <xliff:g id="APP_NAME">%1$s</xliff:g> vimefunguliwa"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Vidhibiti vya arifa vya <xliff:g id="APP_NAME">%1$s</xliff:g> vimefungwa"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Ruhusu arifa kutoka kwenye kituo hiki"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Badilisha orodha ya mipangilio."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Ukurasa wa <xliff:g id="ID_1">%1$d</xliff:g> kati ya <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Skrini iliyofungwa"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Panua"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Punguza"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Funga"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Mipangilio"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Buruta ili uondoe"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menyu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> iko katika hali ya picha ndani ya picha nyingine"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Ikiwa hutaki <xliff:g id="NAME">%s</xliff:g> itumie kipengele hiki, gusa ili ufungue mipangilio na uizime."</string>
- <string name="pip_play" msgid="333995977693142810">"Cheza"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Sitisha"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Ruka ufikie inayofuata"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Ruka ufikie iliyotangulia"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Simu ilizima kutokana na joto"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Simu yako sasa inafanya kazi ipasavyo"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Simu yako sasa inafanya kazi ipasavyo.\nGusa ili upate maelezo zaidi"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Simu yako ilikuwa moto sana, kwa hivyo ilijizima ili ipoe. Simu yako sasa inafanya kazi ipasavyo.\n\nSimu yako inaweza kuwa moto sana ikiwa:\n • Unatumia programu zinazotumia vipengee vingi (kama vile michezo ya video, video au programu za uelekezaji)\n • Unapakua au upakie faili kubwa\n • Unatumia simu yako katika maeneo yenye halijoto ya juu"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Angalia hatua za utunzaji"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Joto la simu linaongezeka"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Baadhi ya vipengele havitatumika kwenye simu wakati inapoa"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Baadhi ya vipengele havitatumika kwenye simu wakati inapoa.\nGusa ili upate maelezo zaidi"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Simu yako itajaribu kupoa kiotomatiki. Bado unaweza kutumia simu yako, lakini huenda ikafanya kazi polepole. \n\nPindi simu yako itakapopoa, itaendelea kufanya kazi kama kawaida."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Angalia hatua za utunzaji"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Chomoa chaja"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Kuna tatizo la kuchaji kifaa hiki. Chomoa adapta ya nishati na uwe mwangalifu, huenda kebo ni moto."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Angalia hatua za ulinzi"</string>
@@ -1078,7 +1042,8 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Shikilia na uburute ili upange vidhibiti upya"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Umeondoa vidhibiti vyote"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Mabadiliko hayajahifadhiwa"</string>
- <string name="controls_favorite_load_error" msgid="5126216176144877419">"Imeshindwa kupakia vidhibiti. Angalia programu ya <xliff:g id="APP">%s</xliff:g> ili uhakikishe kuwa mipangilio ya programu haijabadilika."</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Angalia programu zingine"</string>
+ <string name="controls_favorite_load_error" msgid="5126216176144877419">"Imeshindwa kupakia vidhibiti. Angalia programu ya <xliff:g id="APP">%s</xliff:g> ili uhakikishe kuwa mipangilio yake haijabadilika."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Vidhibiti vinavyooana havipatikani"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Nyingine"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"Weka kwenye vidhibiti vya vifaa"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Thibitisha mabadiliko kwenye <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Telezesha kidole ili uone zaidi"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Inapakia mapendekezo"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Funga kipindi hiki cha maudhui"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Maudhui"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Ficha kipindi cha sasa."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ficha"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Endelea"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Mipangilio"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Haitumiki, angalia programu"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Hitilafu, inajaribu tena…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hakipatikani"</string>
diff --git a/packages/SystemUI/res/values-sw/strings_tv.xml b/packages/SystemUI/res/values-sw/strings_tv.xml
index 7e2a64cf5a6c..b51f93410bb4 100644
--- a/packages/SystemUI/res/values-sw/strings_tv.xml
+++ b/packages/SystemUI/res/values-sw/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picha ndani ya picha"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Programu isiyo na jina)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Funga PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Skrini nzima"</string>
<string name="mic_active" msgid="5766614241012047024">"Maikrofoni Inatumika"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s imefikia maikrofoni yako"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 5b4860b495cf..b97e266850c6 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"திறக்க, மீண்டும் தட்டவும்"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"திறப்பதற்கு மேல் நோக்கி ஸ்வைப் செய்யவும்"</string>
<string name="keyguard_retry" msgid="886802522584053523">"மீண்டும் முயல மேல்நோக்கி ஸ்வைப் செய்யவும்"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு சொந்தமானது"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"இந்த சாதனம் <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> நிறுவனத்துக்கு சொந்தமானது"</string>
<string name="phone_hint" msgid="6682125338461375925">"ஃபோனிற்கு ஐகானிலிருந்து ஸ்வைப் செய்யவும்"</string>
<string name="voice_hint" msgid="7476017460191291417">"குரல் உதவிக்கு ஐகானிலிருந்து ஸ்வைப் செய்யவும்"</string>
<string name="camera_hint" msgid="4519495795000658637">"கேமராவிற்கு ஐகானிலிருந்து ஸ்வைப் செய்யவும்"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"சுயவிவரம் கண்காணிக்கப்படலாம்"</string>
<string name="vpn_footer" msgid="3457155078010607471">"நெட்வொர்க் கண்காணிக்கப்படலாம்"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"நெட்வொர்க் கண்காணிக்கப்படலாம்"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு உரியது, நெட்வொர்க் ட்ராஃபிக்கையும் நிறுவனமே கண்காணிக்கக்கூடும்"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"இந்த சாதனம் <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> நிறுவனத்துக்கு உரியது, நெட்வொர்க் ட்ராஃபிக்கையும் நிறுவனமே கண்காணிக்கக்கூடும்"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு சொந்தமானது, அது <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளது"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"இந்த சாதனம் <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> நிறுவனத்துக்கு சொந்தமானது, அது <xliff:g id="VPN_APP">%2$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளது"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு சொந்தமானது"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"இந்த சாதனம் <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> நிறுவனத்துக்கு சொந்தமானது"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு சொந்தமானது, அது VPNகளுடன் இணைக்கப்பட்டுள்ளது"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"இந்த சாதனம் <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> நிறுவனத்துக்கு சொந்தமானது, அது VPNகளுடன் இணைக்கப்பட்டுள்ளது"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"உங்கள் நிறுவனம் பணிக் கணக்கில் நெட்வொர்க் ட்ராஃபிக்கைக் கண்காணிக்கலாம்"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> உங்கள் பணிக் கணக்கில் நெட்வொர்க் ட்ராஃபிக்கைக் கண்காணிக்கலாம்"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"நெட்வொர்க் கண்காணிக்கப்படலாம்"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"இந்த சாதனம் VPNகளுடன் இணைக்கப்பட்டுள்ளது"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"உங்கள் பணிக் கணக்கு <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளது"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"உங்கள் தனிப்பட்ட சுயவிவரம் <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளது"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"இந்த சாதனம் <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளது"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"சாதன நிர்வாகம்"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"சுயவிவரத்தைக் கண்காணித்தல்"</string>
<string name="monitoring_title" msgid="4063890083735924568">"நெட்வொர்க்கைக் கண்காணித்தல்"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPNஐ முடக்கு"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPNஐத் துண்டி"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"கொள்கைகளைக் காட்டு"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"இந்த சாதனம் <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> நிறுவனத்துக்கு சொந்தமானது.\n\nஉங்கள் IT நிர்வாகியால் அமைப்புகள், நிறுவன அணுகல், ஆப்ஸ், உங்கள் சாதனத்துடன் தொடர்புடைய தரவு, சாதனத்தின் இருப்பிடத் தகவல்கள் ஆகியவற்றைக் கண்காணிக்கவும் நிர்வகிக்கவும் முடியும்.\n\nமேலும் தகவல்களுக்கு IT நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு சொந்தமானது.\n\nஉங்கள் IT நிர்வாகியால் அமைப்புகள், நிறுவன அணுகல், ஆப்ஸ், உங்கள் சாதனத்துடன் தொடர்புடைய தரவு, சாதனத்தின் இருப்பிடத் தகவல்கள் ஆகியவற்றைக் கண்காணிக்கவும் நிர்வகிக்கவும் முடியும்.\n\nமேலும் தகவல்களுக்கு IT நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"உங்கள் நிறுவனம் இந்தச் சாதனத்தில் சான்றிதழ் அங்கீகாரத்தை நிறுவியுள்ளது. உங்களின் பாதுகாப்பான நெட்வொர்க் ட்ராஃபிக் கண்காணிக்கப்படலாம் அல்லது மாற்றப்படலாம்."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"உங்கள் நிறுவனம், பணிக் கணக்கில் சான்றிதழ் அங்கீகாரத்தை நிறுவியுள்ளது. உங்களின் பாதுகாப்பான நெட்வொர்க் ட்ராஃபிக் கண்காணிக்கப்படலாம் அல்லது மாற்றப்படலாம்."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"இந்தச் சாதனத்தில் சான்றிதழ் அங்கீகாரம் நிறுவப்பட்டுள்ளது. உங்களின் பாதுகாப்பான நெட்வொர்க் ட்ராஃபிக் கண்காணிக்கப்படலாம் அல்லது மாற்றப்படலாம்."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"நிசப்தம்"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"இயல்புநிலை"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"பபிள்"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"தானியங்கு"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ஒலி / அதிர்வு இல்லை"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ஒலி / அதிர்வு இல்லாமல் உரையாடல் பிரிவின் கீழ்ப் பகுதியில் தோன்றும்"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"மொபைல் அமைப்புகளின் அடிப்படையில் ஒலிக்கவோ அதிரவோ செய்யும்"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"மொபைல் அமைப்புகளின் அடிப்படையில் ஒலிக்கவோ அதிரவோ செய்யும். <xliff:g id="APP_NAME">%1$s</xliff:g> இலிருந்து வரும் உரையாடல்கள் இயல்பாகவே குமிழாகத் தோன்றும்."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"இந்த உள்ளடக்கத்திற்கான மிதக்கும் ஷார்ட்கட் மூலம் உங்கள் கவனத்தைப் பெற்றிருக்கும்."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"இந்த அறிவிப்பு ஒலி எழுப்ப வேண்டுமா அதிர வேண்டுமா என்பதை சிஸ்டம் தீர்மானிக்கும்"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"உரையாடல் பிரிவின் மேற்பகுதியில் மிதக்கும் குமிழாகத் தோன்றும். பூட்டுத் திரையின் மேல் சுயவிவரப் படத்தைக் காட்டும்"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"அமைப்புகள்"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"முன்னுரிமை"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"இந்த ஆப்ஸானது, உங்கள் திரையில் பிற ஆப்ஸின் இடைமுகத்தின் மேல் தோன்றுவதுடன், மைக்ரோஃபோனையும் கேமராவையும் உபயோகிக்கிறது."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"அமைப்புகள்"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"சரி"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"இந்த அறிவிப்பு சிஸ்டத்தால் ஒலியடக்கப்பட்டது."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"இந்த அறிவிப்பு சிஸ்டத்தால் முக்கியமானது என வரையறுக்கப்பட்டது."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"இந்த அறிவிப்பு சிஸ்டத்தால் முக்கியமில்லாதது என வரையறுக்கப்பட்டது."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"இது சரியானதா?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"உங்கள் கருத்துக்கு நன்றி!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"சரி"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>க்கான அறிவிப்புக் கட்டுப்பாடுகள் திறக்கப்பட்டன"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g>க்கான அறிவிப்புக் கட்டுப்பாடுகள் மூடப்பட்டன"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"இந்தச் சேனலிலிருந்து அறிவிப்புகளைப் பெறுவதை அனுமதிக்கும்"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"அமைப்புகளின் வரிசை முறையைத் திருத்து."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"பக்கம் <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"லாக் ஸ்கிரீன்"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"விரி"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"சிறிதாக்கு"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"மூடு"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"அமைப்புகள்"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"நிராகரிக்க, கீழே இழுக்கவும்"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"மெனு"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> தற்போது பிக்ச்சர்-இன்-பிக்ச்சரில் உள்ளது"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> இந்த அம்சத்தைப் பயன்படுத்த வேண்டாம் என நினைத்தால் இங்கு தட்டி அமைப்புகளைத் திறந்து இதை முடக்கவும்."</string>
- <string name="pip_play" msgid="333995977693142810">"இயக்கு"</string>
- <string name="pip_pause" msgid="1139598607050555845">"இடைநிறுத்து"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"அடுத்ததற்குச் செல்"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"முந்தையதற்குச் செல்"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"வெப்பத்தினால் ஃபோன் ஆஃப் செய்யப்பட்டது"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"இப்போது உங்கள் ஃபோன் இயல்புநிலையில் இயங்குகிறது"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"இப்போது உங்கள் மொபைல் இயல்புநிலையில் இயங்குகிறது.\nமேலும் தகவலுக்கு தட்டவும்"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"உங்கள் ஃபோன் அதிகமாகச் சூடானதால், அதன் சூட்டைக் குறைக்க, ஆஃப் செய்யப்பட்டது. இப்போது உங்கள் ஃபோன் இயல்புநிலையில் இயங்குகிறது.\n\nபின்வருவனவற்றைச் செய்தால், ஃபோன் சூடாகலாம்:\n • அதிகளவு தரவைப் பயன்படுத்தும் ஆப்ஸை (எ.கா: கேமிங், வீடியோ (அ) வழிகாட்டுதல் ஆப்ஸ்) பயன்படுத்துவது\n • பெரிய கோப்புகளைப் பதிவிறக்குவது/பதிவேற்றுவது\n • அதிக வெப்பநிலையில் ஃபோனைப் பயன்படுத்துவது"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"மேலும் விவரங்களுக்கு இதைப் பார்க்கவும்"</string>
<string name="high_temp_title" msgid="2218333576838496100">"மொபைல் சூடாகிறது"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"மொபைலின் வெப்ப அளவு குறையும் போது, சில அம்சங்களைப் பயன்படுத்த முடியாது"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"மொபைலின் வெப்ப அளவு குறையும் வரை சில அம்சங்களைப் பயன்படுத்த முடியாது.\nமேலும் தகவலுக்கு தட்டவும்"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"உங்கள் மொபைலின் வெப்ப அளவு தானாகவே குறையும். தொடர்ந்து நீங்கள் மொபைலைப் பயன்படுத்தலாம், ஆனால் அதன் வேகம் குறைவாக இருக்கக்கூடும்.\n\nமொபைலின் வெப்ப அளவு குறைந்தவுடன், அது இயல்பு நிலையில் இயங்கும்."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"மேலும் விவரங்களுக்கு இதைப் பார்க்கவும்"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"சார்ஜரைத் துண்டிக்கவும்"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"இந்தச் சாதனத்தைச் சார்ஜ் செய்வதில் சிக்கல் உள்ளது. பவர் அடாப்டரைத் துண்டிக்கவும், கேபிள் சூடாக இருக்கக்கூடும் என்பதால் கவனமாகக் கையாளவும்."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"மேலும் விவரங்களுக்கு இதைப் பார்க்கவும்"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"கட்டுப்பாடுகளை மறுவரிசைப்படுத்த அவற்றைப் பிடித்து இழுக்கவும்"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"கட்டுப்பாடுகள் அனைத்தும் அகற்றப்பட்டன"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"மாற்றங்கள் சேமிக்கப்படவில்லை"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"பிற ஆப்ஸையும் காட்டு"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"கட்டுப்பாடுகளை ஏற்ற முடியவில்லை. ஆப்ஸ் அமைப்புகள் மாறவில்லை என்பதை உறுதிப்படுத்த <xliff:g id="APP">%s</xliff:g> ஆப்ஸைப் பார்க்கவும்."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"இணக்கமான கட்டுப்பாடுகள் இல்லை"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"பிற"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> ஐ மாற்றுவதை உறுதிப்படுத்தவும்"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"மேலும் பார்க்க ஸ்வைப் செய்யவும்"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"பரிந்துரைகளை ஏற்றுகிறது"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"இந்த மீடியா அமர்வை மூடுக"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"மீடியா"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"இந்த அமர்வை மறையுங்கள்."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"மறை"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"தொடர்க"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"அமைப்புகள்"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"செயலில் இல்லை , சரிபார்க்கவும்"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"பிழை, மீண்டும் முயல்கிறது…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"இல்லை"</string>
diff --git a/packages/SystemUI/res/values-ta/strings_tv.xml b/packages/SystemUI/res/values-ta/strings_tv.xml
index 43418053f841..1ae3d1d3bdcd 100644
--- a/packages/SystemUI/res/values-ta/strings_tv.xml
+++ b/packages/SystemUI/res/values-ta/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"பிக்ச்சர்-இன்-பிக்ச்சர்"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(தலைப்பு இல்லை)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIPஐ மூடு"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"முழுத்திரை"</string>
<string name="mic_active" msgid="5766614241012047024">"மைக்ரோஃபோன் செயலிலுள்ளது"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s உங்கள் மைக்ரோஃபோனைப் பயன்படுத்தியது"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index d39dda7f0ee8..13319d9719b8 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"తెరవడానికి మళ్లీ నొక్కండి"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"తెరవడానికి, పైకి స్వైప్ చేయండి"</string>
<string name="keyguard_retry" msgid="886802522584053523">"మళ్ళీ ప్రయత్నించడానికి పైకి స్వైప్ చేయండి"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"ఈ పరికరం మీ సంస్థకు చెందినది"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>కు చెందినది"</string>
<string name="phone_hint" msgid="6682125338461375925">"ఫోన్ కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string>
<string name="voice_hint" msgid="7476017460191291417">"వాయిస్ అసిస్టెంట్ చిహ్నం నుండి స్వైప్"</string>
<string name="camera_hint" msgid="4519495795000658637">"కెమెరా కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string>
@@ -510,7 +508,7 @@
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>తో రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"మళ్లీ చూపవద్దు"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"అన్నీ క్లియర్ చేయండి"</string>
- <string name="manage_notifications_text" msgid="6885645344647733116">"నిర్వహించండి"</string>
+ <string name="manage_notifications_text" msgid="6885645344647733116">"మేనేజ్ చేయండి"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"చరిత్ర"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"కొత్తవి"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"నిశ్శబ్దం"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ప్రొఫైల్‌ని పర్యవేక్షించవచ్చు"</string>
<string name="vpn_footer" msgid="3457155078010607471">"నెట్‌వర్క్ పర్యవేక్షించబడవచ్చు"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"నెట్‌వర్క్ పర్యవేక్షించబడవచ్చు"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ఈ పరికరం మీ సంస్థకు చెందినది, కాబట్టి అది నెట్‌వర్క్ ట్రాఫిక్‌ను పర్యవేక్షించవచ్చు"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"మీ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది, కాబట్టి అది నెట్‌వర్క్ ట్రాఫిక్‌ను పర్యవేక్షించవచ్చు"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ఈ పరికరం మీ సంస్థకు చెందినది, ఇది <xliff:g id="VPN_APP">%1$s</xliff:g>కు కనెక్ట్ అయి ఉంది"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది, ఇది <xliff:g id="VPN_APP">%2$s</xliff:g>కు కనెక్ట్ అయి ఉంది"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ఈ పరికరం మీ సంస్థకు చెందినది"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"ఈ పరికరం మీ సంస్థకు చెందినది, ఇది VPNలకు కనెక్ట్ అయి ఉంది"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది, ఇది VPNలకు కనెక్ట్ అయి ఉంది"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"మీ కార్యాలయ ప్రొఫైల్‌లోని నెట్‌వర్క్ ట్రాఫిక్‌ని మీ సంస్థ పర్యవేక్షించవచ్చు"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"మీ కార్యాలయ ప్రొఫైల్‌లోని నెట్‌వర్క్ ట్రాఫిక్‌ని <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> పర్యవేక్షించవచ్చు"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"నెట్‌వర్క్ పర్యవేక్షించబడవచ్చు"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"ఈ పరికరం VPNలకు కనెక్ట్ అయి ఉంది"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"మీ వర్క్ ప్రొఫైల్ <xliff:g id="VPN_APP">%1$s</xliff:g>కు కనెక్ట్ చేయబడింది"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"<xliff:g id="VPN_APP">%1$s</xliff:g>కు మీ వ్యక్తిగత ప్రొఫైల్ కనెక్ట్ చేయబడింది"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ఈ పరికరం <xliff:g id="VPN_APP">%1$s</xliff:g>కు కనెక్ట్ అయి ఉంది"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"పరికర నిర్వహణ"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"ప్రొఫైల్ పర్యవేక్షణ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"నెట్‌వర్క్ పర్యవేక్షణ"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPNని నిలిపివేయి"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPNను డిస్‌కనెక్ట్ చేయి"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"విధానాలను వీక్షించండి"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది.\n\nసెట్టింగ్‌లను, కార్పొరేట్ యాక్సెస్‌ను, యాప్‌లను, మీ పరికరానికి సంబంధించిన డేటాను, అలాగే మీ పరికరం యొక్క లొకేషన్ సమాచారాన్ని మీ IT అడ్మిన్ పర్యవేక్షించగలరు, మేనేజ్ చేయగలరు.\n\nమరింత సమాచారం కోసం, మీ IT అడ్మిన్‌ను సంప్రదించండి."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"ఈ పరికరం మీ సంస్థకు చెందినది.\n\nసెట్టింగ్‌లను, కార్పొరేట్ యాక్సెస్‌ను, యాప్‌లను, మీ పరికరానికి సంబంధించిన డేటాను, అలాగే మీ పరికరం యొక్క లొకేషన్ సమాచారాన్ని మీ IT అడ్మిన్ పర్యవేక్షించగలరు, మేనేజ్ చేయగలరు.\n\nమరింత సమాచారం కోసం, మీ IT అడ్మిన్‌ను సంప్రదించండి."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ఈ పరికరంలో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్‌స్టాల్ చేసింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"మీ కార్యాలయ ప్రొఫైల్‌లో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్‌స్టాల్ చేసింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ఈ పరికరంలో ప్రమాణపత్ర అధికారం ఇన్‌స్టాల్ చేయబడింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"నిశ్శబ్దం"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ఆటోమేటిక్ సెట్టింగ్"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"బబుల్"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"ఆటోమేటిక్"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"శబ్దం లేదా వైబ్రేషన్‌లు ఏవీ లేవు"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"శబ్దం లేదా వైబ్రేషన్ లేదు, సంభాషణ విభాగం దిగువన కనిపిస్తుంది"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ఫోన్ సెట్టింగ్‌ల ఆధారంగా రింగ్ లేదా వైబ్రేట్ కావచ్చు"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ఫోన్ సెట్టింగ్‌ల ఆధారంగా రింగ్ లేదా వైబ్రేట్ కావచ్చు. <xliff:g id="APP_NAME">%1$s</xliff:g> నుండి సంభాషణలు ఆటోమేటిక్‌గా బబుల్‌గా కనిపిస్తాయి."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ఫ్లోటింగ్ షార్ట్‌కట్‌తో మీ దృష్టిని ఈ కంటెంట్‌పై నిలిపి ఉంచుతుంది."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ఈ నోటిఫికేషన్ వచ్చినప్పుడు శబ్దం చేయాలా లేదా వైబ్రేట్ చేయాలా అనేది నిర్ణయించడానికి సిస్టమ్‌కు అనుమతి ఇవ్వండి"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"సంభాషణ విభాగం ఎగువన ఉంటుంది, తేలుతున్న బబుల్‌లాగా కనిపిస్తుంది, లాక్ స్క్రీన్‌పై ప్రొఫైల్ ఫోటోను ప్రదర్శిస్తుంది"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"సెట్టింగ్‌లు"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ప్రాధాన్యత"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ఈ యాప్ మీ స్క్రీన్‌లోని ఇతర యాప్‌లపై ప్రదర్శించబడుతోంది మరియు మైక్రోఫోన్, కెమెరాను ఉపయోగిస్తుంది."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"సెట్టింగ్‌లు"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"సరే"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"ఈ నోటిఫికేషన్‌ను సిస్టమ్ నిశ్శబ్దంగా ఉండేలా చేసింది."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"ఈ నోటిఫికేషన్ స్థాయిని సిస్టమ్ పెంచింది."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"ఈ నోటిఫికేషన్ స్థాయిని సిస్టమ్ తగ్గించింది."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"ఇది సరైనదేనా?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"మీ ఫీడ్‌బ్యాక్‌ను అందించినందుకు ధన్యవాదాలు!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"సరే"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> యొక్క నోటిఫికేషన్ నియంత్రణలు తెరవబడ్డాయి"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> యొక్క నోటిఫికేషన్ నియంత్రణలు మూసివేయబడ్డాయి"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"ఈ ఛానెల్ యొక్క నోటిఫికేషన్‌లను అనుమతించండి"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"సెట్టింగ్‌ల క్రమాన్ని సవరించండి."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>లో <xliff:g id="ID_1">%1$d</xliff:g>వ పేజీ"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"లాక్ స్క్రీన్"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"విస్తరింపజేయి"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"కనిష్టీకరించు"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"మూసివేయి"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"సెట్టింగ్‌లు"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"తీసివేయడానికి కిందికి లాగండి"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"మెను"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> చిత్రంలో చిత్రం రూపంలో ఉంది"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> ఈ లక్షణాన్ని ఉపయోగించకూడదు అని మీరు అనుకుంటే, సెట్టింగ్‌లను తెరవడానికి ట్యాప్ చేసి, దీన్ని ఆఫ్ చేయండి."</string>
- <string name="pip_play" msgid="333995977693142810">"ప్లే చేయి"</string>
- <string name="pip_pause" msgid="1139598607050555845">"పాజ్ చేయి"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"దాటవేసి తర్వాత దానికి వెళ్లు"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"దాటవేసి మునుపటి దానికి వెళ్లు"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"వేడెక్కినందుకు ఫోన్ ఆఫ్ చేయబడింది"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"మీ ఫోన్ ఇప్పుడు సాధారణంగా పని చేస్తుంది"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"మీ ఫోన్ ఇప్పుడు సాధారణంగా పని చేస్తోంది.\nమరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"మీ ఫోన్ చాలా వేడిగా ఉంది, కనుక చల్లబర్చడానికి ఆఫ్ చేయబడింది. మీ ఫోన్ ఇప్పుడు సాధారణంగా పని చేస్తుంది.\n\nమీరు ఇలా చేస్తే మీ ఫోన్ చాలా వేడెక్కవచ్చు:\n • వనరు-ఆధారిత అనువర్తనాలు (గేమింగ్, వీడియో లేదా నావిగేషన్ వంటి అనువర్తనాలు) ఉపయోగించడం\n • పెద్ద ఫైల్‌లను డౌన్‌లోడ్ లేదా అప్‌లోడ్ చేయడం\n • అధిక ఉష్ణోగ్రతలలో మీ ఫోన్‌ని ఉపయోగించడం"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
<string name="high_temp_title" msgid="2218333576838496100">"ఫోన్ వేడెక్కుతోంది"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ఫోన్‌ను చల్లబరిచే క్రమంలో కొన్ని లక్షణాలు పరిమితం చేయబడ్డాయి"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ఫోన్‌ను చల్లబరిచే క్రమంలో కొన్ని ఫీచర్లు పరిమితం చేయబడ్డాయి.\nమరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"మీ ఫోన్ స్వయంచాలకంగా చల్లబడటానికి ప్రయత్నిస్తుంది. మీరు ఇప్పటికీ మీ ఫోన్‌ను ఉపయోగించవచ్చు, కానీ దాని పనితీరు నెమ్మదిగా ఉండవచ్చు.\n\nమీ ఫోన్ చల్లబడిన తర్వాత, అది సాధారణ రీతిలో పని చేస్తుంది."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ప్లగ్ నుండి ఛార్జర్‌ తీసివేయండి"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"ఈ పరికరాన్ని ఛార్జ్ చేయడంలో సమస్య ఉంది. పవర్ అడాప్టర్‌ను ప్లగ్ నుండి తీసివేసి, కేబుల్ ఏమైనా వేడిగా అయితే తగిన జాగ్రత్తలు తీసుకోండి."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
@@ -1028,7 +992,7 @@
<string name="bubbles_settings_button_description" msgid="7324245408859877545">"<xliff:g id="APP_NAME">%1$s</xliff:g> బబుల్స్ సెట్టింగ్‌లు"</string>
<string name="bubble_overflow_button_content_description" msgid="5523744621434300510">"ఓవర్‌ఫ్లో"</string>
<string name="bubble_accessibility_action_add_back" msgid="6217995665917123890">"స్ట్యాక్‌కు తిరిగి జోడించండి"</string>
- <string name="manage_bubbles_text" msgid="6856830436329494850">"నిర్వహించండి"</string>
+ <string name="manage_bubbles_text" msgid="6856830436329494850">"మేనేజ్ చేయండి"</string>
<string name="bubble_content_description_single" msgid="5175160674436546329">"<xliff:g id="APP_NAME">%2$s</xliff:g> నుండి <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="7907610717462651870">"<xliff:g id="APP_NAME">%2$s</xliff:g> నుండి <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> మరియు మరో <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"తరలించు"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"నియంత్రణల క్రమం మార్చడానికి దేనినైనా పట్టుకుని, లాగి వదిలేయండి"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"అన్ని నియంత్రణలు తీసివేయబడ్డాయి"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"మార్పులు సేవ్ చేయబడలేదు"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ఇతర యాప్‌లను చూడండి"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"కంట్రోల్‌లను లోడ్ చేయడం సాధ్యపడలేదు. యాప్ సెట్టింగ్‌లు మారలేదని నిర్ధారించడానికి <xliff:g id="APP">%s</xliff:g> యాప్‌ను చెక్ చేయండి."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"అనుకూల కంట్రోల్‌లు అందుబాటులో లేవు"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ఇతరం"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g>కి సంబంధించి మార్పును నిర్ధారించండి"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"మరిన్నింటిని చూడటం కోసం స్వైప్ చేయండి"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"సిఫార్సులు లోడ్ అవుతున్నాయి"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ఈ మీడియా సెషన్‌ని మూసివేయండి"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"మీడియా"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"ప్రస్తుత సెషన్‌ను దాచు."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"దాచు"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్‌లు"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ఇన్‌యాక్టివ్, యాప్ చెక్ చేయండి"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"లోపం, మళ్లీ ప్రయత్నిస్తోంది..."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"కనుగొనబడలేదు"</string>
diff --git a/packages/SystemUI/res/values-te/strings_tv.xml b/packages/SystemUI/res/values-te/strings_tv.xml
index df8b06d6705a..27911795d771 100644
--- a/packages/SystemUI/res/values-te/strings_tv.xml
+++ b/packages/SystemUI/res/values-te/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"పిక్చర్-ఇన్-పిక్చర్"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(శీర్షిక లేని ప్రోగ్రామ్)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIPని మూసివేయి"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"పూర్తి స్క్రీన్"</string>
<string name="mic_active" msgid="5766614241012047024">"మైక్రోఫోన్ యాక్టివ్‌గా ఉంది"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"మీ మైక్రోఫోన్‌ను %1$s యాక్సెస్ చేసింది"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 021387975f14..e1c0e7e7a614 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"แตะอีกครั้งเพื่อเปิด"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"เลื่อนขึ้นเพื่อเปิด"</string>
<string name="keyguard_retry" msgid="886802522584053523">"เลื่อนขึ้นเพื่อลองอีกครั้ง"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g> เป็นเจ้าของอุปกรณ์นี้"</string>
<string name="phone_hint" msgid="6682125338461375925">"เลื่อนไอคอนโทรศัพท์"</string>
<string name="voice_hint" msgid="7476017460191291417">"เลื่อนไอคอนตัวช่วยเสียง"</string>
<string name="camera_hint" msgid="4519495795000658637">"เลื่อนไอคอนกล้อง"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"อาจมีการตรวจสอบโปรไฟล์"</string>
<string name="vpn_footer" msgid="3457155078010607471">"เครือข่ายอาจได้รับการตรวจสอบ"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"เครือข่ายอาจถูกตรวจสอบ"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้และอาจตรวจสอบการจราจรของข้อมูลในเครือข่าย"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> เป็นเจ้าของอุปกรณ์นี้และอาจตรวจสอบการจราจรของข้อมูลในเครือข่าย"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้ และอุปกรณ์เชื่อมต่ออยู่กับ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> เป็นเจ้าของอุปกรณ์นี้ และอุปกรณ์เชื่อมต่ออยู่กับ <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> เป็นเจ้าของอุปกรณ์นี้"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้ และอุปกรณ์เชื่อมต่ออยู่กับ VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> เป็นเจ้าของอุปกรณ์นี้ และอุปกรณ์เชื่อมต่ออยู่กับ VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"องค์กรของคุณอาจตรวจสอบการจราจรของข้อมูลในเครือข่ายในโปรไฟล์งาน"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> อาจตรวจสอบการจราจรของข้อมูลในเครือข่ายในโปรไฟล์งานของคุณ"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"อาจมีการตรวจสอบเครือข่าย"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"อุปกรณ์นี้เชื่อมต่ออยู่กับ VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"โปรไฟล์งานของคุณเชื่อมต่ออยู่กับ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"โปรไฟล์ส่วนตัวของคุณเชื่อมต่ออยู่กับ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"อุปกรณ์นี้เชื่อมต่ออยู่กับ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"การจัดการอุปกรณ์"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"การตรวจสอบโปรไฟล์"</string>
<string name="monitoring_title" msgid="4063890083735924568">"การตรวจสอบเครือข่าย"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"ปิดใช้ VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"ยกเลิกการเชื่อมต่อ VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"ดูนโยบาย"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> เป็นเจ้าของอุปกรณ์นี้\n\nผู้ดูแลระบบไอทีจะตรวจสอบและจัดการการตั้งค่า การเข้าถึงของบริษัท แอป ข้อมูลที่เชื่อมโยงกับอุปกรณ์ และข้อมูลตำแหน่งของอุปกรณ์ได้\n\nติดต่อผู้ดูแลระบบไอทีหากต้องการข้อมูลเพิ่มเติม"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้\n\nผู้ดูแลระบบไอทีจะตรวจสอบและจัดการการตั้งค่า การเข้าถึงของบริษัท แอป ข้อมูลที่เชื่อมโยงกับอุปกรณ์ และข้อมูลตำแหน่งของอุปกรณ์ได้\n\nติดต่อผู้ดูแลระบบไอทีหากต้องการข้อมูลเพิ่มเติม"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"องค์กรของคุณติดตั้งผู้ออกใบรับรองในอุปกรณ์นี้ อาจมีการตรวจสอบหรือแก้ไขการจราจรของข้อมูลในเครือข่ายที่ปลอดภัยของคุณ"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"องค์กรของคุณติดตั้งผู้ออกใบรับรองในโปรไฟล์งาน อาจมีการตรวจสอบหรือแก้ไขการจราจรของข้อมูลในเครือข่ายที่ปลอดภัยของคุณ"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"มีการติดตั้งผู้ออกใบรับรองในอุปกรณ์นี้ อาจมีการตรวจสอบหรือแก้ไขการจราจรของข้อมูลในเครือข่ายที่ปลอดภัยของคุณ"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"เงียบ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ค่าเริ่มต้น"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"บับเบิล"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"อัตโนมัติ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ไม่มีเสียงหรือการสั่น"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ไม่มีเสียงหรือการสั่น และปรากฏต่ำลงมาในส่วนการสนทนา"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"อาจส่งเสียงหรือสั่นโดยขึ้นอยู่กับการตั้งค่าโทรศัพท์"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"อาจส่งเสียงหรือสั่นโดยขึ้นอยู่กับการตั้งค่าโทรศัพท์ การสนทนาจาก <xliff:g id="APP_NAME">%1$s</xliff:g> จะแสดงเป็นบับเบิลโดยค่าเริ่มต้น"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ดึงดูดความสนใจของคุณไว้เสมอด้วยทางลัดแบบลอยที่มายังเนื้อหานี้"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ให้ระบบพิจารณาว่าจะให้การแจ้งเตือนนี้ส่งเสียงหรือสั่นหรือไม่"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"แสดงที่ด้านบนของส่วนการสนทนา ปรากฏเป็นบับเบิลแบบลอย แสดงรูปโปรไฟล์บนหน้าจอล็อก"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"การตั้งค่า"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ลำดับความสำคัญ"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"แอปนี้กำลังแสดงทับแอปอื่นๆ ในหน้าจอและใช้ไมโครโฟนและกล้อง"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"การตั้งค่า"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ตกลง"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"การแจ้งเตือนนี้ถูกปิดเสียงโดยระบบ"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"การแจ้งเตือนนี้ได้รับการเพิ่มระดับโดยระบบ"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"การแจ้งเตือนนี้ถูกลดระดับโดยระบบ"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"การดำเนินการนี้ถูกต้องไหม"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"ขอบคุณที่แสดงความคิดเห็น"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ตกลง"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"ส่วนควบคุมการแจ้งเตือนของ <xliff:g id="APP_NAME">%1$s</xliff:g> เปิดอยู่"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"ส่วนควบคุมการแจ้งเตือนของ <xliff:g id="APP_NAME">%1$s</xliff:g> ปิดอยู่"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"อนุญาตการแจ้งเตือนจากช่องนี้"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"แก้ไขลำดับการตั้งค่า"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"หน้า <xliff:g id="ID_1">%1$d</xliff:g> จาก <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"หน้าจอล็อก"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"ขยาย"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"ย่อเล็กสุด"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"ปิด"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"การตั้งค่า"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"ลากลงเพื่อปิด"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"เมนู"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> ใช้การแสดงภาพซ้อนภาพ"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"หากคุณไม่ต้องการให้ <xliff:g id="NAME">%s</xliff:g> ใช้ฟีเจอร์นี้ ให้แตะเพื่อเปิดการตั้งค่าแล้วปิดฟีเจอร์"</string>
- <string name="pip_play" msgid="333995977693142810">"เล่น"</string>
- <string name="pip_pause" msgid="1139598607050555845">"หยุดชั่วคราว"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"ข้ามไปรายการถัดไป"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"ข้ามไปรายการก่อนหน้า"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"โทรศัพท์ปิดไปเพราะร้อนมาก"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"ขณะนี้โทรศัพท์ทำงานเป็นปกติ"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"ขณะนี้โทรศัพท์ทำงานเป็นปกติ\nแตะเพื่อดูข้อมูลเพิ่มเติม"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"โทรศัพท์ร้อนเกินไปจึงปิดเครื่องเพื่อให้เย็นลง ขณะนี้โทรศัพท์ทำงานเป็นปกติ\n\nโทรศัพท์อาจร้อนเกินไปหากคุณ\n • ใช้แอปที่ใช้ทรัพยากรมาก (เช่น เกม วิดีโอ หรือแอปการนำทาง)\n • ดาวน์โหลดหรืออัปโหลดไฟล์ขนาดใหญ่\n • ใช้โทรศัพท์ในอุณหภูมิที่สูง"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ดูขั้นตอนในการดูแลรักษา"</string>
<string name="high_temp_title" msgid="2218333576838496100">"โทรศัพท์เริ่มเครื่องร้อน"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"ฟีเจอร์บางอย่างจะใช้งานได้จำกัดขณะโทรศัพท์ลดอุณหภูมิลง"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"ฟีเจอร์บางอย่างจะใช้งานได้จำกัดขณะโทรศัพท์เย็นลง\nแตะเพื่อดูข้อมูลเพิ่มเติม"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"โทรศัพท์จะพยายามลดอุณหภูมิลงโดยอัตโนมัติ คุณยังสามารถใช้โทรศัพท์ได้ แต่โทรศัพท์อาจทำงานช้าลง\n\nโทรศัพท์จะทำงานตามปกติเมื่อเย็นลงแล้ว"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ดูขั้นตอนในการดูแลรักษา"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"ถอดปลั๊กที่ชาร์จ"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"พบปัญหาในการชาร์จอุปกรณ์นี้ ถอดปลั๊กอะแดปเตอร์ด้วยความระมัดระวังเพราะสายอาจร้อน"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ดูขั้นตอนในการดูแลรักษา"</string>
@@ -1078,8 +1042,9 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"แตะตัวควบคุมค้างไว้แล้วลากเพื่อจัดเรียงใหม่"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"นำตัวควบคุมทั้งหมดออกแล้ว"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ยังไม่ได้บันทึกการเปลี่ยนแปลง"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ดูแอปอื่นๆ"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"โหลดตัวควบคุมไม่ได้ ตรวจสอบแอป <xliff:g id="APP">%s</xliff:g> ให้แน่ใจว่าการตั้งค่าของแอปไม่เปลี่ยนแปลง"</string>
- <string name="controls_favorite_load_none" msgid="7687593026725357775">"ตัวควบคุมความเข้ากันได้ไม่พร้อมใช้งาน"</string>
+ <string name="controls_favorite_load_none" msgid="7687593026725357775">"ตัวควบคุมที่เข้ากันได้ไม่พร้อมใช้งาน"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"อื่นๆ"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"เพิ่มไปยังระบบควบคุมอุปกรณ์"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"เพิ่ม"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"ยืนยันการเปลี่ยนแปลงสำหรับ<xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"เลื่อนเพื่อดูเพิ่มเติม"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"กำลังโหลดคำแนะนำ"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"ปิดเซสชันสื่อนี้"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"สื่อ"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"ซ่อนเซสชันปัจจุบัน"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ซ่อน"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"เล่นต่อ"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"การตั้งค่า"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ไม่มีการใช้งาน โปรดตรวจสอบแอป"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"มีข้อผิดพลาด กำลังลองอีกครั้ง…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ไม่พบ"</string>
diff --git a/packages/SystemUI/res/values-th/strings_tv.xml b/packages/SystemUI/res/values-th/strings_tv.xml
index 14d458a5a4f2..783b1f47ca7a 100644
--- a/packages/SystemUI/res/values-th/strings_tv.xml
+++ b/packages/SystemUI/res/values-th/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"การแสดงภาพซ้อนภาพ"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(ไม่มีชื่อรายการ)"</string>
- <string name="pip_close" msgid="5775212044472849930">"ปิด PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"เต็มหน้าจอ"</string>
<string name="mic_active" msgid="5766614241012047024">"ไมโครโฟนเปิดใช้งานอยู่"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s เข้าถึงไมโครโฟนแล้ว"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 23bb7b6c76f4..20a34e1ded0d 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"I-tap ulit upang buksan"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Mag-swipe pataas para buksan"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Mag-swipe pataas para subukan ulit"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Pagmamay-ari ng iyong organisasyon ang device na ito"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Pagmamay-ari ng <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ang device na ito"</string>
<string name="phone_hint" msgid="6682125338461375925">"Mag-swipe mula sa icon para sa telepono"</string>
<string name="voice_hint" msgid="7476017460191291417">"Mag-swipe mula sa icon para sa voice assist"</string>
<string name="camera_hint" msgid="4519495795000658637">"Mag-swipe mula sa icon para sa camera"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Maaaring subaybayan ang profile"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Maaaring sinusubaybayan ang network"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Maaaring sinusubaybayan ang network"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Pagmamay-ari ng organisasyon mo ang device na ito at puwede nitong subaybayan ang trapiko sa network"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Pagmamay-ari ng <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ang device na ito at puwede nitong subaybayan ang trapiko sa network"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Pagmamay-ari ng iyong organisasyon ang device na ito at nakakonekta ito sa <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Pagmamay-ari ng <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ang device na ito at nakakonekta ito sa <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Pagmamay-ari ng iyong organisasyon ang device na ito"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Pagmamay-ari ng <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ang device na ito"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Pagmamay-ari ng iyong organisasyon ang device na ito nakakonekta ito sa mga VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Pagmamay-ari ng <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ang device na ito at nakakonekta ito sa mga VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Maaaring sumubaybay ang iyong organisasyon ng trapiko sa network sa profile sa trabaho mo"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Maaaring subaybayan ng <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ang trapiko sa network sa iyong profile sa trabaho"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Maaaring sinusubaybayan ang network"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Nakakonekta sa mga VPN ang device na ito"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Nakakonekta sa <xliff:g id="VPN_APP">%1$s</xliff:g> ang iyong profile sa trabaho"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Nakakonekta sa <xliff:g id="VPN_APP">%1$s</xliff:g> ang iyong personal na profile"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Nakakonekta sa <xliff:g id="VPN_APP">%1$s</xliff:g> ang device na ito"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Pamamahala ng device"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Pagsubaybay sa Profile"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Pagsubaybay sa network"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"I-disable ang VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Idiskonekta ang VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Tingnan ang Mga Patakaran"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Pagmamay-ari ng <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ang device na ito.\n\nMagagawa ng iyong IT admin na subaybayan at pamahalaan ang mga setting, pangkorporasyong access, mga app, data na nauugnay sa device mo, at ang impormasyon ng lokasyon ng iyong device.\n\nPara sa higit pang impormasyon, makipag-ugnayan sa iyong IT admin."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Pagmamay-ari ng iyong organisasyon ang device na ito.\n\nMagagawa ng iyong IT admin na subaybayan at pamahalaan ang mga setting, pangkorporasyong access, mga app, data na nauugnay sa device mo, at ang impormasyon ng lokasyon ng iyong device.\n\nPara sa higit pang impormasyon, makipag-ugnayan sa iyong IT admin."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Nag-install ang iyong organisasyon ng awtoridad sa certificate sa device na ito. Maaaring subaybayan o baguhin ang iyong ligtas na trapiko sa network."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Nag-install ang iyong organisasyon ng awtoridad sa certificate sa iyong profile sa trabaho. Maaaring subaybayan o baguhin ang iyong ligtas na trapiko sa network."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"May naka-install sa device na ito na isang awtoridad sa certificate. Maaaring subaybayan o baguhin ang iyong ligtas na trapiko sa network."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Naka-silent"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bubble"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Awtomatiko"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Walang tunog o pag-vibrate"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Walang tunog o pag-vibrate at lumalabas nang mas mababa sa seksyon ng pag-uusap"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Puwedeng mag-ring o mag-vibrate batay sa mga setting ng telepono"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Puwedeng mag-ring o mag-vibrate batay sa mga setting ng telepono. Mga pag-uusap mula sa <xliff:g id="APP_NAME">%1$s</xliff:g> bubble bilang default."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Pinapanatili ang iyong atensyon sa pamamagitan ng lumulutang na shortcut sa content na ito."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ipatukoy sa system kung dapat gumawa ng tunog o pag-vibrate ang notification na ito"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Makikita sa itaas ng seksyon ng pag-uusap, lumalabas bilang floating bubble, ipinapakita sa lock screen ang larawan sa profile"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Mga Setting"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priyoridad"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ipinapakita ang app na ito sa ibabaw ng iba pang app sa iyong screen at ginagamit nito ang mikropono at camera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Mga Setting"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Ginawang silent ng system ang notification na ito."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Na-promote ng system ang notification na ito."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Na-demote ng system ang notification na ito."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Tama ba ito?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Salamat sa iyong feedback!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Binuksan ang mga kontrol sa notification para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Isinara ang mga kontrol sa notification para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Payagan ang mga notification mula sa channel na ito"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"I-edit ang pagkakasunud-sunod ng mga setting."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> ng <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Palawakin"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"I-minimize"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Isara"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Mga Setting"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"I-drag pababa upang i-dismiss"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"Nasa picture-in-picture ang <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Kung ayaw mong magamit ni <xliff:g id="NAME">%s</xliff:g> ang feature na ito, i-tap upang buksan ang mga setting at i-off ito."</string>
- <string name="pip_play" msgid="333995977693142810">"I-play"</string>
- <string name="pip_pause" msgid="1139598607050555845">"I-pause"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Lumaktaw sa susunod"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Lumaktaw sa nakaraan"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Na-off ang telepono dahil sa init"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Maayos na ngayong gumagana ang iyong telepono"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Maayos na ngayong gumagana ang iyong telepono.\nMag-tap para sa higit pang impormasyon"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Napakainit ng telepono, kaya nag-off ito para lumamig. Maayos na itong gumagana.\n\nMaaaring lubos na uminit ang telepono kapag:\n • Gumamit ka ng resource-intensive na app (gaya ng app para sa gaming, video, o navigation)\n • Nag-download o nag-upload ka ng malaking file\n • Ginamit mo ito sa mainit na lugar"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Tingnan ang mga hakbang sa pangangalaga"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Umiinit ang telepono"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Limitado ang ilang feature habang nagku-cool down ang telepono"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Limitado ang ilang feature habang nagku-cool down ang telepono.\nMag-tap para sa higit pang impormasyon"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Awtomatikong susubukan ng iyong telepono na mag-cool down. Magagamit mo pa rin ang iyong telepono, ngunit maaaring mas mabagal ang paggana nito.\n\nKapag nakapag-cool down na ang iyong telepono, gagana na ito nang normal."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Tingnan ang mga hakbang sa pangangalaga"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Hugutin ang charger"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"May isyu sa pag-charge ng device na ito. Hugutin ang power adapter at mag-ingat dahil maaaring mainit ang cable."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Tingnan ang mga hakbang sa pangangalaga"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"I-hold at i-drag para baguhin ang pagkakaayos ng mga kontrol"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Inalis ang lahat ng kontrol"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Hindi na-save ang mga pagbabago"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Tingnan ang iba pang app"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Hindi ma-load ang mga kontrol. Tingnan ang app na <xliff:g id="APP">%s</xliff:g> para matiyak na hindi nabago ang mga setting ng app."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Hindi available ang mga compatible na kontrol"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Iba pa"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Kumpirmahin ang pagbabago para sa <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Mag-swipe para tumingin ng higit pa"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nilo-load ang rekomendasyon"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Isara ang session ng media na ito"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Itago ang kasalukuyang session."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Itago"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Ituloy"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Mga Setting"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Hindi aktibo, tingnan ang app"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Nagka-error, sinusubukan ulit…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hindi nahanap"</string>
diff --git a/packages/SystemUI/res/values-tl/strings_tv.xml b/packages/SystemUI/res/values-tl/strings_tv.xml
index 6e873f30edf2..bf46b8fe599a 100644
--- a/packages/SystemUI/res/values-tl/strings_tv.xml
+++ b/packages/SystemUI/res/values-tl/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Picture-in-Picture"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Walang pamagat na programa)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Isara ang PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Full screen"</string>
<string name="mic_active" msgid="5766614241012047024">"Aktibo ang Mikropono"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Na-access ng %1$s ang iyong mikropono"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index acf395356c94..4146f307ac18 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Açmak için tekrar dokunun"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Açmak için yukarı kaydırın"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Tekrar denemek için yukarı kaydırın"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Bu cihaz, kuruluşunuza ait"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> adlı kuruluşa ait"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefon için, simgeden hızlıca kaydırın"</string>
<string name="voice_hint" msgid="7476017460191291417">"Sesli yardım için, simgeden hızlıca kaydırın"</string>
<string name="camera_hint" msgid="4519495795000658637">"Kamera için, simgeden hızlıca kaydırın"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil izlenebilir"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Ağ etkinliği izlenebilir"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Ağ etkinliği izlenebilir"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Bu cihaz, kuruluşunuza ait olup ağ trafiği kuruluşunuz tarafından izlenebilir"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Bu cihaz, <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> adlı kuruluşa ait olup ağ trafiği bu kuruluş tarafından izlenebilir"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Bu cihaz, kuruluşunuza ait olup <xliff:g id="VPN_APP">%1$s</xliff:g> uygulamasına bağlı"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Bu cihaz, <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kuruluşuna ait olup <xliff:g id="VPN_APP">%2$s</xliff:g> uygulamasına bağlı"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Bu cihaz, kuruluşunuza ait"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> adlı kuruluşa ait"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Bu cihaz, kuruluşunuza ait olup VPN\'lere bağlı."</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kuruluşuna ait olup VPN\'lere bağlı"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Kuruluşunuz, iş profilinizdeki ağ trafiğini izleyebilir"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>, iş profilinizdeki ağ trafiğini izleyebilir"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Ağ trafiği izlenebilir"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Bu cihaz VPN\'lere bağlı"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"İş profiliniz <xliff:g id="VPN_APP">%1$s</xliff:g> uygulamasına bağlı"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Kişisel profiliniz <xliff:g id="VPN_APP">%1$s</xliff:g> uygulamasına bağlı"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Bu cihaz <xliff:g id="VPN_APP">%1$s</xliff:g> uygulamasına bağlı"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Cihaz yönetimi"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profil izleme"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Ağ izleme"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN\'yi devre dışı bırak"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN bağlantısını kes"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Politikaları Göster"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> adlı kuruluşa ait.\n\nBT yöneticiniz cihazınızın ayarlarını, şirket erişimini, uygulamaları, cihazınızla ilişkilendirilen verileri, cihazınızın konum bilgilerini izleyip yönetebilir.\n\nDaha fazla bilgi için BT yöneticinize başvurun."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Bu cihaz kuruluşunuza ait.\n\nBT yöneticiniz cihazın ayarlarını, şirket erişimini, uygulamaları, cihazınızla ilişkilendirilen verileri, cihazınızın konum bilgilerini izleyip yönetebilir.\n\nDaha fazla bilgi için BT yöneticinize başvurun."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Kuruluşunuz bu cihaza bir sertifika yetkilisi yükledi. Güvenli ağ trafiğiniz izlenebilir veya değiştirilebilir."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Kuruluşunuz iş profilinize bir sertifika yetkilisi yükledi. Güvenli ağ trafiğiniz izlenebilir veya değiştirilebilir."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Bu cihazda bir sertifika yetkilisi yüklü. Güvenli ağ trafiğiniz izlenebilir veya değiştirilebilir."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Sessiz"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Varsayılan"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Baloncuk"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatik"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sessiz veya titreşim yok"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sessizdir veya titreşim yoktur ve görüşme bölümünün altında görünür"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Telefon ayarlarına bağlı olarak zili çalabilir veya titreyebilir"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Telefon ayarlarına bağlı olarak zili çalabilir veya titreyebilir <xliff:g id="APP_NAME">%1$s</xliff:g> adlı uygulamadan görüşmeler varsayılan olarak baloncukla gösterilir."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Kayan kısayolla dikkatinizi bu içerik üzerinde tutar."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirimin ses çıkarması veya titreşmesi gerekip gerekmediğine sistem karar versin"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Görüşme bölümünün üstünde gösterilir, kayan baloncuk olarak görünür, kilit ekranında profil resmini görüntüler"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ayarlar"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Öncelik"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Bu uygulama, ekranınızdaki diğer uygulamaların üzerinde görüntüleniyor ve mikrofon ile kamerayı kullanıyor."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Ayarlar"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"Tamam"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Bu bildirim sistem tarafından sessize alındı."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Bu bildirimin düzeyi sistem tarafından yükseltildi."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Bu bildirimin düzeyi sistem tarafından düşürüldü."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Bu doğru muydu?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Geri bildiriminiz için teşekkürler!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"Tamam"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> için bildirim kontrolleri açıldı"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> için bildirim kontrolleri kapatıldı"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Bu kanaldan bildirimlere izin verir"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Ayarların sırasını düzenle."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Sayfa <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Kilit ekranı"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Genişlet"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Simge durumuna getir"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Kapat"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Ayarlar"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Kapatmak için aşağıya sürükleyin"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menü"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g>, pencere içinde pencere özelliğini kullanıyor"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> uygulamasının bu özelliği kullanmasını istemiyorsanız dokunarak ayarları açın ve söz konusu özelliği kapatın."</string>
- <string name="pip_play" msgid="333995977693142810">"Oynat"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Duraklat"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Sonrakine atla"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Öncekine atla"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefon ısındığından kapatıldı"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefonunuz şu anda normal bir şekilde çalışıyor"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefonunuz şu anda normal bir şekilde çalışıyor.\nDaha fazla bilgi için dokunun"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonunuz çok ısındığından soğuması için kapatıldı ve şu anda normal bir şekilde çalışıyor.\n\nTelefon şu koşullarda çok ısınabilir:\n • Yoğun kaynak gerektiren uygulamalar (oyun, video veya gezinme uygulamaları gibi) kullanma\n • Büyük dosyalar indirme veya yükleme\n • Telefonu sıcak yerlerde kullanma"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Bakımla ilgili adımlara bakın"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon ısınıyor"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Telefon soğurken bazı özellikler sınırlı olarak kullanılabilir"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Telefon soğurken bazı özellikler sınırlı olarak kullanılabilir.\nDaha fazla bilgi için dokunun"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonunuz otomatik olarak soğumaya çalışacak. Bu sırada telefonunuzu kullanmaya devam edebilirsiniz ancak uygulamalar daha yavaş çalışabilir.\n\nTelefonunuz soğuduktan sonra normal şekilde çalışacaktır."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Bakımla ilgili adımlara bakın"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Şarj cihazını çıkarın"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Bu cihaz şarj edilirken bir sorun oluştu. Güç adaptörünün fişini çekin. Kablo sıcak olabileceğinden fişi çekerken dikkatli olun."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Bakımla ilgili adımlara bakın"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Kontrolleri yeniden düzenlemek için basılı tutup sürükleyin"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Tüm kontroller kaldırıldı"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Değişiklikler kaydedilmedi"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Tüm uygulamaları göster"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontroller yüklenemedi. Uygulama ayarlarının değişmediğinden emin olmak için <xliff:g id="APP">%s</xliff:g> uygulamasını kontrol edin."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Uyumlu kontrol bulunamadı"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Diğer"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> için değişikliği onaylayın"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Diğer öğeleri görmek için hızlıca kaydırın"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Öneriler yükleniyor"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Bu medya oturumunu kapat"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Medya"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Mevcut oturumu gizle."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Gizle"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Devam ettir"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Devre dışı, uygulamaya bakın"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Hata, yeniden deneniyor…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Bulunamadı"</string>
diff --git a/packages/SystemUI/res/values-tr/strings_tv.xml b/packages/SystemUI/res/values-tr/strings_tv.xml
index be87e4cbcbda..30d1fc9cdb9f 100644
--- a/packages/SystemUI/res/values-tr/strings_tv.xml
+++ b/packages/SystemUI/res/values-tr/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Pencere İçinde Pencere"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Başlıksız program)"</string>
- <string name="pip_close" msgid="5775212044472849930">"PIP\'yi kapat"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Tam ekran"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon Etkin"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s mikrofonunuza erişti"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tvdpi/dimens.xml b/packages/SystemUI/res/values-tvdpi/dimens.xml
index 4d978aacc65f..5327cee7cae8 100644
--- a/packages/SystemUI/res/values-tvdpi/dimens.xml
+++ b/packages/SystemUI/res/values-tvdpi/dimens.xml
@@ -24,8 +24,4 @@
<fraction name="battery_subpixel_smoothing_right">10%</fraction>
<dimen name="battery_margin_bottom">1px</dimen>
-
- <!-- The dimensions to user for picture-in-picture action buttons. -->
- <dimen name="picture_in_picture_button_width">100dp</dimen>
- <dimen name="picture_in_picture_button_start_margin">-50dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index ac5bf29d99b6..11eb18cc48d2 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -458,10 +458,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Торкніться знову, щоб відкрити"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Проведіть пальцем угору, щоб відкрити"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Проведіть пальцем угору, щоб повторити спробу"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Цей пристрій належить вашій організації"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string>
<string name="phone_hint" msgid="6682125338461375925">"Телефон: проведіть пальцем від значка"</string>
<string name="voice_hint" msgid="7476017460191291417">"Голосові підказки: проведіть пальцем від значка"</string>
<string name="camera_hint" msgid="4519495795000658637">"Камера: проведіть пальцем від значка"</string>
@@ -529,33 +527,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Профіль може відстежуватись"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Дії в мережі можуть відстежуватися"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Мережа може відстежуватися"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Цей пристрій належить вашій організації. Її адміністратор може відстежувати мережевий трафік"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\". Її адміністратор може відстежувати мережевий трафік"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Цей пристрій належить вашій організації. Його підключено до додатка <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\". Його підключено до додатка <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Цей пристрій належить вашій організації"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\""</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Цей пристрій належить вашій організації. Його підключено до мереж VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\". Його підключено до мереж VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Адміністратор вашої організації може відстежувати мережевий трафік у вашому робочому профілі"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Адміністратор організації <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> мож відстежувати мережевий трафік у вашому робочому профілі"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Мережевий трафік може відстежуватися"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Цей пристрій підключено до мереж VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Ваш робочий профіль підключено до додатка <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Ваш особистий профіль підключено до додатка <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Цей пристрій підключено до додатка <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Керування пристроями"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Відстеження профілю"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Відстеження дій у мережі"</string>
@@ -565,10 +551,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Вимкнути VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Від’єднатися від мережі VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Переглянути правила"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\".\n\nIT-адміністратор може відстежувати й контролювати налаштування, корпоративний доступ, додатки, дані пристрою та інформацію про його місцезнаходження.\n\nЩоб дізнатися більше, зв\'яжіться з IT-адміністратором."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Цей пристрій належить вашій організації.\n\nІТ-адміністратор може відстежувати й контролювати налаштування, корпоративний доступ, додатки, дані пристрою та інформацію про його місцезнаходження.\n\nЩоб дізнатися більше, зв\'яжіться з ІТ-адміністратором."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Адміністратор організації встановив центр сертифікації на цьому пристрої. Захищений мережевий трафік може відстежуватися або змінюватися."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Адміністратор організації встановив центр сертифікації у вашому робочому профілі. Захищений мережевий трафік може відстежуватися або змінюватися."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На цьому пристрої встановлено центр сертифікації. Захищений мережевий трафік може відстежуватися або змінюватися."</string>
@@ -733,15 +717,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Без звуку"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"За умовчанням"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Спливаюче сповіщення"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звуку чи вібрації"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звуку чи вібрації, з\'являється нижче в розділі розмов"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Може дзвонити або вібрувати залежно від налаштувань телефона"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може дзвонити або вібрувати залежно від налаштувань телефона. Показує спливаючі розмови з додатка <xliff:g id="APP_NAME">%1$s</xliff:g> за умовчанням."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Привертає увагу до контенту плаваючим ярликом."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволити системі визначати, чи має сповіщення супроводжуватися звуком або вібрацією"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"З\'являється вгорі розділу розмов у спливаючому сповіщенні та показує зображення профілю на заблокованому екрані"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Налаштування"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Пріоритет"</string>
@@ -762,18 +744,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Цей додаток відображається поверх інших додатків на екрані та використовує мікрофон і камеру."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Налаштування"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Система заглушила це сповіщення."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Система підвищила статус цього сповіщення."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Система знизила статус цього сповіщення."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Правильно?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Дякуємо за відгук!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Елементи керування сповіщеннями для додатка <xliff:g id="APP_NAME">%1$s</xliff:g> відкрито"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Елементи керування сповіщеннями для додатка <xliff:g id="APP_NAME">%1$s</xliff:g> закрито"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Дозволити сповіщення з цього каналу"</string>
@@ -950,26 +926,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Змінити порядок налаштувань."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Сторінка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Заблокований екран"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Розгорнути"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Згорнути"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Закрити"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Налаштування"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Перетягніть униз, щоб закрити"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Меню"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"У додатку <xliff:g id="NAME">%s</xliff:g> є функція \"Картинка в картинці\""</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Щоб додаток <xliff:g id="NAME">%s</xliff:g> не використовував цю функцію, вимкніть її в налаштуваннях."</string>
- <string name="pip_play" msgid="333995977693142810">"Відтворити"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Призупинити"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Перейти далі"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Перейти назад"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Телефон перегрівся й вимкнувся"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Зараз телефон працює, як зазвичай"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Зараз телефон працює як зазвичай.\nНатисніть, щоб дізнатися більше"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Телефон перегрівся, тому вимкнувся, щоб охолонути. Зараз він працює, як зазвичай.\n\nТелефон перегрівається, якщо ви:\n • використовуєте ресурсомісткі додатки (ігри, відео, навігація)\n • завантажуєте великі файли на телефон або з нього\n • використовуєте телефон за високої температури"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Переглянути запобіжні заходи"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Телефон нагрівається"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Під час охолодження деякі функції обмежуються"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Під час охолодження деякі функції обмежуються.\nНатисніть, щоб дізнатися більше"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ваш телефон охолоджуватиметься автоматично. Ви можете далі користуватися телефоном, але він може працювати повільніше.\n\nКоли телефон охолоне, він працюватиме належним чином."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Переглянути запобіжні заходи"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Відключіть зарядний пристрій"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Виникла проблема із заряджанням пристрою. Відключіть адаптер живлення, однак будьте обережні, оскільки кабель може бути гарячим."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Переглянути застереження"</string>
@@ -1090,6 +1054,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Щоб змінити порядок елементів керування, перетягуйте їх"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Усі елементи керування вилучено"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Зміни не збережено"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Переглянути інші додатки"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Не вдалося завантажити елементи керування. Перевірте в додатку <xliff:g id="APP">%s</xliff:g>, чи його налаштування не змінились."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Сумісні елементи керування недоступні"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Інше"</string>
@@ -1107,8 +1072,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g>: підтвердьте зміну"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Гортайте, щоб переглянути інші"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Завантаження рекомендацій"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Закрити цей сеанс медіа"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Медіа"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Приховати поточний сеанс."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Приховати"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Відновити"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Налаштування"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, перейдіть у додаток"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Помилка. Повторна спроба…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не знайдено"</string>
diff --git a/packages/SystemUI/res/values-uk/strings_tv.xml b/packages/SystemUI/res/values-uk/strings_tv.xml
index a11e553fea62..e3872cc35963 100644
--- a/packages/SystemUI/res/values-uk/strings_tv.xml
+++ b/packages/SystemUI/res/values-uk/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Картинка в картинці"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Програма без назви)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Закрити PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"На весь екран"</string>
<string name="mic_active" msgid="5766614241012047024">"Мікрофон активовано"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"Додаток %1$s отримав доступ до вашого мікрофона"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index a65cab4ce132..d7645e9bcb5b 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"کھولنے کیلئے دوبارہ تھپتھپائیں"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"کھولنے کے لیے اوپر سوائپ کريں"</string>
<string name="keyguard_retry" msgid="886802522584053523">"دوبارہ کوشش کرنے کے لیے اوپر سوائپ کريں"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"یہ آلہ آپ کی تنظیم کا ہے"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"یہ آلہ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> کا ہے"</string>
<string name="phone_hint" msgid="6682125338461375925">"فون کیلئے آئیکن سے سوائپ کریں"</string>
<string name="voice_hint" msgid="7476017460191291417">"صوتی معاون کیلئے آئیکن سے سوائپ کریں"</string>
<string name="camera_hint" msgid="4519495795000658637">"کیمرہ کیلئے آئیکن سے سوائپ کریں"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"پروفائل کو مانیٹر کیا جا سکتا ہے"</string>
<string name="vpn_footer" msgid="3457155078010607471">"نیٹ ورک کو مانیٹر کیا جا سکتا ہے"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"نیٹ ورک کو شاید مانیٹر کیا جائے"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"آپ کی تنظیم اس آلے کی مالک ہے اور نیٹ ورک ٹریفک کی نگرانی کر سکتی ہے"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> اس آلے کی مالک ہے اور نیٹ ورک ٹریفک کی نگرانی کر سکتی ہے"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"یہ آلہ آپ کی تنظیم کا ہے اور <xliff:g id="VPN_APP">%1$s</xliff:g> سے منسلک ہے"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"یہ آلہ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> کا ہے اور <xliff:g id="VPN_APP">%2$s</xliff:g> سے منسلک ہے"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"یہ آلہ آپ کی تنظیم کا ہے"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"یہ آلہ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> کا ہے"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"‏یہ آلہ آپ کی تنظیم کا ہے اور VPNs سے منسلک ہے"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"‏یہ آلہ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> کا ہے اور VPNs سے منسلک ہے"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"آپ کی تنظیم آپ کے دفتری پروفائل میں نیٹ ورک ٹریفک مانیٹر کر سکتی ہے"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> آپ کے دفتری پروفائل میں نیٹ ورک ٹریفک مانیٹر کر سکتی ہے"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"نیٹ ورک کو مانیٹر کیا جا سکتا ہے"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"‏یہ آلہ VPNs سے منسلک ہے"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"آپ کی دفتری پروفائل <xliff:g id="VPN_APP">%1$s</xliff:g> سے منسلک ہے"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"آپ کی ذاتی پروفائل <xliff:g id="VPN_APP">%1$s</xliff:g> سے منسلک ہے"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"یہ آلہ <xliff:g id="VPN_APP">%1$s</xliff:g> سے منسلک ہے"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"آلے کا نظم و نسق"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"پروفائل کو مانیٹر کرنا"</string>
<string name="monitoring_title" msgid="4063890083735924568">"نیٹ ورک کو مانیٹر کرنا"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"‏VPN کو غیر فعال کریں"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"‏VPN کو غیر منسلک کریں"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"پالیسیاں دیکھیں"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"‏یہ آلہ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> کا ہے۔\n\nآپ کا IT منتظم ترتیبات، کارپوریٹ رسائی، ایپس، آپ کے آلہ سے وابستہ ڈیٹا اور آپ کے آلہ کے مقام کی معلومات کی نگرانی اور ان کا نظم کر سکتا ہے۔\n\nمزید معلومات کے لیے اپنے IT منتظم سے رابطہ کریں۔"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"‏یہ آلہ آپ کی تنظیم کا ہے۔\n\nآپ کا IT منتظم ترتیبات، کارپوریٹ رسائی، ایپس، آپ کے آلہ سے وابستہ ڈیٹا اور آپ کے آلہ کے مقام کی معلومات کی نگرانی اور ان کا نظم کر سکتا ہے۔\n\nمزید معلومات کے لیے اپنے IT منتظم سے رابطہ کریں۔"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"آپ کی تنظیم نے اس آلے پر ایک سرٹیفکیٹ کی اتھارٹی کو انسٹال کیا ہے۔ آپ کا محفوظ نیٹ ورک ٹریفک مانیٹر ہو سکتا ہے یا اس میں ترمیم کی جا سکتی ہے۔"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"آپ کی تنظیم نے آپ کے دفتری پروفائل میں ایک سرٹیفکیٹ کی اتھارٹی کو انسٹال کیا ہے۔ آپ کا محفوظ نیٹ ورک ٹریفک مانیٹر ہو سکتا ہے یا اس میں ترمیم کی جا سکتی ہے۔"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ایک سرٹیفکیٹ کی اتھارٹی اس آلہ پر انسٹال ہے۔ آپ کا محفوظ نیٹ ورک ٹریفک مانیٹر ہو سکتا ہے یا اس میں ترمیم کی جا سکتی ہے۔"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"خاموش"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ڈیفالٹ"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"بلبلہ"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"کوئی آواز یا وائبریشن نہیں"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"کوئی آواز یا وائبریشن نہیں اور گفتگو کے سیکشن میں نیچے ظاہر ہوتا ہے"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"آپ کے آلہ کی ترتیبات کے مطابق وائبریٹ یا گھنٹی بج سکتی ہے"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"فون کی ترتیبات کے مطابق وائبریٹ یا گھنٹی بج سکتی ہے۔ بذریعہ ڈیفالٹ <xliff:g id="APP_NAME">%1$s</xliff:g> بلبلہ سے گفتگوئیں۔"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"اس مواد کے فلوٹنگ شارٹ کٹ کے ساتھ آپ کی توجہ دیتی ہے۔"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"سسٹم کو اس بات کا تعین کرنے دیں کہ آیا اس اطلاع کی آواز ہو یا وائبریٹ ہونا چاہیے"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"گفتگو کے سیکشن کے اوپری حصے پر دکھاتا ہے، تیرتے بلبلے کی طرح ظاہر ہوتا ہے، لاک اسکرین پر پروفائل تصویر دکھاتا ہے"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ترتیبات"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ترجیح"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"یہ ایپ آپ کی اسکرین پر دیگر ایپس پر ڈسپلے کر رہی ہے اور مائیکروفون اور کیمرے کا استعمال کر رہی ہے۔"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ترتیبات"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ٹھیک ہے"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"سسٹم کے ذریعے اس اطلاع کو خاموش کیا گيا۔"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"سسٹم کے ذریعے اس اطلاع کو پروموٹ کیا گيا۔"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"سسٹم کے ذریعے اس اطلاع کو ڈیموٹ کیا گيا۔"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"کیا یہ درست تھا؟"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"آپ کے تاثرات کا شکریہ!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"ٹھیک ہے"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> کیلئے اطلاعی کنٹرولز کھلے ہیں"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> کیلئے اطلاعی کنٹرولز بند کر دئے گئے ہیں"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"اس چینل سے اطلاعات کی اجازت دیں"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ترتیبات کی ترتیب میں ترمیم کریں۔"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"صفحہ <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"مقفل اسکرین"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"پھیلائیں"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"چھوٹی کریں"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"بند کریں"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"ترتیبات"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"برخاست کرنے کیلئے نیچے گھسیٹیں"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"مینو"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> تصویر میں تصویر میں ہے"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"اگر آپ نہیں چاہتے ہیں کہ <xliff:g id="NAME">%s</xliff:g> اس خصوصیت کا استعمال کرے تو ترتیبات کھولنے کے لیے تھپتھپا کر اسے آف کرے۔"</string>
- <string name="pip_play" msgid="333995977693142810">"چلائیں"</string>
- <string name="pip_pause" msgid="1139598607050555845">"موقوف کریں"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"نظرانداز کرکے اگلے پر جائیں"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"نظرانداز کرکے پچھلے پر جائیں"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"حرارت کی وجہ سے فون آف ہو گیا"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"آپ کا فون اب حسب معمول کام کر رہا ہے"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"آپ کا فون اب حسب معمول چل رہا ہے۔\nمزید معلومات کیلئے تھپتھپائیں"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"آپ کا فون کافی گرم ہو گيا تھا، اس لئے سرد ہونے کیلئے یہ آف ہو گیا۔ اب آپ کا فون حسب معمول کام کر رہا ہے۔\n\nمندرجہ ذیل چیزیں کرنے پر آپ کا فون کافی گرم ہو سکتا ہے:\n • ماخذ کا زیادہ استعمال کرنے والی ایپس (جیسے کہ گیمنگ، ویڈیو، یا نیویگیشن ایپس) کا استعمال کرنا\n • بڑی فائلز ڈاؤن لوڈ یا اپ لوڈ کرنا\n • اعلی درجہ حرارت میں فون کا استعمال کرنا"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"نگہداشت کے اقدامات ملاحظہ کریں"</string>
<string name="high_temp_title" msgid="2218333576838496100">"فون گرم ہو رہا ہے"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"فون کے ٹھنڈے ہو جانے تک کچھ خصوصیات محدود ہیں"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"فون کے ٹھنڈے ہو جانے تک کچھ خصوصیات محدود ہیں۔\nمزید معلومات کیلئے تھپتھپائیں"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"آپ کا فون خودکار طور پر ٹھنڈا ہونے کی کوشش کرے گا۔ آپ ابھی بھی اپنا فون استعمال کر سکتے ہیں، مگر ہو سکتا ہے یہ سست چلے۔\n\nایک بار آپ کا فون ٹھنڈا ہوجائے تو یہ معمول کے مطابق چلے گا۔"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"نگہداشت کے اقدامات ملاحظہ کریں"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"چارجر ان پلگ کریں"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"اس آلہ کو چارج کرنے میں ایک مسئلہ ہے۔ پاور ایڈاپٹر کو ان پلگ کریں اور دھیان دیں کیونکہ تار گرم ہو سکتا ہے۔"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"نگہداشت کے اقدامات ملاحظہ کریں"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"کنٹرولز کو دوبارہ ترتیب دینے کے ليے پکڑیں اور گھسیٹیں"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"سبھی کنٹرولز ہٹا دیے گئے"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"تبدیلیاں محفوظ نہیں ہوئیں"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"دیگر ایپس دیکھیں"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"کنٹرولز کو لوڈ نہیں کیا جا سکا۔ یہ یقینی بنانے کے لیے <xliff:g id="APP">%s</xliff:g> ایپ کو چیک کریں کہ ایپ کی ترتیبات تبدیل نہیں ہوئی ہیں۔"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"موافق کنٹرولز دستیاب نہیں ہیں"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"دیگر"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> کی تبدیلی کی توثیق کریں"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"مزید دیکھنے کیلئے سوائپ کریں"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"تجاویز لوڈ ہو رہی ہیں"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"اس میڈیا سیشن کو بند کریں"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"میڈیا"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"موجودہ سیشن چھپائیں۔"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"چھپائیں"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"غیر فعال، ایپ چیک کریں"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"خرابی، دوبارہ کوشش کی جا رہی ہے…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"نہیں ملا"</string>
diff --git a/packages/SystemUI/res/values-ur/strings_tv.xml b/packages/SystemUI/res/values-ur/strings_tv.xml
index 8f5ad0d63cf1..cb50fafaf931 100644
--- a/packages/SystemUI/res/values-ur/strings_tv.xml
+++ b/packages/SystemUI/res/values-ur/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"تصویر میں تصویر"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(بلا عنوان پروگرام)"</string>
- <string name="pip_close" msgid="5775212044472849930">"‏PIP بند کریں"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"فُل اسکرین"</string>
<string name="mic_active" msgid="5766614241012047024">"مائیکروفون فعال ہے"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"‏%1$s نے آپ کے مائیکروفون تک رسائی حاصل کی ہے"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 765179e076d6..1fc4f9bea270 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ochish uchun yana bosing"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ochish uchun tepaga suring"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Qayta urinish uchun tepaga suring"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Bu qurilma tashkilotingizga tegishli"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Bu qurilma <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> tashkilotiga tegishli"</string>
<string name="phone_hint" msgid="6682125338461375925">"Telefonni ochish uchun suring"</string>
<string name="voice_hint" msgid="7476017460191291417">"Ovozli yordam: belgidan boshlab suring"</string>
<string name="camera_hint" msgid="4519495795000658637">"Kamerani ochish uchun suring"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Profil kuzatilishi mumkin"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Tarmoqni kuzatish mumkin"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Tarmoq kuzatilishi mumkin"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Bu qurilma tashkilotingizga tegishli va tarmoq trafigi tashkilotingiz tomonidan kuzatilishi mumkin"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"Bu qurilma <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tashkilotiga tegishli va tarmoq trafigi tashkilot tomonidan kuzatilishi mumkin"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Bu qurilma tashkilotingizga tegishli va <xliff:g id="VPN_APP">%1$s</xliff:g> tarmogʻiga ulangan"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Bu qurilma <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tashkilotiga tegishli va <xliff:g id="VPN_APP">%2$s</xliff:g> tarmogʻiga ulangan"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Bu qurilma tashkilotingizga tegishli"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Bu qurilma <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tashkilotiga tegishli"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Bu qurilma tashkilotingizga tegishli va VPN tarmoqlarga ulangan"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Bu qurilma <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tashkilotiga tegishli va VPN tarmoqlarga ulangan"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Tashkilotingiz ishchi profilingizda tarmoq trafigini nazorat qilishi mumkin"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ishchi profilingizda tarmoq trafigini nazorat qilishi mumkin"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Tarmoq kuzatilishi mumkin"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Bu qurilma VPN tarmoqlarga ulangan"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Ish profilingiz <xliff:g id="VPN_APP">%1$s</xliff:g> tarmogʻiga ulangan"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Shaxsiy profilingiz <xliff:g id="VPN_APP">%1$s</xliff:g> tarmogʻiga ulangan"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Bu qurilma <xliff:g id="VPN_APP">%1$s</xliff:g> tarmogʻiga ulangan"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Qurilmalar boshqaruvi"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Profilni kuzatish"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Tarmoqlarni kuzatish"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"VPN tarmog‘ini o‘chirish"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"VPN ulanishini uzish"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Siyosatlarni ko‘rish"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Bu qurilma <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> tashkilotiga tegishli.\n\nAT administratori sozlamalar, korporativ ruxsat, ilovalar, qurilmaning geolokatsiyasi va unga aloqador axborotlarni kuzatishi va boshqarishi mumkin.\n\nBatafsil axborot uchun AT administratoriga murojaat qiling."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Bu qurilma tashkilotingizga tegishli.\n\nAT administratori sozlamalar, korporativ ruxsat, ilovalar, qurilmaning geolokatsiyasi va unga aloqador axborotlarni kuzatishi va boshqarishi mumkin.\n\nBatafsil axborot uchun AT administratoriga murojaat qiling."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Tashkilotingiz bu qurilmada CA sertifikatini o‘rnatdi. U himoyalangan tarmoq trafigini nazorat qilishi va o‘zgartirishi mumkin."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tashkilotingiz ishchi profilingizga CA sertifikatini o‘rnatdi. U himoyalangan tarmoq trafigini nazorat qilishi va o‘zgartirishi mumkin."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Qurilmada CA sertifikati o‘rnatilgan. U himoyalangan tarmoq trafigini nazorat qilishi va o‘zgartirishi mumkin."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Tovushsiz"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standart"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Pufaklar"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Tovush yoki tebranishsiz"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Tovush yoki tebranishsiz hamda suhbatlar ruknining pastida chiqadi"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Telefon sozlamalari asosida jiringlashi yoki tebranishi mumkin"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Telefon sozlamalari asosida jiringlashi yoki tebranishi mumkin. <xliff:g id="APP_NAME">%1$s</xliff:g> suhbatlari standart holatda bulutcha shaklida chiqadi."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Bu kontentni ochuvchi erkin yorliq diqqatingizda boʻladi."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirishnoma jiringlashi yoki tebranishini hal qilsin"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Suhbatlar ruknining tepasida qalqib chiquvchi bulutcha shaklida chiqadi, ekran qulfida profil rasmi chiqadi"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Sozlamalar"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Muhim"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Bu ilova ekranda boshqa ilovalar ustidan ochilgan hamda mikrofon va kameradan foydalanmoqda."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Sozlamalar"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Bu bildirishnoma tizim tomonidan ovozsiz qilindi."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Bu bildirishnoma tizim tomonidan balandlatildi."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Bu bildirishnoma tizim tomonidan pasaytirildi."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Xatolar boʻlmadimi?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Fikr-mulohazangiz uchun tashakkur!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun bildirishnoma sozlamalari ochildi"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun bildirishnoma sozlamalari yopildi"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Bu kanaldan keladigan bildirishnomalarga ruxsat berish"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Sozlamalar tartibini o‘zgartirish."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>-sahifa, jami: <xliff:g id="ID_2">%2$d</xliff:g> ta sahifa"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ekran qulfi"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Yoyish"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Yig‘ish"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Yopish"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Sozlamalar"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Yopish uchun pastga torting"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menyu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> tasvir ustida tasvir rejimida"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"<xliff:g id="NAME">%s</xliff:g> ilovasi uchun bu funksiyani sozlamalar orqali faolsizlantirish mumkin."</string>
- <string name="pip_play" msgid="333995977693142810">"Ijro"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Pauza"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Keyingisiga o‘tish"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Avvalgisiga qaytish"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Qizigani uchun o‘chirildi"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Telefoningiz hozir normal holatda ishlayapti"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Endi telefoningiz normal holatda ishlayapti.\nBatafsil axborot uchun bosing"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon qizib ketganligi sababli sovitish uchun o‘chirib qo‘yilgan. Endi telefoningiz normal holatda ishlayapti.\n\nTelefon bu hollarda qizib ketishi mumkin:\n • Resurstalab ilovalar ishlatilganda (masalan, o‘yin, video yoki navigatsiya ilovalari)\n • Katta faylni yuklab olishda yoki yuklashda\n • Telefondan yuqori haroratda foydalanganda"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Batafsil axborot"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefon qizib ketdi"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Telefon sovish paytida ayrim funksiyalar ishlamasligi mumkin"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Telefon sovib qolganda ayrim funksiyalari ishlamasligi mumkin.\nBatafsil axborot uchun bosing"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon avtomatik ravishda o‘zini sovitadi. Telefoningizdan foydalanishda davom etishingiz mumkin, lekin u sekinroq ishlashi mumkin.\n\nTelefon sovishi bilan normal holatda ishlashni boshlaydi."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Batafsil axborot"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Quvvatlash moslamasini uzing"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Bu qurilmani quvvatlashda muammo bor. Quvvat adapteri va kabelni tarmoqdan uzing, ular qizib ketgan boʻlishi mumkin."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Batafsil axborot"</string>
@@ -1078,8 +1042,9 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Boshqaruv elementlarini qayta tartiblash uchun ushlab torting"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Barcha boshqaruv elementlari olib tashlandi"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Oʻzgarishlar saqlanmadi"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Boshqa ilovalar"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Boshqaruvlar yuklanmadi. <xliff:g id="APP">%s</xliff:g> ilovasining sozlamalari oʻzgarmaganini tekshiring."</string>
- <string name="controls_favorite_load_none" msgid="7687593026725357775">"Mos boshqaruvlar mavjud emas"</string>
+ <string name="controls_favorite_load_none" msgid="7687593026725357775">"Mos boshqaruv elementlari mavjud emas"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Boshqa"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"Qurilma boshqaruv elementlariga kiritish"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"Kiritish"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g> uchun oʻzgarishlarni tasdiqlang"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Batafsil axborot olish uchun suring"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tavsiyalar yuklanmoqda"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Bu media seansni yopish"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Joriy seans berkitilsin."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Berkitish"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Davom etish"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Sozlamalar"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Nofaol. Ilovani tekshiring"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Xato, qayta urinilmoqda…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Topilmadi"</string>
diff --git a/packages/SystemUI/res/values-uz/strings_tv.xml b/packages/SystemUI/res/values-uz/strings_tv.xml
index 6fb5d732bfbd..09bc51ee293c 100644
--- a/packages/SystemUI/res/values-uz/strings_tv.xml
+++ b/packages/SystemUI/res/values-uz/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Tasvir ustida tasvir"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Nomsiz)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Kadr ichida kadr – chiqish"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Butun ekran"</string>
<string name="mic_active" msgid="5766614241012047024">"Mikrofon faol"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s mikrofondan foydalandi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 0324e1b7ede5..8df5ee530210 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Nhấn lại để mở"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Vuốt lên để mở"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Vuốt lên để thử lại"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Thiết bị này thuộc về tổ chức của bạn"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Thiết bị này thuộc về <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Vuốt từ biểu tượng để mở điện thoại"</string>
<string name="voice_hint" msgid="7476017460191291417">"Vuốt từ biểu tượng để mở trợ lý thoại"</string>
<string name="camera_hint" msgid="4519495795000658637">"Vuốt từ biểu tượng để mở máy ảnh"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Hồ sơ có thể được giám sát"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Mạng có thể được giám sát"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Mạng có thể được giám sát"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Tổ chức của bạn sở hữu thiết bị này và có thể giám sát lưu lượng truy cập mạng"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> sở hữu thiết bị này và có thể giám sát lưu lượng truy cập mạng"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Thiết bị này thuộc về tổ chức của bạn và đã kết nối với <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Thiết bị này thuộc về <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> và đã kết nối với <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Thiết bị này thuộc về tổ chức của bạn"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Thiết bị này thuộc về <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Thiết bị này thuộc về tổ chức của bạn và đã kết nối với VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Thiết bị này thuộc về <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> và đã kết nối với VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Tổ chức của bạn có thể giám sát lưu lượng truy cập mạng trong hồ sơ công việc của bạn"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> có thể giám sát lưu lượng truy cập mạng trong hồ sơ công việc của bạn"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Mạng có thể được giám sát"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Thiết bị này đã kết nối với VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Hồ sơ công việc của bạn đã kết nối với <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Hồ sơ cá nhân của bạn đã kết nối với <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Thiết bị này đã kết nối với <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Quản lý thiết bị"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Giám sát hồ sơ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Giám sát mạng"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Tắt VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Ngắt kết nối VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Xem chính sách"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Thiết bị này thuộc về <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nQuản trị viên CNTT có thể giám sát và quản lý các tùy chọn cài đặt, quyền truy cập vào dữ liệu công ty, ứng dụng, dữ liệu liên kết với thiết bị và thông tin vị trí thiết bị của bạn.\n\nĐể biết thêm thông tin, hãy liên hệ với quản trị viên CNTT của bạn."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Thiết bị này thuộc về tổ chức của bạn.\n\nQuản trị viên CNTT có thể giám sát và quản lý các tùy chọn cài đặt, quyền truy cập vào dữ liệu công ty, ứng dụng, dữ liệu liên kết với thiết bị và thông tin vị trí thiết bị của bạn.\n\nĐể biết thêm thông tin, hãy liên hệ với quản trị viên CNTT của bạn."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Tổ chức của bạn đã cài đặt một tổ chức phát hành chứng chỉ trên thiết bị này. Lưu lượng truy cập mạng bảo mật của bạn có thể được giám sát hoặc sửa đổi."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tổ chức của bạn đã cài đặt một tổ chức phát hành chứng chỉ trong hồ cơ công việc của bạn. Lưu lượng truy cập mạng bảo mật của bạn có thể được giám sát hoặc sửa đổi."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Một tổ chức phát hành chứng chỉ được cài đặt trên thiết bị này. Lưu lượng truy cập mạng bảo mật của bạn có thể được giám sát hoặc sửa đổi."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Im lặng"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Mặc định"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Bong bóng"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Tự động"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Không phát âm thanh hoặc rung"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Không phát âm thanh hoặc rung và xuất hiện phía dưới trong phần cuộc trò chuyện"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Có thể đổ chuông hoặc rung tùy theo chế độ cài đặt trên điện thoại"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Có thể đổ chuông hoặc rung tùy theo chế độ cài đặt trên điện thoại. Theo mặc định, các cuộc trò chuyện từ <xliff:g id="APP_NAME">%1$s</xliff:g> được phép hiển thị dưới dạng bong bóng."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Luôn chú ý vào nội dung này bằng phím tắt nổi."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Cho phép hệ thống quyết định xem thông báo này phát âm thanh hay rung"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Hiển thị cuộc trò chuyện ở đầu phần cuộc trò chuyện và dưới dạng bong bóng nổi, hiển thị ảnh hồ sơ trên màn hình khóa"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Cài đặt"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Mức độ ưu tiên"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Ứng dụng này đang hiển thị chồng lên các ứng dụng khác trên màn hình, đồng thời đang sử dụng micrô và máy ảnh."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Cài đặt"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"OK"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Hệ thống đã tắt tiếng thông báo này."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Hệ thống đã nâng mức ưu tiên của thông báo này."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Hệ thống đã giảm mức ưu tiên của thông báo này."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Thông tin này có chính xác không?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Cảm ơn bạn đã phản hồi!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Đã mở điều khiển thông báo đối với <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Đã đóng điều khiển thông báo đối với <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Cho phép thông báo từ kênh này"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Chỉnh sửa thứ tự cài đặt."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Trang <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Màn hình khóa"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Mở rộng"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Thu nhỏ"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Đóng"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Cài đặt"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Kéo xuống để loại bỏ"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Menu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g> đang ở chế độ ảnh trong ảnh"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Nếu bạn không muốn <xliff:g id="NAME">%s</xliff:g> sử dụng tính năng này, hãy nhấn để mở cài đặt và tắt tính năng này."</string>
- <string name="pip_play" msgid="333995977693142810">"Phát"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Tạm dừng"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Chuyển tới mục tiếp theo"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Chuyển về mục trước"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Điện thoại đã tắt do nhiệt"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Điện thoại của bạn hiện đang chạy bình thường"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Điện thoại của bạn đang chạy bình thường.\nHãy nhấn để biết thêm thông tin"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Do quá nóng nên điện thoại đã tắt để hạ nhiệt. Hiện điện thoại của bạn đang chạy bình thường.\n\nĐiện thoại có thể bị quá nóng nếu bạn:\n • Dùng các ứng dụng tốn nhiều tài nguyên (như ứng dụng trò chơi, video hoặc điều hướng)\n • Tải xuống hoặc tải lên tệp có dung lượng lớn\n • Dùng điện thoại ở nhiệt độ cao"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Xem các bước chăm sóc"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Điện thoại đang nóng lên"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Một số tính năng bị hạn chế trong khi điện thoại nguội dần"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Một số tính năng bị hạn chế trong khi điện thoại nguội dần.\nHãy nhấn để biết thêm thông tin"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Điện thoại của bạn sẽ tự động nguội dần. Bạn vẫn có thể sử dụng điện thoại, nhưng điện thoại có thể chạy chậm hơn. \n\nSau khi đã nguội, điện thoại sẽ chạy bình thường."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Xem các bước chăm sóc"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Rút phích cắm bộ sạc"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Đã xảy ra sự cố khi sạc thiết bị này. Hãy rút phích cắm bộ chuyển đổi điện và cẩn thận vì dây cáp có thể nóng."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Xem các bước chăm sóc"</string>
@@ -1037,7 +1001,7 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Chuyển tới dưới cùng bên trái"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Chuyển tới dưới cùng bên phải"</string>
<string name="bubble_dismiss_text" msgid="1314082410868930066">"Đóng bong bóng"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Dừng trò chuyện bằng bong bóng"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Dừng sử dụng bong bóng cho cuộc trò chuyện"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Trò chuyện bằng bong bóng trò chuyện"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Các cuộc trò chuyện mới sẽ xuất hiện dưới dạng biểu tượng nổi hoặc bong bóng trò chuyện. Nhấn để mở bong bóng trò chuyện. Kéo để di chuyển bong bóng trò chuyện."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Kiểm soát bong bóng bất cứ lúc nào"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Giữ và kéo để sắp xếp lại các tùy chọn điều khiển"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Đã xóa tất cả tùy chọn điều khiển"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Chưa lưu các thay đổi"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Xem ứng dụng khác"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Không tải được các chức năng điều khiển. Hãy kiểm tra ứng dụng <xliff:g id="APP">%s</xliff:g> để đảm bảo rằng thông tin cài đặt của ứng dụng chưa thay đổi."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Không có các chức năng điều khiển tương thích"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Khác"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Xác nhận thay đổi <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Vuốt để xem thêm"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Đang tải các đề xuất"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Đóng phiên đa phương tiện này"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Nội dung nghe nhìn"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Ẩn phiên hiện tại."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ẩn"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Tiếp tục"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Cài đặt"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Không hoạt động, hãy kiểm tra ứng dụng"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Lỗi, đang thử lại…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Không tìm thấy"</string>
diff --git a/packages/SystemUI/res/values-vi/strings_tv.xml b/packages/SystemUI/res/values-vi/strings_tv.xml
index 03b53561d06e..f298407bcaf3 100644
--- a/packages/SystemUI/res/values-vi/strings_tv.xml
+++ b/packages/SystemUI/res/values-vi/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Ảnh trong ảnh"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Không có chương trình tiêu đề)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Đóng PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Toàn màn hình"</string>
<string name="mic_active" msgid="5766614241012047024">"Micrô đang hoạt động"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s đang dùng micrô của bạn"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 633faa18e804..c51982cddac3 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"再次点按即可打开"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑动即可打开"</string>
<string name="keyguard_retry" msgid="886802522584053523">"向上滑动即可重试"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"此设备归贵单位所有"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"此设备归<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>所有"</string>
<string name="phone_hint" msgid="6682125338461375925">"滑动图标即可拨打电话"</string>
<string name="voice_hint" msgid="7476017460191291417">"滑动图标即可打开语音助理"</string>
<string name="camera_hint" msgid="4519495795000658637">"滑动图标即可打开相机"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"资料可能会受到监控"</string>
<string name="vpn_footer" msgid="3457155078010607471">"网络可能会受到监控"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"网络可能会受到监控"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"贵单位拥有此设备,且可能会监控网络流量"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>拥有此设备,且可能会监控网络流量"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"此设备归贵单位所有,且已连接到“<xliff:g id="VPN_APP">%1$s</xliff:g>”"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"此设备归<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>所有,且已连接到“<xliff:g id="VPN_APP">%2$s</xliff:g>”"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"此设备归贵单位所有"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"此设备归<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>所有"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"此设备归贵单位所有,且已连接到多个 VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"此设备归<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>所有,且已连接到多个 VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"您所在的单位可能会监控您工作资料中的网络流量"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"“<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>”可能会监控您工作资料中的网络流量"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"网络可能会受到监控"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"此设备已连接到多个 VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"您的工作资料已连接到“<xliff:g id="VPN_APP">%1$s</xliff:g>”"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"您的个人资料已连接到“<xliff:g id="VPN_APP">%1$s</xliff:g>”"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"此设备已连接到“<xliff:g id="VPN_APP">%1$s</xliff:g>”"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"设备管理"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"资料监控"</string>
<string name="monitoring_title" msgid="4063890083735924568">"网络监控"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"关闭VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"断开VPN连接"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"查看政策"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"此设备归<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>所有。\n\n您的 IT 管理员能够监控和管理与您的设备相关的设置、企业权限、应用、数据,以及您设备的位置信息。\n\n如需了解详情,请与您的 IT 管理员联系。"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"此设备归贵单位所有。\n\n您的 IT 管理员能够监控和管理与您的设备相关的设置、企业权限、应用、数据,以及您设备的位置信息。\n\n如需了解详情,请与您的 IT 管理员联系。"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"您所在的单位已在此设备上安装证书授权中心。您的安全网络流量可能会受到监控或修改。"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"您所在的单位已为您的工作资料安装证书授权中心。您的安全网络流量可能会受到监控或修改。"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"此设备上已安装证书授权中心。您的安全网络流量可能会受到监控或修改。"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"静音"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"默认"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"气泡"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"自动"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"不发出提示音,也不振动"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"不发出提示音,也不振动;显示在对话部分的靠下位置"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"可能会响铃或振动(取决于手机设置)"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能会响铃或振动(取决于手机设置)。默认情况下,来自<xliff:g id="APP_NAME">%1$s</xliff:g>的对话会以对话泡的形式显示。"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"通过可链接到这项内容的浮动快捷方式吸引您的注意。"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"让系统决定是否应让设备在收到此通知时发出提示音或振动"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"以悬浮对话泡形式显示在对话部分顶部,如果设备处于锁定状态,在锁定屏幕上显示个人资料照片"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"设置"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"优先"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"此应用正显示在屏幕上其他应用的上层,并且正在使用麦克风和摄像头。"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"设置"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"确定"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"此通知已被系统静音。"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"此通知已被系统升级。"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"此通知已被系统降级。"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"是否正确?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"感谢您提供反馈!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"确定"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>的通知控件已打开"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"<xliff:g id="APP_NAME">%1$s</xliff:g>的通知控件已关闭"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"允许接收来自此频道的通知"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"修改设置顺序。"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 页,共 <xliff:g id="ID_2">%2$d</xliff:g> 页"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"锁定屏幕"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"展开"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"最小化"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"关闭"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"设置"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"向下拖动即可关闭"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"菜单"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"<xliff:g id="NAME">%s</xliff:g>目前位于“画中画”中"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"如果您不想让“<xliff:g id="NAME">%s</xliff:g>”使用此功能,请点按以打开设置,然后关闭此功能。"</string>
- <string name="pip_play" msgid="333995977693142810">"播放"</string>
- <string name="pip_pause" msgid="1139598607050555845">"暂停"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"跳到下一个"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"跳到上一个"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"手机因严重发热而自动关机"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"现在,您的手机已恢复正常运行"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"现在,您的手机已恢复正常运行。\n点按即可了解详情"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"由于发热严重,因此您的手机执行了自动关机以降温。现在,您的手机已恢复正常运行。\n\n以下情况可能会导致您的手机严重发热:\n • 使用占用大量资源的应用(例如游戏、视频或导航应用)\n • 下载或上传大型文件\n • 在高温环境下使用手机"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"查看处理步骤"</string>
<string name="high_temp_title" msgid="2218333576838496100">"手机温度上升中"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"手机降温时,部分功能的使用会受限制"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"手机降温时,部分功能的使用会受限制。\n点按即可了解详情"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"您的手机将自动尝试降温。您依然可以使用您的手机,但是手机运行速度可能会更慢。\n\n手机降温后,就会恢复正常的运行速度。"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"查看处理步骤"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"拔下充电器"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"为此设备充电时出现问题。这可能是由数据线太热所导致,请拔下电源适配器并采取相应的处理措施。"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"查看处理步骤"</string>
@@ -1057,7 +1021,7 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"设置"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"放大窗口"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"放大窗口控件"</string>
- <string name="quick_controls_title" msgid="6839108006171302273">"设备控件"</string>
+ <string name="quick_controls_title" msgid="6839108006171302273">"设备控制器"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"为您所连接的设备添加控件"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"设置设备控件"</string>
<string name="quick_controls_setup_subtitle" msgid="1681506617879773824">"按住电源按钮即可访问您的控件"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"按住并拖动即可重新排列控件"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"已移除所有控件"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"未保存更改"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"查看其他应用"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"无法加载控件。请查看<xliff:g id="APP">%s</xliff:g>应用,确保应用设置没有更改。"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"找不到兼容的控件"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"其他"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"确认<xliff:g id="DEVICE">%s</xliff:g>的更改"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"滑动可查看更多结构"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在加载推荐内容"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"关闭此媒体会话"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"媒体"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"隐藏当前会话。"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"隐藏"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"继续播放"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"设置"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"无效,请检查应用"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"出现错误,正在重试…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"未找到"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings_tv.xml b/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
index acb6ee0123e6..aa3e251c9c35 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"画中画"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(节目没有标题)"</string>
- <string name="pip_close" msgid="5775212044472849930">"关闭画中画"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"全屏"</string>
<string name="mic_active" msgid="5766614241012047024">"麦克风处于启用状态"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s访问过您的麦克风"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 84b3364619a2..aa9945fbd956 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"再次輕按即可開啟"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string>
<string name="keyguard_retry" msgid="886802522584053523">"請向上滑動以再試一次"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"此裝置屬於您的機構"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"此裝置屬於「<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>」"</string>
<string name="phone_hint" msgid="6682125338461375925">"從圖示滑動即可使用手機功能"</string>
<string name="voice_hint" msgid="7476017460191291417">"從圖示滑動即可使用語音助手"</string>
<string name="camera_hint" msgid="4519495795000658637">"從圖示滑動即可使用相機功能"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"個人檔案可能受到監控"</string>
<string name="vpn_footer" msgid="3457155078010607471">"網絡可能會受到監控"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"網絡可能會受到監控"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"您的機構擁有此裝置,並可能會監察網絡流量"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」擁有此裝置,並可能會監察網絡流量"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"此裝置屬於您的機構,並已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"此裝置屬於「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」,並已連結至「<xliff:g id="VPN_APP">%2$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"此裝置屬於您的機構"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"此裝置屬於「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"此裝置屬於您的機構,並已連結至 VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"此裝置屬於「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」,並已連結至 VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"您的機構可能監控您工作設定檔上的網絡流量"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>可能會監控您工作設定檔上的網絡流量"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"網絡可能會受到監控"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"此裝置已連結至 VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"您的工作設定檔已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"您的個人設定檔已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"此裝置已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"裝置管理"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"個人檔案監控"</string>
<string name="monitoring_title" msgid="4063890083735924568">"網絡監控"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"停用 VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"中斷 VPN 連線"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"查看政策"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"此裝置屬於 <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>。\n\n您的 IT 管理員可監察及管理與裝置相關聯的設定、公司存取權、應用程式和資料,以及裝置的位置資料。\n\n如要瞭解詳情,請與您的 IT 管理員聯絡。"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"此裝置屬於您的機構。\n\n您的 IT 管理員可監察及管理與裝置相關聯的設定、公司存取權、應用程式和資料,以及裝置的位置資料。\n\n如要瞭解詳情,請與您的 IT 管理員聯絡。"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"您的機構已在此裝置中安裝憑證授權單位。您的安全網絡流量可能會受監控或修改。"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"您的機構已在您的工作設定檔中安裝憑證授權單位。您的安全網絡流量可能會受監控或修改。"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"此裝置已安裝憑證授權單位。您的安全網絡流量可能會受監控或修改。"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"靜音"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"預設"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"氣泡"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"無音效或震動"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"無音效或震動,並在對話部分的較低位置顯示"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"可能會根據手機設定發出鈴聲或震動"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機設定發出鈴聲或震動。「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會預設以對話氣泡顯示。"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"為此內容建立浮動捷徑以保持注意力。"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"由系統判斷是否要讓此通知發出音效或震動"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"在對話部分的頂部以浮動對話氣泡顯示,並在上鎖畫面顯示個人檔案相片"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"設定"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"重要"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"此應用程式目前透過其他應用程式在畫面上顯示內容,且正在使用麥克風和相機。"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"設定"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"確定"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"系統已將此通知設為靜音。"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"系統已將此通知升級。"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"系統已將此通知降級。"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"是否正確?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"多謝您提供意見!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"確定"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"開咗「<xliff:g id="APP_NAME">%1$s</xliff:g>」嘅通知控制項"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"閂咗「<xliff:g id="APP_NAME">%1$s</xliff:g>」嘅通知控制項"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"允許收到呢個頻道嘅通知"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"編輯設定次序。"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁 (共 <xliff:g id="ID_2">%2$d</xliff:g> 頁)"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"螢幕鎖定"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"展開"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"最小化"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"關閉"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"設定"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"向下拖曳即可關閉"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"選單"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"「<xliff:g id="NAME">%s</xliff:g>」目前在畫中畫模式"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"如果您不想「<xliff:g id="NAME">%s</xliff:g>」使用此功能,請輕按以開啟設定,然後停用此功能。"</string>
- <string name="pip_play" msgid="333995977693142810">"播放"</string>
- <string name="pip_pause" msgid="1139598607050555845">"暫停"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"跳到下一個"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"跳到上一個"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"手機因過熱而關上"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"您的手機現已正常運作"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"您的手機現已正常運作。\n輕按即可瞭解詳情"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"您的手機之前因過熱而關上降溫。手機現已正常運作。\n\n以下情況可能會導致手機過熱:\n • 使用耗用大量資源的應用程式 (例如遊戲、影片或導航應用程式)\n • 下載或上載大型檔案\n • 在高溫環境下使用手機"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"查看保養步驟"</string>
<string name="high_temp_title" msgid="2218333576838496100">"手機溫度正在上升"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"手機降溫時,部分功能會受限制"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"手機降溫時,部分功能會受限制。\n輕按即可瞭解詳情"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"手機會自動嘗試降溫。您仍可以使用手機,但手機的運作速度可能較慢。\n\n手機降溫後便會恢復正常。"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"查看保養步驟"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"拔下充電器"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"為此裝置充電時發生問題。請拔除電源適配器並注意安全,因為連接線可能會發熱。"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"查看保養步驟"</string>
@@ -1078,8 +1042,9 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"按住並拖曳便可重新排列控制項"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"已移除所有控制項"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"未儲存變更"</string>
- <string name="controls_favorite_load_error" msgid="5126216176144877419">"無法載入控制項。請檢查 <xliff:g id="APP">%s</xliff:g> 應用程式,確定應用程式設定並無變更。"</string>
- <string name="controls_favorite_load_none" msgid="7687593026725357775">"沒有可用的兼容控制項"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"查看其他應用程式"</string>
+ <string name="controls_favorite_load_error" msgid="5126216176144877419">"無法載入控制項。請檢查 <xliff:g id="APP">%s</xliff:g> 應用程式,確保設定沒有變動。"</string>
+ <string name="controls_favorite_load_none" msgid="7687593026725357775">"沒有兼容的控制項"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"其他"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"加到裝置控制"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"新增"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"確認<xliff:g id="DEVICE">%s</xliff:g>變更"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"滑動以查看更多"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"關閉此媒體版面"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"隱藏"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"已停用,請檢查應用程式"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"發生錯誤,正在重試…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"找不到"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings_tv.xml b/packages/SystemUI/res/values-zh-rHK/strings_tv.xml
index 1cd63144b904..45d3229cdcb6 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings_tv.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"畫中畫"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(沒有標題的節目)"</string>
- <string name="pip_close" msgid="5775212044472849930">"關閉 PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"全螢幕"</string>
<string name="mic_active" msgid="5766614241012047024">"麥克風已啟用"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"「%1$s」已存取您的麥克風"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 1f7e86bf2306..92bdfc3f1219 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -28,15 +28,15 @@
<string name="battery_low_percent_format" msgid="4276661262843170964">"僅剩 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"電力剩餘 <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="5917433188456218857">"電力剩餘 <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="4968468824040940688">"僅剩 <xliff:g id="PERCENTAGE">%s</xliff:g>。節約耗電量模式已開啟。"</string>
+ <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"僅剩 <xliff:g id="PERCENTAGE">%s</xliff:g>。省電模式已開啟。"</string>
<string name="invalid_charger" msgid="4370074072117767416">"無法透過 USB 充電,請使用裝置隨附的充電器。"</string>
<string name="invalid_charger_title" msgid="938685362320735167">"無法透過 USB 充電"</string>
<string name="invalid_charger_text" msgid="2339310107232691577">"使用裝置隨附的充電器"</string>
<string name="battery_low_why" msgid="2056750982959359863">"設定"</string>
- <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"要開啟節約耗電量模式嗎?"</string>
- <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"關於節約耗電量功能"</string>
+ <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"要開啟省電模式嗎?"</string>
+ <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"關於省電模式"</string>
<string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"開啟"</string>
- <string name="battery_saver_start_action" msgid="4553256017945469937">"開啟節約耗電量模式"</string>
+ <string name="battery_saver_start_action" msgid="4553256017945469937">"開啟省電模式"</string>
<string name="status_bar_settings_settings_button" msgid="534331565185171556">"設定"</string>
<string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"Wi-Fi"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"自動旋轉螢幕"</string>
@@ -421,7 +421,7 @@
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"<xliff:g id="TIME">%s</xliff:g> 開啟"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> 關閉"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"深色主題"</string>
- <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"節約耗電量"</string>
+ <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"省電模式"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"於日落時開啟"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"於日出時關閉"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"開啟時間:<xliff:g id="TIME">%s</xliff:g>"</string>
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"再次輕觸即可開啟"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string>
<string name="keyguard_retry" msgid="886802522584053523">"向上滑動即可重試"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"這部裝置的擁有者為貴機構"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"這部裝置的擁有者為「<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>」"</string>
<string name="phone_hint" msgid="6682125338461375925">"滑動手機圖示即可啟用"</string>
<string name="voice_hint" msgid="7476017460191291417">"滑動語音小幫手圖示即可啟用"</string>
<string name="camera_hint" msgid="4519495795000658637">"滑動相機圖示即可啟用"</string>
@@ -501,9 +499,9 @@
<string name="user_remove_user_title" msgid="9124124694835811874">"要移除使用者嗎?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"系統將刪除這個使用者的所有應用程式和資料。"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"移除"</string>
- <string name="battery_saver_notification_title" msgid="8419266546034372562">"節約耗電量模式已開啟"</string>
+ <string name="battery_saver_notification_title" msgid="8419266546034372562">"省電模式已開啟"</string>
<string name="battery_saver_notification_text" msgid="2617841636449016951">"降低效能並限制背景數據傳輸"</string>
- <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"關閉節約耗電量模式"</string>
+ <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"關閉省電模式"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"在錄製或投放內容時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款詳情、相片、訊息和你播放的音訊。"</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"在錄製或投放內容時,提供這項功能的服務可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款詳情、相片、訊息和你播放的音訊。"</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"要開始錄製或投放內容嗎?"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"設定檔可能會受到監控"</string>
<string name="vpn_footer" msgid="3457155078010607471">"網路可能會受到監控"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"網路可能會受到監控"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"貴機構擁有這部裝置,而且可能會監控網路流量"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"這部裝置的擁有者為「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」,而且該機構可能會監控網路流量"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"這部裝置的擁有者為貴機構,並且已連線到「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"這部裝置的擁有者為「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」,並且已連線到「<xliff:g id="VPN_APP">%2$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"這部裝置的擁有者為貴機構"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"這部裝置的擁有者為「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"這部裝置的擁有者為貴機構,並且已連線到 VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"這部裝置的擁有者為「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」,並且已連線到 VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"貴機構可能會監控你工作資料夾的網路流量"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」可能會監控你工作資料夾的網路流量"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"網路可能會受到監控"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"這部裝置已連線到 VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"你的工作資料夾已連線到「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"你的個人資料夾已連線到「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"這部裝置已連線到「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"裝置管理"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"設定檔監控"</string>
<string name="monitoring_title" msgid="4063890083735924568">"網路監控"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"停用 VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"中斷 VPN 連線"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"查看政策"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"這部裝置的擁有者為「<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>」。\n\n你的 IT 管理員可以監控及管理與裝置相關聯的設定、公司系統權限、應用程式和資料,以及裝置的位置資訊。\n\n如要瞭解詳情,請與你的 IT 管理員聯絡。"</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"這部裝置的擁有者為貴機構。\n\n你的 IT 管理員可以監控及管理與裝置相關聯的設定、公司系統權限、應用程式和資料,以及裝置的位置資訊。\n\n如要瞭解詳情,請與你的 IT 管理員聯絡。"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"貴機構已為這個裝置安裝憑證授權單位憑證。你的安全網路流量可能會受到監控或修改。"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"貴機構已為你的工作資料夾安裝憑證授權單位憑證。你的安全網路流量可能會受到監控或修改。"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"這個裝置已安裝憑證授權單位憑證。你的安全網路流量可能會受到監控或修改。"</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"靜音"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"預設"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"泡泡"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"不震動或發出聲音"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"不震動或發出聲音,並顯示在對話部分的下方"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"根據手機的設定響鈴或震動"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機的設定響鈴或震動。根據預設,來自「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會以對話框形式顯示。"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"利用浮動式捷徑快速存取這項內容。"</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"由系統判斷要讓裝置在收到這則通知時震動還是發出音效"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"以浮動對話框的形式顯示在對話部分的頂端。如果裝置處於鎖定狀態,則在螢幕鎖定畫面上顯示個人資料相片"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"設定"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"優先"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"這個應用程式顯示在畫面上其他應用程式的上層,且正在使用麥克風和相機。"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"設定"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"確定"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"系統已將這則通知設為靜音。"</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"系統已提升這則通知的優先順序。"</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"系統已降低這則通知的優先順序。"</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"是否正確?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"感謝你提供意見!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"確定"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的通知控制項已開啟"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的通知控制項已關閉"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"允許來自這個頻道的通知"</string>
@@ -799,8 +775,8 @@
<item quantity="one">%d 分鐘</item>
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"電池用量"</string>
- <string name="battery_detail_charging_summary" msgid="8821202155297559706">"充電時無法使用節約耗電量模式"</string>
- <string name="battery_detail_switch_title" msgid="6940976502957380405">"節約耗電量"</string>
+ <string name="battery_detail_charging_summary" msgid="8821202155297559706">"充電時無法使用省電模式"</string>
+ <string name="battery_detail_switch_title" msgid="6940976502957380405">"省電模式"</string>
<string name="battery_detail_switch_summary" msgid="3668748557848025990">"降低效能並限制背景資料傳輸"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 按鈕"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home 鍵"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"編輯設定順序。"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁,共 <xliff:g id="ID_2">%2$d</xliff:g> 頁"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"鎖定畫面"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"展開"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"最小化"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"關閉"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"設定"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"向下拖曳即可關閉"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"選單"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"「<xliff:g id="NAME">%s</xliff:g>」目前在子母畫面中"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"如果你不想讓「<xliff:g id="NAME">%s</xliff:g>」使用這項功能,請輕觸開啟設定頁面,然後停用此功能。"</string>
- <string name="pip_play" msgid="333995977693142810">"播放"</string>
- <string name="pip_pause" msgid="1139598607050555845">"暫停"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"跳到下一個"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"跳到上一個"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"手機先前過熱,因此關閉電源"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"手機現在已恢復正常運作"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"手機現在已恢復正常運作。\n輕觸即可瞭解詳情"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"手機先前的溫度過高,因此關閉了電源以進行降溫。手機現在已恢復正常運作。\n\n以下情況可能會導致你的手機溫度過高:\n • 使用需要密集處理資料的應用程式 (例如遊戲、影片或導航應用程式)\n • 下載或上傳大型檔案\n • 在高溫環境下使用手機"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"查看處理步驟"</string>
<string name="high_temp_title" msgid="2218333576838496100">"手機變熱"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"手機降溫時,部分功能會受限"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"手機降溫時,某些功能會受限。\n輕觸即可瞭解詳情"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"手機會自動嘗試降溫。你仍可繼續使用手機,但是手機的運作速度可能會較慢。\n\n手機降溫完畢後,就會恢復正常的運作速度。"</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"查看處理步驟"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"拔除充電器"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"為這個裝置充電時發生問題。這可能是因為傳輸線過熱所致,請拔除電源變壓器並採取處理措施。"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"查看處理步驟"</string>
@@ -1013,11 +977,11 @@
<string name="slice_permission_checkbox" msgid="4242888137592298523">"允許「<xliff:g id="APP">%1$s</xliff:g>」顯示任何應用程式的區塊"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"允許"</string>
<string name="slice_permission_deny" msgid="6870256451658176895">"拒絕"</string>
- <string name="auto_saver_title" msgid="6873691178754086596">"輕觸即可排定節約耗電量模式自動開啟的情況"</string>
+ <string name="auto_saver_title" msgid="6873691178754086596">"輕觸即可排定省電模式自動開啟的情況"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"在電池電量即將耗盡時開啟"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"不用了,謝謝"</string>
- <string name="auto_saver_enabled_title" msgid="4294726198280286333">"已按照排定開啟節約耗電量模式"</string>
- <string name="auto_saver_enabled_text" msgid="7889491183116752719">"節約耗電量模式會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
+ <string name="auto_saver_enabled_title" msgid="4294726198280286333">"已按照排定開啟省電模式"</string>
+ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"我知道了"</string>
<string name="heap_dump_tile_name" msgid="2464189856478823046">"傾印 SysUI 記憶體快照"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"按住並拖曳即可重新排列控制項"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"所有控制項都已移除"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"未儲存變更"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"查看其他應用程式"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"無法載入控制項。請查看「<xliff:g id="APP">%s</xliff:g>」應用程式,確認應用程式設定沒有任何異動。"</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"找不到相容的控制項"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"其他"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"確認「<xliff:g id="DEVICE">%s</xliff:g>」的變更"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"滑動即可查看其他結構"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議控制項"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"關閉這個媒體工作階段"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"隱藏"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"無效,請查看應用程式"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"發生錯誤,正在重試…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"找不到控制項"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings_tv.xml b/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
index b48fc6f99755..fdd088449567 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"子母畫面"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(無標題的節目)"</string>
- <string name="pip_close" msgid="5775212044472849930">"關閉子母畫面"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"全螢幕"</string>
<string name="mic_active" msgid="5766614241012047024">"麥克風已開啟"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"「%1$s」已存取你的麥克風"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index e68fe07214d0..4ae126799141 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -454,10 +454,8 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Thepha futhi ukuze uvule"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swayiphela phezulu ukuze uvule"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Swayiphela phezulu ukuze uzame futhi"</string>
- <!-- no translation found for do_disclosure_generic (4896482821974707167) -->
- <skip />
- <!-- no translation found for do_disclosure_with_name (2091641464065004091) -->
- <skip />
+ <string name="do_disclosure_generic" msgid="4896482821974707167">"Le divayisi eyenhlangano yakho"</string>
+ <string name="do_disclosure_with_name" msgid="2091641464065004091">"Le divayisi ngeye-<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Swayiphela ifoni kusukela kusithonjana"</string>
<string name="voice_hint" msgid="7476017460191291417">"Swayiphela isilekeleli sezwi kusukela kusithonjana"</string>
<string name="camera_hint" msgid="4519495795000658637">"Swayiphela ikhamela kusukela kusithonjana"</string>
@@ -523,33 +521,21 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"Iphrofayela ingaqashwa"</string>
<string name="vpn_footer" msgid="3457155078010607471">"Inethiwekhi kungenzeka iqashiwe"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"Inethiwekhi kungenzeka iqashiwe"</string>
- <!-- no translation found for quick_settings_disclosure_management_monitoring (8231336875820702180) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_monitoring (2831423806103479812) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_named_vpn (6096715329056415588) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (5302786161534380104) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management (5515296598440684962) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management (3476472755775165827) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_management_vpns (371835422690053154) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_management_vpns (4046375645500668555) -->
- <skip />
+ <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Inhlangano yakho ingumnikazi wale divayisi futhi ingaqapha ithrafikhi yenethiwekhi"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"I-<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ingumnikazi wale divayisi futhi ingaqapha ithrafikhi yenethiwekhi"</string>
+ <string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"Le divayisi ngeyenhlangano yakho futhi ixhunywe ku-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"Le divayisi ngeye-<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> futhi ixhunywe ku-<xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Le divayisi eyenhlangano yakho"</string>
+ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Le divayisi ngeye-<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"Le divayisi ngeyenhlangano yakho futhi ixhunywe kuma-VPN"</string>
+ <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"Le divayisi ngeye-<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> futhi ixhunywe kuma-VPN"</string>
<string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Inhlangano yakho ingaqapha ithrafikhi yenethiwekhi kuphrofayela yakho yomsebenzi"</string>
<string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"I-<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ingaqaphela ithrafikhi yenethiwekhi kuphrofayela yakho yomsebenzi"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Inethiwekhi kungenzeka iqashiwe"</string>
- <!-- no translation found for quick_settings_disclosure_vpns (7213546797022280246) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (8117568745060010789) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (5481763430080807797) -->
- <skip />
- <!-- no translation found for quick_settings_disclosure_named_vpn (2350838218824492465) -->
- <skip />
+ <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"Le divayisi ixhunywe kuma-VPN"</string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Iphrofayela yakho yomsebenzi ixhunywe ku-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"Iphrofayela yakho yomuntu ixhunywe ku-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+ <string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"Le divayisi ixhunywe ku-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"Ukuphathwa kwedivayisi"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"Ukuqapha iphrofayela"</string>
<string name="monitoring_title" msgid="4063890083735924568">"Ukuqashwa kwenethiwekhi"</string>
@@ -559,10 +545,8 @@
<string name="disable_vpn" msgid="482685974985502922">"Khubaza i-VPN"</string>
<string name="disconnect_vpn" msgid="26286850045344557">"Nqamula i-VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Buka izinqubomgomo"</string>
- <!-- no translation found for monitoring_description_named_management (505833016545056036) -->
- <skip />
- <!-- no translation found for monitoring_description_management (4308879039175729014) -->
- <skip />
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Le divayisi ngeye-<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nUmphathi wakho we-IT angakwazi ukugada nokulawula amasethingi, ukufinyelela kwenhlangano, izinhlelo zokusebenza, idatha ehlobene nedivayisi yakho, nolwazi lwendawo yedivayisi yakho.\n\nUkuze uthole ulwazi olwengeziwe, xhumana nomphathi wakho we-IT."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Le divayisi ngeyenhlangano.\n\nUmphathi wakho we-IT angakwazi ukugada nokulawula amasethingi, ukufinyelela kwenhlangano, izinhlelo zokusebenza, idatha ehlobene nedivayisi yakho, nolwazi lwendawo yedivayisi yakho.\n\nUkuze uthole ulwazi olwengeziwe, xhumana nomphathi wakho we-IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Inhlangano yakho ifake ukugunyazwa kwesitifiketi kule divayisi. Ithrafikhi yenethiwekhi yakho evikelekile kungenzeka iqashelwe noma ilungiswe."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Inhlangano yakho ifake ukugunyaza kwesitifiketi kuphrofayela yakho yomsebenzi. Ithrafikhi yenethiwekhi yakho evikelekile ingaqashwa noma ilungiswe."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Ukugunyaza kwesitifiketi kufakwe kule divayisi. Ithrafikhi yenethiwekhi yakho evikelekile ingaqashelwa noma ilungiswe."</string>
@@ -727,15 +711,13 @@
<string name="notification_silence_title" msgid="8608090968400832335">"Kuthulile"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Okuzenzekelayo"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Ibhamuza"</string>
- <!-- no translation found for notification_automatic_title (3745465364578762652) -->
- <skip />
+ <string name="notification_automatic_title" msgid="3745465364578762652">"Okuzenzekelayo"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Awukho umsindo noma ukudlidliza"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Awukho umsindo noma ukudlidliza futhi ivela ngezansi esigabeni sengxoxo"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Ingase ikhale noma idlidlize kuya ngamasethingi wefoni yakho"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Ingase ikhale noma idlidlize kuya ngamasethingi wefoni yakho. Izingxoxo ezivela ku-<xliff:g id="APP_NAME">%1$s</xliff:g> ziba yibhamuza ngokuzenzakalela."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Igcina ukunaka kwakho ngesinqamuleli esintantayo kulokhu okuqukethwe."</string>
- <!-- no translation found for notification_channel_summary_automatic (5813109268050235275) -->
- <skip />
+ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Vumela isistimu inqume uma lesi saziso kufanele senze umsindo noma sidlidlize"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Iboniswa ngenhla kwesigaba sengxoxo, ivela njengebhamuza elintantayo, ibonisa isithombe sephrofayela kukukhiya isikrini"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Izilungiselelo"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Okubalulekile"</string>
@@ -756,18 +738,12 @@
<string name="appops_camera_mic_overlay" msgid="5584311236445644095">"Lolu hlelo lokusebenza liboniswa ngaphezulu kwezinye izinhlelo zokusebenza kusikrini sakho futhi kusetshenziswa imakrofoni nekhamera."</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"Izilungiselelo"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"KULUNGILE"</string>
- <!-- no translation found for feedback_silenced (5382212321253328247) -->
- <skip />
- <!-- no translation found for feedback_promoted (8075757485407091976) -->
- <skip />
- <!-- no translation found for feedback_demoted (5848066008939031913) -->
- <skip />
- <!-- no translation found for feedback_prompt (2278631214125128281) -->
- <skip />
- <!-- no translation found for feedback_response (4671729244976641339) -->
- <skip />
- <!-- no translation found for feedback_ok (6481426753298857144) -->
- <skip />
+ <string name="feedback_silenced" msgid="5382212321253328247">"Lesi saziso sithuliswe isistimu."</string>
+ <string name="feedback_promoted" msgid="8075757485407091976">"Lesi saziso siphromothwe isistimu."</string>
+ <string name="feedback_demoted" msgid="5848066008939031913">"Lesi saziso sehliswe isikhundla isistimu."</string>
+ <string name="feedback_prompt" msgid="2278631214125128281">"Ingabe kade kulungile lokhu?"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"Siyabonga ngempendulo!"</string>
+ <string name="feedback_ok" msgid="6481426753298857144">"KULUNGILE"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Izilawuli zesaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g> zivuliwe"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Izilawuli zesaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g> zivaliwe"</string>
<string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Zonke izaziso kusuka kulesi siteshi"</string>
@@ -940,26 +916,14 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Hlela uhlelo lwezilungiselelo."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Ikhasi <xliff:g id="ID_1">%1$d</xliff:g> kwangu-<xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Khiya isikrini"</string>
- <string name="pip_phone_expand" msgid="1424988917240616212">"Nweba"</string>
- <string name="pip_phone_minimize" msgid="9057117033655996059">"Nciphisa"</string>
- <string name="pip_phone_close" msgid="8801864042095341824">"Vala"</string>
- <string name="pip_phone_settings" msgid="5687538631925004341">"Izilungiselelo"</string>
- <string name="pip_phone_dismiss_hint" msgid="5825740708095316710">"Hudulela phansi ukuze ucashise"</string>
- <string name="pip_menu_title" msgid="6365909306215631910">"Imenyu"</string>
- <string name="pip_notification_title" msgid="8661573026059630525">"U-<xliff:g id="NAME">%s</xliff:g> ungaphakathi kwesithombe esiphakathi kwesithombe"</string>
- <string name="pip_notification_message" msgid="4991831338795022227">"Uma ungafuni i-<xliff:g id="NAME">%s</xliff:g> ukuthi isebenzise lesi sici, thepha ukuze uvule izilungiselelo uphinde uyivale."</string>
- <string name="pip_play" msgid="333995977693142810">"Dlala"</string>
- <string name="pip_pause" msgid="1139598607050555845">"Misa isikhashana"</string>
- <string name="pip_skip_to_next" msgid="3864212650579956062">"Yeqela kokulandelayo"</string>
- <string name="pip_skip_to_prev" msgid="3742589641443049237">"Yeqela kokwangaphambilini"</string>
- <!-- no translation found for accessibility_action_pip_resize (8237306972921160456) -->
- <skip />
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Ifoni ivaliwe ngenxa yokushisa"</string>
- <string name="thermal_shutdown_message" msgid="7432744214105003895">"Ifoni yakho manje isebenza kahle"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Ifoni yakho manje isebenza ngokuvamile.\nThepha ukuze uthole ulwazi olungeziwe"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Ifoni yakho ibishisa kakhulu, ngakho-ke yacisha ukuze iphole. Ifoni yakho manje isebenza ngokuvamile.\n\nIfoni yakho ingashisa kakhulu uma:\n • Usebenzisa izinhlelo zokusebenza ezinkulu (njegegeyimu, ividiyo, noma izinhlelo zokusebenza zokuzula)\n • Landa noma layisha amafayela amakhulu\n • Sebenzisa ifoni yakho kumathempelesha aphezulu"</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Bona izinyathelo zokunakekelwa"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Ifoni iyafudumala"</string>
- <string name="high_temp_notif_message" msgid="163928048626045592">"Ezinye izici zikhawulelwe ngenkathi ifoni iphola"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Ezinye izici zikhawulelwe ngenkathi ifoni iphola.\nThepha mayelana nolwazi olwengeziwe"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ifoni yakho izozama ngokuzenzakalela ukuphola. Ungasasebenzisa ifoni yakho, kodwa ingasebenza ngokungasheshi.\n\nUma ifoni yakho isipholile, izosebenza ngokuvamile."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Bona izinyathelo zokunakekelwa"</string>
<string name="high_temp_alarm_title" msgid="2359958549570161495">"Khipha ishaja"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Kukhona inkinga yokushaja le divayisi. Khipha i-adaptha yamandla, uphinde unakekele njengoba ikhebuli kungenzeka lifudumele."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Bona izinyathelo zokunakekelwa"</string>
@@ -1078,6 +1042,7 @@
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Bamba futhi uhudule ukuze uphinde ulungise izilawuli"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Zonke izilawuli zisusiwe"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Izinguquko azilondolozwanga"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Bona ezinye izinhlelo zokusebenza"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Izilawuli azikwazanga ukulayishwa. Hlola uhlelo lokusebenza le-<xliff:g id="APP">%s</xliff:g> ukuqinisekisa ukuthi amasethingi wohlelo lokusebenza awashintshile."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Izilawuli ezihambelanayo azitholakali"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Okunye"</string>
@@ -1095,8 +1060,11 @@
<string name="controls_confirmation_message" msgid="7744104992609594859">"Qinisekisa ushintsho lwe-<xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_structure_tooltip" msgid="4355922222944447867">"Swayipha ukuze ubone okuningi"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ilayisha izincomo"</string>
- <string name="controls_media_close_session" msgid="9023534788828414585">"Vala leseshini yemidiya"</string>
+ <string name="controls_media_title" msgid="1746947284862928133">"Imidiya"</string>
+ <string name="controls_media_close_session" msgid="3957093425905475065">"Fihla iseshini yamanje."</string>
+ <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Fihla"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Qalisa kabusha"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Izilungiselelo"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Akusebenzi, hlola uhlelo lokusebenza"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"Iphutha, iyazama futhi…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ayitholakali"</string>
diff --git a/packages/SystemUI/res/values-zu/strings_tv.xml b/packages/SystemUI/res/values-zu/strings_tv.xml
index c6f8d3ffcc74..4fed753a88ad 100644
--- a/packages/SystemUI/res/values-zu/strings_tv.xml
+++ b/packages/SystemUI/res/values-zu/strings_tv.xml
@@ -19,10 +19,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="844249465483874817">"Isithombe-esithombeni"</string>
- <string name="pip_notification_unknown_title" msgid="4413256731340767259">"(Alukho uhlelo lwesihloko)"</string>
- <string name="pip_close" msgid="5775212044472849930">"Vala i-PIP"</string>
- <string name="pip_fullscreen" msgid="3877997489869475181">"Iskrini esigcwele"</string>
<string name="mic_active" msgid="5766614241012047024">"Imakrofoni iyasebenza"</string>
<string name="app_accessed_mic" msgid="2754428675130470196">"%1$s ifinyelele imakrofoni yakho"</string>
</resources>
diff --git a/packages/SystemUI/res/values/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml
index 3343a84e7a9c..9c7707791180 100644
--- a/packages/SystemUI/res/values/arrays_tv.xml
+++ b/packages/SystemUI/res/values/arrays_tv.xml
@@ -17,23 +17,6 @@
*/
-->
<resources>
- <!-- List of package names or class names which are considered as Settings,
- so PIP location should be adjusted to the left of the side panel.
- If it should be applied for all activities in a package, add the package name.
- If it should be applied for an activity in a package, add its class name with package name.
- The class name must follow format 'package_name/.class_name' ('/.' in between).
- This can be overriden in an overlay.
- -->
- <string-array name="tv_pip_settings_class_name" translatable="false">
- <item>com.android.tv.settings</item>
- <item>com.google.android.leanbacklauncher/.settings.HomeScreenSettingsActivity</item>
- <item>com.google.android.apps.mediashell/.settings.CastSettingsActivity</item>
- <item>com.google.android.katniss.setting/.SpeechSettingsActivity</item>
- <item>com.google.android.katniss.setting/.SearchSettingsActivity</item>
- <item>com.google.android.tungsten.setupwraith/.settings.usage.UsageDiagnosticsSettingActivity</item>
- <item>com.google.android.tvlauncher/.notifications.NotificationsSidePanelActivity</item>
- </string-array>
-
<string-array name="audio_recording_disclosure_exempt_apps" translatable="false">
</string-array>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index c4195940d11a..84dbd60b799d 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -133,11 +133,6 @@
<attr name="buttonStrokeWidth" format="dimension" />
</declare-styleable>
- <!-- Used to style rotate suggestion button AVD animations -->
- <attr name="rotateButtonStartAngle" format="float" />
- <attr name="rotateButtonEndAngle" format="float" />
- <attr name="rotateButtonScaleX" format="float" />
-
<!-- Used to style charging animation AVD animation -->
<attr name="chargingAnimColor" format="color" />
@@ -161,5 +156,12 @@
<attr name="rippleMinSize" format="dimension" />
<attr name="rippleMaxSize" format="dimension" />
</declare-styleable>
+
+ <declare-styleable name="UdfpsView">
+ <attr name="sensorRadius" format="dimension"/>
+ <attr name="sensorMarginBottom" format="dimension"/>
+ <attr name="sensorPressureCoefficient" format="float"/>
+ <attr name="sensorTouchAreaCoefficient" format="float"/>
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 3b6400f208bd..f407a8dcc57f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -494,23 +494,13 @@
<!-- On debuggable builds, alert the user if SystemUI PSS goes over this number (in kb) -->
<integer name="watch_heap_limit">256000</integer>
- <!-- Animation duration for resizing of PIP when entering/exiting. -->
- <integer name="config_pipResizeAnimationDuration">425</integer>
-
- <!-- Allow dragging the PIP to a location to close it -->
- <bool name="config_pipEnableDismissDragToEdge">true</bool>
-
- <!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu -->
- <bool name="config_pipEnableResizeForMenu">true</bool>
-
- <!-- Allow PIP to enable round corner, see also R.dimen.pip_corner_radius -->
- <bool name="config_pipEnableRoundCorner">false</bool>
-
<!-- SystemUI Plugins that can be loaded on user builds. -->
<string-array name="config_pluginWhitelist" translatable="false">
<item>com.android.systemui</item>
</string-array>
+ <integer name="ongoing_appops_dialog_max_apps">5</integer>
+
<!-- Launcher package name for overlaying icons. -->
<string name="launcher_overlayable_package" translatable="false">com.android.launcher3</string>
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
index ffd58dcfd50d..5cb840f24c84 100644
--- a/packages/SystemUI/res/values/config_tv.xml
+++ b/packages/SystemUI/res/values/config_tv.xml
@@ -16,10 +16,15 @@
<resources>
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
- when the PIP menu is shown with settings. -->
- <string translatable="false" name="pip_settings_bounds">"662 756 1142 1026"</string>
-
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
when the PIP menu is shown in center. -->
<string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
+
+ <!-- Whether to enable microphone disclosure indicator
+ (com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar). -->
+ <bool name="audio_recording_disclosure_enabled">true</bool>
+
+ <!-- Whether the indicator should expand and show the recording application's label.
+ When disabled (false) the "minimized" indicator would appear on the screen whenever an
+ application is recording, but will not reveal to the user what application this is. -->
+ <bool name="audio_recording_disclosure_reveal_packages">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 840e82c4bdb6..122fcb21a9f4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -974,47 +974,8 @@
<!-- The start margin of quick scrub onboarding toast. -->
<dimen name="recents_quick_scrub_onboarding_margin_start">8dp</dimen>
- <!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
- <dimen name="floating_dismiss_gradient_height">250dp</dimen>
-
<dimen name="floating_dismiss_bottom_margin">50dp</dimen>
- <!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. -->
- <dimen name="pip_dismiss_text_bottom_margin">24dp</dimen>
-
- <!-- The shortest-edge size of the expanded PiP. -->
- <dimen name="pip_expanded_shortest_edge_size">160dp</dimen>
-
- <!-- The additional offset to apply to the IME animation to account for the input field. -->
- <dimen name="pip_ime_offset">48dp</dimen>
-
- <!-- The padding between actions in the PiP in landscape Note that the PiP does not reflect
- the configuration of the device, so we can't use -land resources. -->
- <dimen name="pip_between_action_padding_land">8dp</dimen>
-
- <!-- The height of the PiP actions container in which the actions are vertically centered. -->
- <dimen name="pip_action_size">48dp</dimen>
-
- <!-- The padding around a PiP actions. -->
- <dimen name="pip_action_padding">12dp</dimen>
-
- <!-- The bottom margin of the expand container when there are actions.
- Equal to pip_action_size - pip_action_padding. -->
- <dimen name="pip_expand_container_edge_margin">30dp</dimen>
-
- <!-- The touchable/draggable edge size for PIP resize. -->
- <dimen name="pip_resize_edge_size">48dp</dimen>
-
- <!-- PIP Resize handle size and margin. -->
- <dimen name="pip_resize_handle_size">12dp</dimen>
- <dimen name="pip_resize_handle_margin">4dp</dimen>
-
- <!-- The corner radius for PiP window. -->
- <dimen name="pip_corner_radius">8dp</dimen>
-
- <!-- The buffer to use when calculating whether the pip is in an adjust zone. -->
- <dimen name="pip_bottom_offset_buffer">1dp</dimen>
-
<dimen name="default_gear_space">18dp</dimen>
<dimen name="cell_overlay_padding">18dp</dimen>
@@ -1062,6 +1023,7 @@
<!-- Shutdown and restart actions are larger in power options dialog -->
<dimen name="global_actions_power_dialog_item_height">190dp</dimen>
<dimen name="global_actions_power_dialog_item_width">255dp</dimen>
+ <dimen name="global_actions_power_dialog_item_bottom_margin">45dp</dimen>
<!-- The maximum offset in either direction that elements are moved horizontally to prevent
burn-in on AOD. -->
@@ -1175,6 +1137,23 @@
<!-- How much into a DisplayCutout's bounds we can go, on each side -->
<dimen name="display_cutout_margin_consumption">0px</dimen>
+
+ <!-- Height of the Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_height">32dp</dimen>
+ <!-- Padding between background of Ongoing App Ops chip and content -->
+ <dimen name="ongoing_appops_chip_bg_padding">8dp</dimen>
+ <!-- Side padding between background of Ongoing App Ops chip and content -->
+ <dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
+ <!-- Margin between icons of Ongoing App Ops chip when QQS-->
+ <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
+ <!-- Margin between icons of Ongoing App Ops chip when QS-->
+ <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen>
+ <!-- Icon size of Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
+ <!-- Radius of Ongoing App Ops chip corners -->
+ <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
+
+
<!-- How much each bubble is elevated. -->
<dimen name="bubble_elevation">1dp</dimen>
<!-- How much the bubble flyout text container is elevated. -->
@@ -1221,9 +1200,9 @@
<!-- Padding of bubble overflow empty state subtitle -->
<dimen name="bubble_empty_overflow_subtitle_padding">50dp</dimen>
<!-- Height of the triangle that points to the expanded bubble -->
- <dimen name="bubble_pointer_height">4dp</dimen>
+ <dimen name="bubble_pointer_height">8dp</dimen>
<!-- Width of the triangle that points to the expanded bubble -->
- <dimen name="bubble_pointer_width">6dp</dimen>
+ <dimen name="bubble_pointer_width">12dp</dimen>
<!-- Extra padding around the dismiss target for bubbles -->
<dimen name="bubble_dismiss_slop">16dp</dimen>
<!-- Height of button allowing users to adjust settings for bubbles. -->
@@ -1248,7 +1227,6 @@
<dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
<dimen name="bubble_manage_menu_elevation">4dp</dimen>
- <dimen name="dismiss_circle_size">52dp</dimen>
<dimen name="dismiss_target_x_size">24dp</dimen>
<!-- Bubbles user education views -->
@@ -1269,9 +1247,10 @@
<dimen name="qs_media_padding">16dp</dimen>
<dimen name="qs_media_panel_outer_padding">16dp</dimen>
<dimen name="qs_media_album_size">52dp</dimen>
- <dimen name="qs_seamless_icon_size">20dp</dimen>
- <dimen name="qs_seamless_fallback_icon_size">20dp</dimen>
- <dimen name="qs_seamless_fallback_top_margin">18dp</dimen>
+ <dimen name="qs_media_icon_size">16dp</dimen>
+ <dimen name="qs_center_guideline_padding">10dp</dimen>
+ <dimen name="qs_seamless_icon_size">@dimen/qs_media_icon_size</dimen>
+ <dimen name="qs_seamless_fallback_icon_size">@dimen/qs_seamless_icon_size</dimen>
<dimen name="qs_seamless_fallback_end_margin">16dp</dimen>
<dimen name="qqs_media_spacing">16dp</dimen>
<dimen name="qs_footer_horizontal_margin">22dp</dimen>
@@ -1382,4 +1361,8 @@
<dimen name="config_rounded_mask_size">@*android:dimen/rounded_corner_radius</dimen>
<dimen name="config_rounded_mask_size_top">@*android:dimen/rounded_corner_radius_top</dimen>
<dimen name="config_rounded_mask_size_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
+
+ <!-- One-Handed Mode -->
+ <!-- Threshold for dragging distance to enable one-handed mode -->
+ <dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 8212d6159737..a56f6f56836a 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -92,6 +92,8 @@
<item type="id" name="requires_remeasuring"/>
+ <item type="id" name="secondary_home_handle" />
+
<!-- Whether the icon is from a notification for which targetSdk < L -->
<item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 66ef747cbd75..8b6543ac73bd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2374,45 +2374,6 @@
<!-- SysUI Tuner: Section to customize lockscreen shortcuts [CHAR LIMIT=60] -->
<string name="tuner_lock_screen">Lock screen</string>
- <!-- Making the PIP fullscreen [CHAR LIMIT=25] -->
- <string name="pip_phone_expand">Expand</string>
-
- <!-- Label for PIP action to Minimize the PIP [CHAR LIMIT=25] -->
- <string name="pip_phone_minimize">Minimize</string>
-
- <!-- Label for PIP close button [CHAR LIMIT=NONE]-->
- <string name="pip_phone_close">Close</string>
-
- <!-- Label for PIP settings button [CHAR LIMIT=NONE]-->
- <string name="pip_phone_settings">Settings</string>
-
- <!-- Label for PIP the drag to dismiss hint [CHAR LIMIT=NONE]-->
- <string name="pip_phone_dismiss_hint">Drag down to dismiss</string>
-
- <!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
- <string name="pip_menu_title">Menu</string>
-
- <!-- PiP BTW notification title. [CHAR LIMIT=50] -->
- <string name="pip_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is in picture-in-picture</string>
-
- <!-- PiP BTW notification description. [CHAR LIMIT=NONE] -->
- <string name="pip_notification_message">If you don\'t want <xliff:g id="name" example="Google Maps">%s</xliff:g> to use this feature, tap to open settings and turn it off.</string>
-
- <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
- <string name="pip_play">Play</string>
-
- <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
- <string name="pip_pause">Pause</string>
-
- <!-- Button to skip to the next media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
- <string name="pip_skip_to_next">Skip to next</string>
-
- <!-- Button to skip to the prev media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
- <string name="pip_skip_to_prev">Skip to previous</string>
-
- <!-- Accessibility action for resizing PIP [CHAR LIMIT=NONE] -->
- <string name="accessibility_action_pip_resize">Resize</string>
-
<!-- Tuner string -->
<string name="change_theme_reboot" translatable="false">Changing the theme requires a restart.</string>
<!-- Tuner string -->
@@ -2422,17 +2383,26 @@
<!-- Title for notification & dialog that the user's phone last shut down because it got too hot. [CHAR LIMIT=40] -->
<string name="thermal_shutdown_title">Phone turned off due to heat</string>
- <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=100] -->
- <string name="thermal_shutdown_message">Your phone is now running normally</string>
- <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=450] -->
+ <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=120] -->
+ <string name="thermal_shutdown_message">Your phone is now running normally.\nTap for more info</string>
+ <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=500] -->
<string name="thermal_shutdown_dialog_message">Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n\t&#8226; Use resource-intensive apps (such as gaming, video, or navigation apps)\n\t&#8226; Download or upload large files\n\t&#8226; Use your phone in high temperatures</string>
+ <!-- Text help link for care instructions for overheating devices [CHAR LIMIT=40] -->
+ <string name="thermal_shutdown_dialog_help_text">See care steps</string>
+ <!-- URL for care instructions for overheating devices -->
+ <string name="thermal_shutdown_dialog_help_url" translatable="false"></string>
<!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] -->
<string name="high_temp_title">Phone is getting warm</string>
- <!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=100] -->
- <string name="high_temp_notif_message">Some features limited while phone cools down</string>
- <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] -->
+ <!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=120] -->
+ <string name="high_temp_notif_message">Some features limited while phone cools down.\nTap for more info</string>
+ <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=350] -->
<string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string>
+ <!-- Text help link for care instructions for overheating devices [CHAR LIMIT=40] -->
+ <string name="high_temp_dialog_help_text">See care steps</string>
+ <!-- URL for care instructions for overheating devices -->
+ <string name="high_temp_dialog_help_url" translatable="false"></string>
+
<!-- Title for alarm dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=30] -->
<string name="high_temp_alarm_title">Unplug charger</string>
<!-- Text body for dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=300] -->
@@ -2626,6 +2596,27 @@
app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
<string name="heap_dump_tile_name">Dump SysUI Heap</string>
+ <!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
+ <string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
+
+ <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
+ <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
+
+ <!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
+ <string name="ongoing_privacy_dialog_separator">,\u0020</string>
+
+ <!-- Separator for types, before last type. Include spaces before and after if needed [CHAR LIMIT=10] -->
+ <string name="ongoing_privacy_dialog_last_separator">\u0020and\u0020</string>
+
+ <!-- Text for camera app op [CHAR LIMIT=20]-->
+ <string name="privacy_type_camera">camera</string>
+
+ <!-- Text for location app op [CHAR LIMIT=20]-->
+ <string name="privacy_type_location">location</string>
+
+ <!-- Text for microphone app op [CHAR LIMIT=20]-->
+ <string name="privacy_type_microphone">microphone</string>
+
<!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] -->
<string name="sensor_privacy_mode">Sensors off</string>
@@ -2840,4 +2831,11 @@
<string name="controls_menu_add">Add controls</string>
<!-- Controls menu, edit [CHAR_LIMIT=30] -->
<string name="controls_menu_edit">Edit controls</string>
+
+ <!-- Device-specific path to the node for enabling/disabling the high-brightness mode -->
+ <string name="udfps_hbm_sysfs_path" translatable="false"></string>
+ <!-- Device-specific payload for enabling the high-brightness mode -->
+ <string name="udfps_hbm_enable_command" translatable="false"></string>
+ <!-- Device-specific payload for disabling the high-brightness mode -->
+ <string name="udfps_hbm_disable_command" translatable="false"></string>
</resources>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 6d61ff989cb1..5d7f08eef0a1 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -17,21 +17,6 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Picture-in-Picture (PIP) notification -->
- <!-- Title for the notification channel for TV PIP controls. [CHAR LIMIT=NONE] -->
- <string name="notification_channel_tv_pip">Picture-in-Picture</string>
- <!-- Title of the picture-in-picture (PIP) notification title
- when the media doesn't have title [CHAR LIMIT=NONE] -->
- <string name="pip_notification_unknown_title">(No title program)</string>
-
- <!-- Picture-in-Picture (PIP) menu -->
- <eat-comment />
- <!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=30] -->
- <string name="pip_close">Close PIP</string>
- <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
- <string name="pip_fullscreen">Full screen</string>
-
<!-- Title and subtitle for AudioRecordingIndicator -->
<string name="mic_active">Microphone Active</string>
<string name="app_accessed_mic">%1$s accessed your microphone</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 68c2a38f53c3..9e5b94ee855c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -604,31 +604,6 @@
<item name="android:colorBackground">?android:attr/colorSecondary</item>
</style>
- <!-- Used to style rotate suggestion button AVD animations -->
- <style name="RotateButtonCCWStart0">
- <item name="rotateButtonStartAngle">0</item>
- <item name="rotateButtonEndAngle">-90</item>
- <item name="rotateButtonScaleX">1</item>
- </style>
-
- <style name="RotateButtonCCWStart90">
- <item name="rotateButtonStartAngle">90</item>
- <item name="rotateButtonEndAngle">0</item>
- <item name="rotateButtonScaleX">1</item>
- </style>
-
- <style name="RotateButtonCWStart0">
- <item name="rotateButtonStartAngle">0</item>
- <item name="rotateButtonEndAngle">90</item>
- <item name="rotateButtonScaleX">-1</item>
- </style>
-
- <style name="RotateButtonCWStart90">
- <item name="rotateButtonStartAngle">90</item>
- <item name="rotateButtonEndAngle">180</item>
- <item name="rotateButtonScaleX">-1</item>
- </style>
-
<style name="MediaPlayer.Button" parent="@android:style/Widget.Material.Button.Borderless.Small">
<item name="android:background">@drawable/qs_media_light_source</item>
<item name="android:tint">@android:color/white</item>
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
index 811e0e351bd3..ee958f28c51b 100644
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_collapsed.xml
@@ -19,11 +19,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<Constraint
android:id="@+id/icon"
- android:layout_width="16dp"
- android:layout_height="16dp"
+ android:layout_width="@dimen/qs_media_icon_size"
+ android:layout_height="@dimen/qs_media_icon_size"
android:layout_marginStart="18dp"
- android:layout_marginTop="22dp"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toTopOf="@id/app_name"
+ app:layout_constraintBottom_toBottomOf="@id/app_name"
app:layout_constraintStart_toStartOf="parent"
/>
@@ -31,36 +31,43 @@
android:id="@+id/app_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginEnd="10dp"
+ android:layout_marginEnd="@dimen/qs_center_guideline_padding"
android:layout_marginStart="10dp"
android:layout_marginTop="20dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/icon"
- app:layout_constraintEnd_toStartOf="@id/media_seamless_barrier"
+ app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
app:layout_constraintHorizontal_bias="0"
/>
<Constraint
android:id="@+id/media_seamless"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
+ app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="60dp"
+ app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
+ app:layout_constraintHorizontal_bias="1"
android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginStart="@dimen/qs_center_guideline_padding"
/>
<Constraint
android:id="@+id/media_seamless_fallback"
android:layout_width="@dimen/qs_seamless_fallback_icon_size"
android:layout_height="@dimen/qs_seamless_fallback_icon_size"
- android:layout_marginTop="@dimen/qs_seamless_fallback_top_margin"
android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
+ android:layout_marginStart="@dimen/qs_center_guideline_padding"
android:alpha="0.5"
android:visibility="gone"
+ app:layout_constraintHorizontal_bias="1"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toTopOf="@id/app_name"
+ app:layout_constraintBottom_toBottomOf="@id/app_name"
+ app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
/>
<Constraint
@@ -78,10 +85,11 @@
<!-- Song name -->
<Constraint
android:id="@+id/header_title"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="17dp"
android:layout_marginStart="16dp"
+ app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@id/app_name"
app:layout_constraintBottom_toTopOf="@id/header_artist"
app:layout_constraintStart_toEndOf="@id/album_art"
@@ -91,10 +99,11 @@
<!-- Artist name -->
<Constraint
android:id="@+id/header_artist"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:layout_marginBottom="24dp"
+ app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@id/header_title"
app:layout_constraintStart_toStartOf="@id/header_title"
app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
@@ -128,27 +137,6 @@
/>
<Constraint
- android:id="@+id/media_action_barrier"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:orientation="vertical"
- app:layout_constraintTop_toTopOf="parent"
- app:barrierDirection="start"
- app:constraint_referenced_ids="media_action_guidebox,action0,action1,action2,action3,action4"
- />
-
- <Constraint
- android:id="@+id/media_action_guidebox"
- android:layout_width="0dp"
- android:layout_height="48dp"
- android:layout_marginTop="18dp"
- android:visibility="invisible"
- app:layout_constraintTop_toBottomOf="@id/app_name"
- app:layout_constraintStart_toEndOf="@id/header_title"
- app:layout_constraintEnd_toEndOf="parent"
- />
-
- <Constraint
android:id="@+id/action0"
android:layout_width="48dp"
android:layout_height="48dp"
@@ -158,8 +146,9 @@
android:visibility="gone"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintTop_toBottomOf="@id/app_name"
- app:layout_constraintLeft_toLeftOf="@id/media_action_guidebox"
+ app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/action1"
+ app:layout_constraintHorizontal_bias="1"
>
</Constraint>
@@ -213,7 +202,8 @@
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintTop_toBottomOf="@id/app_name"
app:layout_constraintLeft_toRightOf="@id/action3"
- app:layout_constraintRight_toRightOf="@id/media_action_guidebox"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintHorizontal_bias="0"
>
</Constraint>
</ConstraintSet>
diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml
index 8432abcc16cb..d5a02c2d39d5 100644
--- a/packages/SystemUI/res/xml/media_expanded.xml
+++ b/packages/SystemUI/res/xml/media_expanded.xml
@@ -19,11 +19,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<Constraint
android:id="@+id/icon"
- android:layout_width="16dp"
- android:layout_height="16dp"
+ android:layout_width="@dimen/qs_media_icon_size"
+ android:layout_height="@dimen/qs_media_icon_size"
android:layout_marginStart="18dp"
- android:layout_marginTop="22dp"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toTopOf="@id/app_name"
+ app:layout_constraintBottom_toBottomOf="@id/app_name"
app:layout_constraintStart_toStartOf="parent"
/>
@@ -31,36 +31,43 @@
android:id="@+id/app_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginEnd="10dp"
+ android:layout_marginEnd="@dimen/qs_center_guideline_padding"
android:layout_marginStart="10dp"
android:layout_marginTop="20dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/icon"
- app:layout_constraintEnd_toStartOf="@id/media_seamless_barrier"
+ app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
app:layout_constraintHorizontal_bias="0"
/>
<Constraint
android:id="@+id/media_seamless"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="60dp"
android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginStart="@dimen/qs_center_guideline_padding"
/>
<Constraint
android:id="@+id/media_seamless_fallback"
android:layout_width="@dimen/qs_seamless_fallback_icon_size"
android:layout_height="@dimen/qs_seamless_fallback_icon_size"
- android:layout_marginTop="@dimen/qs_seamless_fallback_top_margin"
android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
+ android:layout_marginStart="@dimen/qs_center_guideline_padding"
android:alpha="0.5"
android:visibility="gone"
+ app:layout_constraintTop_toTopOf="@id/app_name"
+ app:layout_constraintBottom_toBottomOf="@id/app_name"
+ app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintHorizontal_bias="1"
/>
<Constraint
@@ -76,11 +83,12 @@
<!-- Song name -->
<Constraint
android:id="@+id/header_title"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
android:layout_marginTop="17dp"
android:layout_marginStart="16dp"
+ app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@+id/app_name"
app:layout_constraintStart_toEndOf="@id/album_art"
app:layout_constraintEnd_toEndOf="parent"
@@ -89,10 +97,11 @@
<!-- Artist name -->
<Constraint
android:id="@+id/header_artist"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
android:layout_marginTop="3dp"
+ app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@id/header_title"
app:layout_constraintStart_toStartOf="@id/header_title"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index ee2d001866ce..9d3620f34186 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -25,6 +25,7 @@ import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.util.Log;
@@ -62,10 +63,12 @@ public class ThumbnailData {
public ThumbnailData(TaskSnapshot snapshot) {
final HardwareBuffer buffer = snapshot.getHardwareBuffer();
- if (buffer != null && (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
+ if (buffer == null || (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
// TODO(b/157562905): Workaround for a crash when we get a snapshot without this state
- Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE");
- thumbnail = Bitmap.createBitmap(buffer.getWidth(), buffer.getHeight(), ARGB_8888);
+ Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: "
+ + buffer);
+ Point taskSize = snapshot.getTaskSize();
+ thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888);
thumbnail.eraseColor(Color.BLACK);
} else {
thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
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 4794847d8be1..2091baaaf8a1 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,6 +16,7 @@
package com.android.systemui.shared.system;
+import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.view.DisplayInfo;
@@ -66,7 +67,7 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
}
@Override
- public void onActionsChanged(ParceledListSlice actions) {
+ public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
for (PinnedStackListener listener : mListeners) {
listener.onActionsChanged(actions);
}
@@ -111,7 +112,7 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
- public void onActionsChanged(ParceledListSlice actions) {}
+ public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {}
public void onActivityHidden(ComponentName componentName) {}
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 82e6251a4484..2985a61dec9e 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
@@ -46,6 +46,7 @@ public class SyncRtSurfaceTransactionApplierCompat {
public static final int FLAG_CORNER_RADIUS = 1 << 4;
public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5;
public static final int FLAG_VISIBILITY = 1 << 6;
+ public static final int FLAG_RELATIVE_LAYER = 1 << 7;
private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
@@ -192,6 +193,8 @@ public class SyncRtSurfaceTransactionApplierCompat {
Matrix matrix;
Rect windowCrop;
int layer;
+ SurfaceControl relativeTo;
+ int relativeLayer;
boolean visible;
/**
@@ -249,6 +252,18 @@ public class SyncRtSurfaceTransactionApplierCompat {
}
/**
+ * @param relativeTo The surface that's set relative layer to.
+ * @param relativeLayer The relative layer.
+ * @return this Builder
+ */
+ public Builder withRelativeLayerTo(SurfaceControl relativeTo, int relativeLayer) {
+ this.relativeTo = relativeTo;
+ this.relativeLayer = relativeLayer;
+ flags |= FLAG_RELATIVE_LAYER;
+ return this;
+ }
+
+ /**
* @param radius the Radius for rounded corners to apply to the surface.
* @return this Builder
*/
@@ -283,7 +298,7 @@ public class SyncRtSurfaceTransactionApplierCompat {
*/
public SurfaceParams build() {
return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer,
- cornerRadius, backgroundBlurRadius, visible);
+ relativeTo, relativeLayer, cornerRadius, backgroundBlurRadius, visible);
}
}
@@ -297,21 +312,25 @@ public class SyncRtSurfaceTransactionApplierCompat {
* @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) {
+ Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer,
+ float cornerRadius) {
this(surface.mSurfaceControl,
FLAG_ALL & ~(FLAG_VISIBILITY | FLAG_BACKGROUND_BLUR_RADIUS), alpha,
- matrix, windowCrop, layer, cornerRadius, 0 /* backgroundBlurRadius */, true);
+ matrix, windowCrop, layer, relativeTo, relativeLayer, cornerRadius,
+ 0 /* backgroundBlurRadius */, true);
}
private SurfaceParams(SurfaceControl surface, int flags, float alpha, Matrix matrix,
- Rect windowCrop, int layer, float cornerRadius, int backgroundBlurRadius,
- boolean visible) {
+ Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer,
+ float cornerRadius, int backgroundBlurRadius, boolean visible) {
this.flags = flags;
this.surface = surface;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
this.windowCrop = windowCrop != null ? new Rect(windowCrop) : null;
this.layer = layer;
+ this.relativeTo = relativeTo;
+ this.relativeLayer = relativeLayer;
this.cornerRadius = cornerRadius;
this.backgroundBlurRadius = backgroundBlurRadius;
this.visible = visible;
@@ -327,6 +346,8 @@ public class SyncRtSurfaceTransactionApplierCompat {
public final Matrix matrix;
public final Rect windowCrop;
public final int layer;
+ public final SurfaceControl relativeTo;
+ public final int relativeLayer;
public final boolean visible;
public void applyTo(SurfaceControl.Transaction t) {
@@ -355,6 +376,9 @@ public class SyncRtSurfaceTransactionApplierCompat {
t.hide(surface);
}
}
+ if ((flags & FLAG_RELATIVE_LAYER) != 0) {
+ t.setRelativeLayer(surface, relativeTo, relativeLayer);
+ }
}
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index b966f9356849..255fffdb3291 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -114,6 +114,11 @@ public class TransactionCompat {
t.deferTransactionUntil(surfaceControl, barrier, frameNumber);
}
+ public static void setRelativeLayer(Transaction t, SurfaceControl surfaceControl,
+ SurfaceControl relativeTo, int z) {
+ t.setRelativeLayer(surfaceControl, relativeTo, z);
+ }
+
@Deprecated
public static void setEarlyWakeup(Transaction t) {
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 65bf7e6e5025..97317cf5580f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -138,7 +138,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
case PROMPT_REASON_USER_REQUEST:
return R.string.kg_prompt_reason_user_request;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- return R.string.kg_prompt_reason_prepare_for_update_password;
+ return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index ad92f8f623e4..c4a9fcb45284 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -440,8 +440,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request);
break;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- mSecurityMessageDisplay.setMessage(
- R.string.kg_prompt_reason_prepare_for_update_pattern);
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
break;
case PROMPT_REASON_NONE:
break;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 6d865ab525f3..c7f27cf8a71a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -117,7 +117,7 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
case PROMPT_REASON_USER_REQUEST:
return R.string.kg_prompt_reason_user_request;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- return R.string.kg_prompt_reason_prepare_for_update_pin;
+ return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 10dcbd6f9182..a84664ceee3d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -448,8 +448,8 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ " UpdateSim.onSimCheckResponse: "
+ " attemptsRemaining=" + result.getAttemptsRemaining());
- mStateMachine.reset();
}
+ mStateMachine.reset();
mCheckSimPukThread = null;
}
});
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ee31706c0b94..878947f6ba37 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -55,6 +55,7 @@ import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorProperties;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -177,6 +178,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final int MSG_TIMEZONE_UPDATE = 339;
private static final int MSG_USER_STOPPED = 340;
private static final int MSG_USER_REMOVED = 341;
+ private static final int MSG_KEYGUARD_GOING_AWAY = 342;
/** Biometric authentication state: Not listening. */
private static final int BIOMETRIC_STATE_STOPPED = 0;
@@ -227,6 +229,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final Context mContext;
private final boolean mIsPrimaryUser;
+ private final boolean mIsAutomotive;
private final StatusBarStateController mStatusBarStateController;
HashMap<Integer, SimData> mSimDatas = new HashMap<>();
HashMap<Integer, ServiceState> mServiceStates = new HashMap<Integer, ServiceState>();
@@ -251,7 +254,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean mDeviceProvisioned;
// Battery status
- private BatteryStatus mBatteryStatus;
+ @VisibleForTesting
+ BatteryStatus mBatteryStatus;
private StrongAuthTracker mStrongAuthTracker;
@@ -283,17 +287,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final Executor mBackgroundExecutor;
/**
- * Short delay before restarting biometric authentication after a successful try
- * This should be slightly longer than the time between on<biometric>Authenticated
- * (e.g. onFingerprintAuthenticated) and setKeyguardGoingAway(true).
+ * Short delay before restarting fingerprint authentication after a successful try. This should
+ * be slightly longer than the time between onFingerprintAuthenticated and
+ * setKeyguardGoingAway(true).
*/
- private static final int BIOMETRIC_CONTINUE_DELAY_MS = 500;
+ private static final int FINGERPRINT_CONTINUE_DELAY_MS = 500;
// If the HAL dies or is unable to authenticate, keyguard should retry after a short delay
private int mHardwareFingerprintUnavailableRetryCount = 0;
private int mHardwareFaceUnavailableRetryCount = 0;
private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
- private static final int HAL_ERROR_RETRY_MAX = 10;
+ private static final int HAL_ERROR_RETRY_MAX = 20;
private final Runnable mCancelNotReceived = new Runnable() {
@Override
@@ -531,7 +535,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
- * Updates KeyguardUpdateMonitor's internal state to know if keyguard is goingAway
+ * Updates KeyguardUpdateMonitor's internal state to know if keyguard is going away.
*/
public void setKeyguardGoingAway(boolean goingAway) {
mKeyguardGoingAway = goingAway;
@@ -595,7 +599,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
- BIOMETRIC_CONTINUE_DELAY_MS);
+ FINGERPRINT_CONTINUE_DELAY_MS);
// Only authenticate fingerprint once when assistant is visible
mAssistantVisible = false;
@@ -680,7 +684,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
public void run() {
Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " +
mHardwareFingerprintUnavailableRetryCount);
- updateFingerprintListeningState();
+ if (mFpm.isHardwareDetected()) {
+ updateFingerprintListeningState();
+ } else if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
+ mHardwareFingerprintUnavailableRetryCount++;
+ mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
+ }
}
};
@@ -702,11 +711,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
- if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
- mHardwareFingerprintUnavailableRetryCount++;
- mHandler.removeCallbacks(mRetryFingerprintAuthentication);
- mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
- }
+ mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
@@ -777,9 +782,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
- BIOMETRIC_CONTINUE_DELAY_MS);
-
// Only authenticate face once when assistant is visible
mAssistantVisible = false;
@@ -1069,6 +1071,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
}
+ private boolean isEncryptedOrLockdown(int userId) {
+ final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId);
+ final boolean isLockDown =
+ containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+ || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ return isEncrypted || isLockDown;
+ }
+
public boolean userNeedsStrongAuth() {
return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser())
!= LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
@@ -1232,7 +1243,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final FingerprintManager.LockoutResetCallback mFingerprintLockoutResetCallback
= new FingerprintManager.LockoutResetCallback() {
@Override
- public void onLockoutReset() {
+ public void onLockoutReset(int sensorId) {
handleFingerprintLockoutReset();
}
};
@@ -1240,12 +1251,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback
= new FaceManager.LockoutResetCallback() {
@Override
- public void onLockoutReset() {
+ public void onLockoutReset(int sensorId) {
handleFaceLockoutReset();
}
};
- private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
+ private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback
+ = (sensorId, userId, isStrongBiometric) -> {
+ // Trigger the fingerprint success path so the bouncer can be shown
+ handleFingerprintAuthenticated(userId, isStrongBiometric);
+ };
+
+ private final FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
= new AuthenticationCallback() {
@Override
@@ -1276,6 +1293,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
};
+ private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
+ = (sensorId, userId, isStrongBiometric) -> {
+ // Trigger the face success path so the bouncer can be shown
+ handleFaceAuthenticated(userId, isStrongBiometric);
+ };
+
@VisibleForTesting
FaceManager.AuthenticationCallback mFaceAuthenticationCallback
= new FaceManager.AuthenticationCallback() {
@@ -1312,6 +1335,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private CancellationSignal mFaceCancelSignal;
private FingerprintManager mFpm;
private FaceManager mFaceManager;
+ private List<FaceSensorProperties> mFaceSensorProperties;
private boolean mFingerprintLockedOut;
private TelephonyManager mTelephonyManager;
@@ -1521,6 +1545,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mUserTrustIsUsuallyManaged.delete(userId);
}
+ private void handleKeyguardGoingAway(boolean goingAway) {
+ Assert.isMainThread();
+ setKeyguardGoingAway(goingAway);
+ }
+
@VisibleForTesting
protected void setStrongAuthTracker(@NonNull StrongAuthTracker tracker) {
if (mStrongAuthTracker != null) {
@@ -1661,6 +1690,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
case MSG_TELEPHONY_CAPABLE:
updateTelephonyCapable((boolean) msg.obj);
break;
+ case MSG_KEYGUARD_GOING_AWAY:
+ handleKeyguardGoingAway((boolean) msg.obj);
+ break;
default:
super.handleMessage(msg);
break;
@@ -1698,6 +1730,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
.getServiceStateForSubscriber(subId);
mHandler.sendMessage(
mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
+
+ // Get initial state. Relying on Sticky behavior until API for getting info.
+ if (mBatteryStatus == null) {
+ Intent intent = mContext.registerReceiver(
+ null,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+ );
+ if (intent != null && mBatteryStatus == null) {
+ mBroadcastReceiver.onReceive(mContext, intent);
+ }
+ }
});
mHandler.post(this::registerRingerTracker);
@@ -1734,6 +1777,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
mFaceManager = (FaceManager) context.getSystemService(Context.FACE_SERVICE);
+ mFaceSensorProperties = mFaceManager.getSensorProperties();
}
if (mFpm != null || mFaceManager != null) {
@@ -1749,6 +1793,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFaceManager.addLockoutResetCallback(mFaceLockoutResetCallback);
}
+ mIsAutomotive = isAutomotive();
+
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
mUserManager = context.getSystemService(UserManager.class);
mIsPrimaryUser = mUserManager.isPrimaryUser();
@@ -1817,7 +1863,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
return;
}
- mHandler.removeCallbacks(mRetryFingerprintAuthentication);
+
boolean shouldListenForFingerprint = shouldListenForFingerprint();
boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
|| mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
@@ -2026,8 +2072,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
- mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
- null, userId);
+
+ if (isEncryptedOrLockdown(userId)) {
+ mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback,
+ userId, null /* surface */);
+ } else {
+ mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
+ mFingerprintAuthenticationCallback, null /* handler */, userId);
+ }
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
}
@@ -2044,8 +2096,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFaceCancelSignal.cancel();
}
mFaceCancelSignal = new CancellationSignal();
- mFaceManager.authenticate(null, mFaceCancelSignal, 0,
- mFaceAuthenticationCallback, null, userId);
+
+ // This would need to be updated for multi-sensor devices
+ final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
+ && mFaceSensorProperties.get(0).supportsFaceDetection;
+ if (isEncryptedOrLockdown(userId) && supportsFaceDetection) {
+ mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
+ } else {
+ mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal,
+ mFaceAuthenticationCallback, null /* handler */, userId);
+ }
setFaceRunningState(BIOMETRIC_STATE_RUNNING);
}
}
@@ -2063,7 +2123,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean isUnlockWithFingerprintPossible(int userId) {
return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
- && mFpm.getEnrolledFingerprints(userId).size() > 0;
+ && mFpm.hasEnrolledTemplates(userId);
}
private boolean isUnlockWithFacePossible(int userId) {
@@ -2463,6 +2523,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
.addCategory(Intent.CATEGORY_HOME);
ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(homeIntent,
0 /* flags */);
+
+ // TODO(b/160971249): Replace in the future by resolving activity as user.
+ if (resolveInfo == null && mIsAutomotive) {
+ Log.w(TAG, "resolveNeedsSlowUnlockTransition: returning false since activity "
+ + "could not be resolved.");
+ return false;
+ }
+
return FALLBACK_HOME_COMPONENT.equals(resolveInfo.getComponentInfo().getComponentName());
}
@@ -2533,6 +2601,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return false;
}
+ private boolean isAutomotive() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
/**
* Remove the given observer's callback.
*
@@ -2802,6 +2874,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mHandler.sendMessage(mHandler.obtainMessage(MSG_DREAMING_STATE_CHANGED, 0, 0));
}
+ /**
+ * Sends a message to update the keyguard going away state on the main thread.
+ *
+ * @param goingAway Whether the keyguard is going away.
+ */
+ public void dispatchKeyguardGoingAway(boolean goingAway) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_KEYGUARD_GOING_AWAY, goingAway));
+ }
+
public boolean isDeviceInteractive() {
return mDeviceInteractive;
}
@@ -2960,5 +3041,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" " + time + " " + model.toString());
}
}
+ if (mIsAutomotive) {
+ pw.println(" Running on Automotive build");
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index a46ab3a9e35b..58698151b655 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui;
-import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
-import static android.app.StatusBarManager.DISABLE_NONE;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -56,7 +54,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -64,7 +61,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.Utils.DisableStateTracker;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -137,11 +133,6 @@ public class BatteryMeterView extends LinearLayout implements
mShowPercentAvailable = context.getResources().getBoolean(
com.android.internal.R.bool.config_battery_percentage_setting_available);
-
- addOnAttachStateChangeListener(
- new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS,
- Dependency.get(CommandQueue.class)));
-
setupLayoutTransition();
mSlotBattery = context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 02d2b8e4ef0f..58f8c07ad7c9 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -54,6 +54,7 @@ import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenrecord.RecordingController;
@@ -122,17 +123,17 @@ import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
import com.android.systemui.util.sensors.AsyncSensorManager;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
+import javax.inject.Singleton;
import dagger.Lazy;
-import dagger.Subcomponent;
/**
* Class to handle ugly dependencies throughout sysui until we determine the
@@ -149,6 +150,7 @@ import dagger.Subcomponent;
* they have no clients they should not have any registered resources like bound
* services, registered receivers, etc.
*/
+@Singleton
public class Dependency {
/**
* Key for getting a the main looper.
@@ -294,6 +296,7 @@ public class Dependency {
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
@Inject Lazy<AutoHideController> mAutoHideController;
@Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
+ @Inject Lazy<PrivacyItemController> mPrivacyItemController;
@Inject @Background Lazy<Looper> mBgLooper;
@Inject @Background Lazy<Handler> mBgHandler;
@Inject @Main Lazy<Looper> mMainLooper;
@@ -491,6 +494,7 @@ public class Dependency {
mProviders.put(ForegroundServiceNotificationListener.class,
mForegroundServiceNotificationListener::get);
mProviders.put(ClockManager.class, mClockManager::get);
+ mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
@@ -519,7 +523,12 @@ public class Dependency {
mProviders.put(RecordingController.class, mRecordingController::get);
mProviders.put(Divider.class, mDivider::get);
- sDependency = this;
+ Dependency.setInstance(this);
+ }
+
+ @VisibleForTesting
+ public static void setInstance(Dependency dependency) {
+ sDependency = dependency;
}
protected final <T> T getDependency(Class<T> cls) {
@@ -546,7 +555,7 @@ public class Dependency {
}
@VisibleForTesting
- protected <T> T createDependency(Object cls) {
+ public <T> T createDependency(Object cls) {
Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);
@SuppressWarnings("unchecked")
@@ -635,9 +644,4 @@ public class Dependency {
return mDisplayName;
}
}
-
- @Subcomponent
- public interface DependencyInjector {
- void createSystemUI(Dependency dependency);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 6c06553a84a6..ad11d71eb132 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -62,6 +62,7 @@ import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayCutout.BoundsPosition;
import android.view.DisplayInfo;
@@ -820,6 +821,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private static final float HIDDEN_CAMERA_PROTECTION_SCALE = 0.5f;
+ private Display.Mode mDisplayMode = null;
private final DisplayInfo mInfo = new DisplayInfo();
private final Paint mPaint = new Paint();
private final List<Rect> mBounds = new ArrayList();
@@ -904,11 +906,33 @@ public class ScreenDecorations extends SystemUI implements Tunable {
@Override
public void onDisplayChanged(int displayId) {
+ Display.Mode oldMode = mDisplayMode;
+ mDisplayMode = getDisplay().getMode();
+
+ // Display mode hasn't meaningfully changed, we can ignore it
+ if (!modeChanged(oldMode, mDisplayMode)) {
+ return;
+ }
+
if (displayId == getDisplay().getDisplayId()) {
update();
}
}
+ private boolean modeChanged(Display.Mode oldMode, Display.Mode newMode) {
+ if (oldMode == null) {
+ return true;
+ }
+
+ boolean changed = false;
+ changed |= oldMode.getPhysicalHeight() != newMode.getPhysicalHeight();
+ changed |= oldMode.getPhysicalWidth() != newMode.getPhysicalWidth();
+ // We purposely ignore refresh rate and id changes here, because we don't need to
+ // invalidate for those, and they can trigger the refresh rate to increase
+
+ return changed;
+ }
+
public void setRotation(int rotation) {
mRotation = rotation;
update();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 5674fdd3bb36..1a15c0aae8c1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,7 +16,6 @@
package com.android.systemui;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
@@ -30,7 +29,6 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.dagger.DaggerSystemUIRootComponent;
-import com.android.systemui.dagger.DependencyProvider;
import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.FalsingManager;
@@ -48,9 +46,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.concurrent.Executor;
-import dagger.Module;
-import dagger.Provides;
-
/**
* Class factory to provide customizable SystemUI components.
*/
@@ -97,24 +92,13 @@ 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 dependency = new Dependency();
- mRootComponent.createDependency().createSystemUI(dependency);
+ Dependency dependency = mRootComponent.createDependency();
dependency.start();
}
- protected void initWithRootComponent(@NonNull SystemUIRootComponent rootComponent) {
- if (mRootComponent != null) {
- throw new RuntimeException("Root component can be set only once.");
- }
-
- mRootComponent = rootComponent;
- }
-
protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
return DaggerSystemUIRootComponent.builder()
- .dependencyProvider(new DependencyProvider())
- .contextHolder(new ContextHolder(context))
+ .context(context)
.build();
}
@@ -168,18 +152,4 @@ public class SystemUIFactory {
Dependency.get(DozeParameters.class),
Dependency.get(BubbleController.class));
}
-
- @Module
- public static class ContextHolder {
- private Context mContext;
-
- public ContextHolder(Context context) {
- mContext = context;
- }
-
- @Provides
- public Context provideContext() {
- return mContext;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
new file mode 100644
index 000000000000..769a344eedac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManager;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Supplies the instance with given display Id. It generates a new instance if the corresponding
+ * one is not existed. It should run in single thread to avoid race conditions.
+ *
+ * @param <T> the type of results supplied by {@link #createInstance(Display)}.
+ */
+abstract class DisplayIdIndexSupplier<T> {
+
+ private final SparseArray<T> mSparseArray = new SparseArray<>();
+ private final DisplayManager mDisplayManager;
+
+ /**
+ * @param displayManager DisplayManager
+ */
+ DisplayIdIndexSupplier(DisplayManager displayManager) {
+ mDisplayManager = displayManager;
+ }
+
+ /**
+ * @param displayId the logical display Id
+ * @return {@code null} if the given display id is invalid
+ */
+ @Nullable
+ public T get(int displayId) {
+ T instance = mSparseArray.get(displayId);
+ if (instance != null) {
+ return instance;
+ }
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ return null;
+ }
+ instance = createInstance(display);
+ mSparseArray.put(displayId, instance);
+ return instance;
+ }
+
+ @NonNull
+ protected abstract T createInstance(Display display);
+
+ /**
+ * Removes the instance with given display Id.
+ *
+ * @param displayId the logical display id
+ */
+ public void remove(int displayId) {
+ mSparseArray.remove(displayId);
+ }
+
+ /**
+ * Clears all elements.
+ */
+ public void clear() {
+ mSparseArray.clear();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 332a00d1d4e7..68a0a65ef50f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.PixelFormat;
import android.provider.Settings;
@@ -23,11 +24,13 @@ import android.view.Gravity;
import android.view.WindowManager;
import android.widget.ImageView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
/**
* Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
* {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
+ * The button UI would automatically be dismissed after displaying for a period of time.
*/
class MagnificationModeSwitch {
@@ -42,23 +45,36 @@ class MagnificationModeSwitch {
private boolean mIsVisible = false;
MagnificationModeSwitch(Context context) {
+ this(context, createView(context));
+ }
+
+ @VisibleForTesting
+ MagnificationModeSwitch(Context context, @NonNull ImageView imageView) {
mContext = context;
mWindowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
mParams = createLayoutParams();
- mImageView = createView(mContext, mMagnificationMode);
+ mImageView = imageView;
+ applyResourcesValues();
mImageView.setOnClickListener(
view -> {
removeButton();
toggleMagnificationMode();
});
+ mImageView.setImageResource(getIconResId(mMagnificationMode));
+ }
+
+ private void applyResourcesValues() {
+ final int padding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.magnification_switch_button_padding);
+ mImageView.setPadding(padding, padding, padding, padding);
}
void removeButton() {
- mImageView.animate().cancel();
if (!mIsVisible) {
return;
}
+ mImageView.animate().cancel();
mWindowManager.removeView(mImageView);
mIsVisible = false;
}
@@ -72,7 +88,7 @@ class MagnificationModeSwitch {
mWindowManager.addView(mImageView, mParams);
mIsVisible = true;
}
-
+ mImageView.setAlpha(1.0f);
// TODO(b/143852371): use accessibility timeout as a delay.
// Dismiss the magnification switch button after the button is displayed for a period of
// time.
@@ -82,30 +98,29 @@ class MagnificationModeSwitch {
.setStartDelay(START_DELAY_MS)
.setDuration(DURATION_MS)
.withEndAction(
- () -> removeButton());
+ () -> removeButton())
+ .start();
}
private void toggleMagnificationMode() {
final int newMode =
mMagnificationMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
mMagnificationMode = newMode;
+ mImageView.setImageResource(getIconResId(newMode));
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, newMode);
}
- private static ImageView createView(Context context, int mode) {
- final int padding = context.getResources().getDimensionPixelSize(
- R.dimen.magnification_switch_button_padding);
+ private static ImageView createView(Context context) {
ImageView imageView = new ImageView(context);
- imageView.setImageResource(getIconResId(mode));
imageView.setClickable(true);
imageView.setFocusable(true);
- imageView.setPadding(padding, padding, padding, padding);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
return imageView;
}
- private static int getIconResId(int mode) {
+ @VisibleForTesting
+ static int getIconResId(int mode) {
return (mode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
? R.drawable.ic_open_in_new_window
: R.drawable.ic_open_in_new_fullscreen;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index e73ff13ceac1..ffc70bcf63d0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -19,31 +19,33 @@ package com.android.systemui.accessibility;
import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
-import android.util.Log;
-import android.util.SparseArray;
import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
+
import javax.inject.Singleton;
/**
- * Class to control magnification mode switch button. Shows the button UI when both full-screen
- * and window magnification mode are capable, and when the magnification scale is changed. And
- * the button UI would automatically be dismissed after displaying for a period of time.
+ * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following
+ * conditions:
+ * <ol>
+ * <li> Both full-screen and window magnification mode are capable.</li>
+ * <li> The magnification scale is changed by a user.</li>
+ * <ol>
*/
@Singleton
public class ModeSwitchesController {
- private static final String TAG = "ModeSwitchesController";
-
- private final Context mContext;
- private final DisplayManager mDisplayManager;
-
- private final SparseArray<MagnificationModeSwitch> mDisplaysToSwitches =
- new SparseArray<>();
+ private final SwitchSupplier mSwitchSupplier;
public ModeSwitchesController(Context context) {
- mContext = context;
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mSwitchSupplier = new SwitchSupplier(context,
+ context.getSystemService(DisplayManager.class));
+ }
+
+ @VisibleForTesting
+ ModeSwitchesController(SwitchSupplier switchSupplier) {
+ mSwitchSupplier = switchSupplier;
}
/**
@@ -52,20 +54,17 @@ public class ModeSwitchesController {
*
* @param displayId The logical display id
* @param mode The magnification mode
- *
* @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
* @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
*/
@MainThread
void showButton(int displayId, int mode) {
- if (mDisplaysToSwitches.get(displayId) == null) {
- final MagnificationModeSwitch magnificationModeSwitch =
- createMagnificationSwitchController(displayId);
- if (magnificationModeSwitch == null) {
- return;
- }
+ final MagnificationModeSwitch magnificationModeSwitch =
+ mSwitchSupplier.get(displayId);
+ if (magnificationModeSwitch == null) {
+ return;
}
- mDisplaysToSwitches.get(displayId).showButton(mode);
+ magnificationModeSwitch.showButton(mode);
}
/**
@@ -74,30 +73,34 @@ public class ModeSwitchesController {
* @param displayId The logical display id
*/
void removeButton(int displayId) {
- if (mDisplaysToSwitches.get(displayId) == null) {
+ final MagnificationModeSwitch magnificationModeSwitch =
+ mSwitchSupplier.get(displayId);
+ if (magnificationModeSwitch == null) {
return;
}
- mDisplaysToSwitches.get(displayId).removeButton();
+ magnificationModeSwitch.removeButton();
}
- private MagnificationModeSwitch createMagnificationSwitchController(int displayId) {
- if (mDisplayManager.getDisplay(displayId) == null) {
- Log.w(TAG, "createMagnificationSwitchController displayId is invalid.");
- return null;
+ @VisibleForTesting
+ static class SwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {
+
+ private final Context mContext;
+
+ /**
+ * @param context Context
+ * @param displayManager DisplayManager
+ */
+ SwitchSupplier(Context context, DisplayManager displayManager) {
+ super(displayManager);
+ mContext = context;
}
- final MagnificationModeSwitch
- magnificationModeSwitch = new MagnificationModeSwitch(
- getDisplayContext(displayId));
- mDisplaysToSwitches.put(displayId, magnificationModeSwitch);
- return magnificationModeSwitch;
- }
- private Context getDisplayContext(int displayId) {
- final Display display = mDisplayManager.getDisplay(displayId);
- final Context context = (displayId == Display.DEFAULT_DISPLAY)
- ? mContext
- : mContext.createDisplayContext(display);
- return context;
+ @Override
+ protected MagnificationModeSwitch createInstance(Display display) {
+ final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY)
+ ? mContext
+ : mContext.createDisplayContext(display);
+ return new MagnificationModeSwitch(context);
+ }
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 2d28b143796c..426e400c9fb9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -30,6 +30,7 @@ import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.CommandQueue;
@@ -94,7 +95,9 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
//TODO: b/144080869 support multi-display.
if (mWindowMagnificationController == null) {
mWindowMagnificationController = new WindowMagnificationController(mContext,
- mHandler, null,
+ mHandler,
+ new SfVsyncFrameCallbackProvider(),
+ null,
this);
}
mWindowMagnificationController.enableWindowMagnification(scale, centerX, centerY);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 18e777838391..8b6581ff1856 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -33,6 +33,7 @@ import android.graphics.Region;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import android.view.Choreographer;
import android.view.Display;
import android.view.Gravity;
import android.view.IWindow;
@@ -47,6 +48,7 @@ import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -98,15 +100,27 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
// The boundary of magnification frame.
private final Rect mMagnificationFrameBoundary = new Rect();
+ private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+ private final Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback =
+ l -> {
+ if (mMirrorView != null) {
+ final Rect sourceBounds = getSourceBounds(mMagnificationFrame, mScale);
+ mTransaction.setGeometry(mMirrorSurface, sourceBounds, mTmpRect,
+ Surface.ROTATION_0).apply();
+ }
+ };
+
@Nullable
private MirrorWindowControl mMirrorWindowControl;
WindowMagnificationController(Context context,
@NonNull Handler handler,
+ SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
MirrorWindowControl mirrorWindowControl,
@NonNull WindowMagnifierCallback callback) {
mContext = context;
mHandler = handler;
+ mSfVsyncFrameProvider = sfVsyncFrameProvider;
mWindowMagnifierCallback = callback;
Display display = mContext.getDisplay();
display.getRealSize(mDisplaySize);
@@ -244,8 +258,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
params.gravity = Gravity.TOP | Gravity.LEFT;
- params.x = mMagnificationFrame.left;
- params.y = mMagnificationFrame.top;
+ params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
+ params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
params.setTitle(mContext.getString(R.string.magnification_window_title));
@@ -316,7 +330,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
.reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl());
modifyWindowMagnification(mTransaction);
- mTransaction.apply();
}
private void addDragTouchListeners() {
@@ -337,14 +350,13 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
* Modifies the placement of the mirrored content when the position of mMirrorView is updated.
*/
private void modifyWindowMagnification(SurfaceControl.Transaction t) {
- Rect sourceBounds = getSourceBounds(mMagnificationFrame, mScale);
// The final destination for the magnification surface should be at 0,0 since the
// ViewRootImpl's position will change
mTmpRect.set(0, 0, mMagnificationFrame.width(), mMagnificationFrame.height());
updateMirrorViewLayout();
- t.setGeometry(mMirrorSurface, sourceBounds, mTmpRect, Surface.ROTATION_0);
+ mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback);
}
/**
@@ -505,7 +517,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
showControls();
} else {
modifyWindowMagnification(mTransaction);
- mTransaction.apply();
}
}
@@ -535,7 +546,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
if (updateMagnificationFramePosition((int) offsetX, (int) offsetY)) {
modifyWindowMagnification(mTransaction);
- mTransaction.apply();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 941de2dc63ec..4df66602bb7e 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -18,18 +18,22 @@ package com.android.systemui.appops;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.WorkerThread;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.Assert;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -57,12 +61,12 @@ public class AppOpsControllerImpl implements AppOpsController,
private static final long NOTED_OP_TIME_DELAY_MS = 5000;
private static final String TAG = "AppOpsControllerImpl";
private static final boolean DEBUG = false;
- private final Context mContext;
private final AppOpsManager mAppOps;
private H mBGHandler;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
- private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
+ private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
+ private final PermissionFlagsCache mFlagsCache;
private boolean mListening;
@GuardedBy("mActiveItems")
@@ -71,6 +75,7 @@ public class AppOpsControllerImpl implements AppOpsController,
private final List<AppOpItem> mNotedItems = new ArrayList<>();
protected static final int[] OPS = new int[] {
+ AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
AppOpsManager.OP_CAMERA,
AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
AppOpsManager.OP_RECORD_AUDIO,
@@ -82,9 +87,11 @@ public class AppOpsControllerImpl implements AppOpsController,
public AppOpsControllerImpl(
Context context,
@Background Looper bgLooper,
- DumpManager dumpManager) {
- mContext = context;
+ DumpManager dumpManager,
+ PermissionFlagsCache cache
+ ) {
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mFlagsCache = cache;
mBGHandler = new H(bgLooper);
final int numOps = OPS.length;
for (int i = 0; i < numOps; i++) {
@@ -131,7 +138,7 @@ public class AppOpsControllerImpl implements AppOpsController,
boolean added = false;
final int numCodes = opsCodes.length;
for (int i = 0; i < numCodes; i++) {
- if (mCallbacksByCode.containsKey(opsCodes[i])) {
+ if (mCallbacksByCode.contains(opsCodes[i])) {
mCallbacksByCode.get(opsCodes[i]).add(callback);
added = true;
} else {
@@ -155,7 +162,7 @@ public class AppOpsControllerImpl implements AppOpsController,
public void removeCallback(int[] opsCodes, AppOpsController.Callback callback) {
final int numCodes = opsCodes.length;
for (int i = 0; i < numCodes; i++) {
- if (mCallbacksByCode.containsKey(opsCodes[i])) {
+ if (mCallbacksByCode.contains(opsCodes[i])) {
mCallbacksByCode.get(opsCodes[i]).remove(callback);
}
}
@@ -231,10 +238,66 @@ public class AppOpsControllerImpl implements AppOpsController,
}
/**
+ * Does the app-op code refer to a user sensitive permission for the specified user id
+ * and package. Only user sensitive permission should be shown to the user by default.
+ *
+ * @param appOpCode The code of the app-op.
+ * @param uid The uid of the user.
+ * @param packageName The name of the package.
+ *
+ * @return {@code true} iff the app-op item is user sensitive
+ */
+ private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
+ String permission = AppOpsManager.opToPermission(appOpCode);
+ if (permission == null) {
+ return false;
+ }
+ int permFlags = mFlagsCache.getPermissionFlags(permission,
+ packageName, uid);
+ return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
+ }
+
+ /**
+ * Does the app-op item refer to an operation that should be shown to the user.
+ * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
+ * permission should be shown to the user by default.
+ *
+ * @param item The item
+ *
+ * @return {@code true} iff the app-op item should be shown to the user
+ */
+ private boolean isUserVisible(AppOpItem item) {
+ return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
+ }
+
+
+ /**
+ * Does the app-op, uid and package name, refer to an operation that should be shown to the
+ * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
+ * ops that refer to user sensitive permission should be shown to the user by default.
+ *
+ * @param item The item
+ *
+ * @return {@code true} iff the app-op for should be shown to the user
+ */
+ private boolean isUserVisible(int appOpCode, int uid, String packageName) {
+ // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
+ // which may be user senstive, so for now always show it to the user.
+ if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
+ return true;
+ }
+
+ return isUserSensitive(appOpCode, uid, packageName);
+ }
+
+ /**
* Returns a copy of the list containing all the active AppOps that the controller tracks.
*
+ * Call from a worker thread as it may perform long operations.
+ *
* @return List of active AppOps information
*/
+ @WorkerThread
public List<AppOpItem> getActiveAppOps() {
return getActiveAppOpsForUser(UserHandle.USER_ALL);
}
@@ -243,18 +306,23 @@ public class AppOpsControllerImpl implements AppOpsController,
* Returns a copy of the list containing all the active AppOps that the controller tracks, for
* a given user id.
*
+ * Call from a worker thread as it may perform long operations.
+ *
* @param userId User id to track, can be {@link UserHandle#USER_ALL}
*
* @return List of active AppOps information for that user id
*/
+ @WorkerThread
public List<AppOpItem> getActiveAppOpsForUser(int userId) {
+ Assert.isNotMainThread();
List<AppOpItem> list = new ArrayList<>();
synchronized (mActiveItems) {
final int numActiveItems = mActiveItems.size();
for (int i = 0; i < numActiveItems; i++) {
AppOpItem item = mActiveItems.get(i);
if ((userId == UserHandle.USER_ALL
- || UserHandle.getUserId(item.getUid()) == userId)) {
+ || UserHandle.getUserId(item.getUid()) == userId)
+ && isUserVisible(item)) {
list.add(item);
}
}
@@ -264,7 +332,8 @@ public class AppOpsControllerImpl implements AppOpsController,
for (int i = 0; i < numNotedItems; i++) {
AppOpItem item = mNotedItems.get(i);
if ((userId == UserHandle.USER_ALL
- || UserHandle.getUserId(item.getUid()) == userId)) {
+ || UserHandle.getUserId(item.getUid()) == userId)
+ && isUserVisible(item)) {
list.add(item);
}
}
@@ -312,7 +381,7 @@ public class AppOpsControllerImpl implements AppOpsController,
}
private void notifySuscribers(int code, int uid, String packageName, boolean active) {
- if (mCallbacksByCode.containsKey(code)) {
+ if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) {
if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
for (Callback cb: mCallbacksByCode.get(code)) {
cb.onActiveStateChanged(code, uid, packageName, active);
diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
new file mode 100644
index 000000000000..45ed78f750be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.appops
+
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import androidx.annotation.WorkerThread
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.Assert
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private data class PermissionFlagKey(
+ val permission: String,
+ val packageName: String,
+ val uid: Int
+)
+
+/**
+ * Cache for PackageManager's PermissionFlags.
+ *
+ * After a specific `{permission, package, uid}` has been requested, updates to it will be tracked,
+ * and changes to the uid will trigger new requests (in the background).
+ */
+@Singleton
+class PermissionFlagsCache @Inject constructor(
+ private val packageManager: PackageManager,
+ @Background private val executor: Executor
+) : PackageManager.OnPermissionsChangedListener {
+
+ private val permissionFlagsCache =
+ mutableMapOf<Int, MutableMap<PermissionFlagKey, Int>>()
+ private var listening = false
+
+ override fun onPermissionsChanged(uid: Int) {
+ executor.execute {
+ // Only track those that we've seen before
+ val keys = permissionFlagsCache.get(uid)
+ if (keys != null) {
+ keys.mapValuesTo(keys) {
+ getFlags(it.key)
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieve permission flags from cache or PackageManager. There parameters will be passed
+ * directly to [PackageManager].
+ *
+ * Calls to this method should be done from a background thread (though it will only be
+ * enforced if the cache is not hit).
+ */
+ @WorkerThread
+ fun getPermissionFlags(permission: String, packageName: String, uid: Int): Int {
+ if (!listening) {
+ listening = true
+ packageManager.addOnPermissionsChangeListener(this)
+ }
+ val key = PermissionFlagKey(permission, packageName, uid)
+ return permissionFlagsCache.getOrPut(uid, { mutableMapOf() }).get(key) ?: run {
+ getFlags(key).also {
+ Assert.isNotMainThread()
+ permissionFlagsCache.get(uid)?.put(key, it)
+ }
+ }
+ }
+
+ private fun getFlags(key: PermissionFlagKey): Int {
+ return packageManager.getPermissionFlags(key.permission, key.packageName,
+ UserHandle.getUserHandleForUid(key.uid))
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 81439e186d38..361ea674cead 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -37,6 +37,7 @@ import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -76,6 +77,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
private Handler mHandler = new Handler(Looper.getMainLooper());
private WindowManager mWindowManager;
+ private UdfpsController mUdfpsController;
@VisibleForTesting
IActivityTaskManager mActivityTaskManager;
@VisibleForTesting
@@ -242,6 +244,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
IActivityTaskManager getActivityTaskManager() {
return ActivityTaskManager.getService();
}
+
+ FingerprintManager getFingerprintManager(Context context) {
+ return context.getSystemService(FingerprintManager.class);
+ }
}
@Inject
@@ -261,12 +267,25 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
context.registerReceiver(mBroadcastReceiver, filter);
}
+ @SuppressWarnings("deprecation")
@Override
public void start() {
mCommandQueue.addCallback(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mActivityTaskManager = mInjector.getActivityTaskManager();
+ final FingerprintManager fpm = mInjector.getFingerprintManager(mContext);
+ if (fpm != null && fpm.isHardwareDetected()) {
+ final List<FingerprintSensorProperties> fingerprintSensorProperties =
+ fpm.getSensorProperties();
+ for (FingerprintSensorProperties props : fingerprintSensorProperties) {
+ if (props.sensorType == FingerprintSensorProperties.TYPE_UDFPS) {
+ mUdfpsController = new UdfpsController(mContext, mWindowManager);
+ break;
+ }
+ }
+ }
+
try {
mTaskStackListener = new BiometricTaskStackListener();
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
new file mode 100644
index 000000000000..739c2b155444
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
+ * and coordinates triggering of the high-brightness mode (HBM).
+ */
+class UdfpsController {
+ private static final String TAG = "UdfpsController";
+
+ private final Context mContext;
+ private final FingerprintManager mFingerprintManager;
+ private final WindowManager mWindowManager;
+ private final Handler mHandler;
+
+ private UdfpsView mView;
+ private WindowManager.LayoutParams mLayoutParams;
+ private String mHbmPath;
+ private String mHbmEnableCommand;
+ private String mHbmDisableCommand;
+ private boolean mIsOverlayShowing;
+
+ public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
+ @Override
+ public void showUdfpsOverlay() {
+ UdfpsController.this.showUdfpsOverlay();
+ }
+
+ @Override
+ public void hideUdfpsOverlay() {
+ UdfpsController.this.hideUdfpsOverlay();
+ }
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ boolean isValidTouch = mView.isValidTouch(event.getX(), event.getY(),
+ event.getPressure());
+ if (!mView.isFingerDown() && isValidTouch) {
+ onFingerDown((int) event.getX(), (int) event.getY(), event.getTouchMinor(),
+ event.getTouchMajor());
+ } else if (mView.isFingerDown() && !isValidTouch) {
+ onFingerUp();
+ }
+ return true;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ if (mView.isFingerDown()) {
+ onFingerUp();
+ }
+ return true;
+
+ default:
+ return false;
+ }
+ };
+
+ UdfpsController(Context context, WindowManager windowManager) {
+ mContext = context;
+ mFingerprintManager = context.getSystemService(FingerprintManager.class);
+ mWindowManager = windowManager;
+ mHandler = new Handler(Looper.getMainLooper());
+ start();
+ }
+
+ private void start() {
+ Log.v(TAG, "start");
+
+ Point displaySize = new Point();
+ mWindowManager.getDefaultDisplay().getRealSize(displaySize);
+ // TODO(b/160025856): move to the "dump" method.
+ Log.v(TAG, "UdfpsController | display size: " + displaySize.x + "x"
+ + displaySize.y);
+
+ mLayoutParams = new WindowManager.LayoutParams(
+ displaySize.x,
+ displaySize.y,
+ // TODO(b/152419866): Use the UDFPS window type when it becomes available.
+ WindowManager.LayoutParams.TYPE_BOOT_PROGRESS,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ mLayoutParams.setTitle(TAG);
+ mLayoutParams.windowAnimations = 0;
+
+ LinearLayout layout = new LinearLayout(mContext);
+ layout.setLayoutParams(mLayoutParams);
+ mView = (UdfpsView) LayoutInflater.from(mContext).inflate(R.layout.udfps_view, layout,
+ false);
+ mView.setOnTouchListener(mOnTouchListener);
+
+ mHbmPath = mContext.getResources().getString(R.string.udfps_hbm_sysfs_path);
+ mHbmEnableCommand = mContext.getResources().getString(R.string.udfps_hbm_enable_command);
+ mHbmDisableCommand = mContext.getResources().getString(R.string.udfps_hbm_disable_command);
+
+ mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
+ mIsOverlayShowing = false;
+ }
+
+ private void showUdfpsOverlay() {
+ mHandler.post(() -> {
+ Log.v(TAG, "showUdfpsOverlay | adding window");
+ if (!mIsOverlayShowing) {
+ try {
+ mWindowManager.addView(mView, mLayoutParams);
+ mIsOverlayShowing = true;
+ } catch (RuntimeException e) {
+ Log.e(TAG, "showUdfpsOverlay | failed to add window", e);
+ }
+ }
+ });
+ }
+
+ private void hideUdfpsOverlay() {
+ onFingerUp();
+ mHandler.post(() -> {
+ Log.v(TAG, "hideUdfpsOverlay | removing window");
+ if (mIsOverlayShowing) {
+ mWindowManager.removeView(mView);
+ mIsOverlayShowing = false;
+ }
+ });
+ }
+
+ private void onFingerDown(int x, int y, float minor, float major) {
+ try {
+ FileWriter fw = new FileWriter(mHbmPath);
+ fw.write(mHbmEnableCommand);
+ fw.close();
+ } catch (IOException e) {
+ Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
+ }
+ mView.onFingerDown();
+ mFingerprintManager.onFingerDown(x, y, minor, major);
+ }
+
+ private void onFingerUp() {
+ mFingerprintManager.onFingerUp();
+ mView.onFingerUp();
+ try {
+ FileWriter fw = new FileWriter(mHbmPath);
+ fw.write(mHbmDisableCommand);
+ fw.close();
+ } catch (IOException e) {
+ Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
new file mode 100644
index 000000000000..8190550a74fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import com.android.systemui.R;
+
+/**
+ * A full screen view with a configurable illumination dot and scrim.
+ */
+public class UdfpsView extends View {
+ private static final String TAG = "UdfpsView";
+
+ private final Rect mScrimRect;
+ private final Paint mScrimPaint;
+
+ private float mSensorX;
+ private float mSensorY;
+ private final RectF mSensorRect;
+ private final Paint mSensorPaint;
+ private final float mSensorRadius;
+ private final float mSensorMarginBottom;
+ private final float mSensorTouchAreaCoefficient;
+
+ private final Rect mTouchableRegion;
+ private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener;
+
+ private boolean mIsFingerDown;
+
+ public UdfpsView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.UdfpsView, 0,
+ 0);
+ try {
+ if (!a.hasValue(R.styleable.UdfpsView_sensorRadius)) {
+ throw new IllegalArgumentException("UdfpsView must contain sensorRadius");
+ }
+ if (!a.hasValue(R.styleable.UdfpsView_sensorMarginBottom)) {
+ throw new IllegalArgumentException("UdfpsView must contain sensorMarginBottom");
+ }
+ if (!a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) {
+ throw new IllegalArgumentException(
+ "UdfpsView must contain sensorTouchAreaCoefficient");
+ }
+ mSensorRadius = a.getDimension(R.styleable.UdfpsView_sensorRadius, 0f);
+ mSensorMarginBottom = a.getDimension(R.styleable.UdfpsView_sensorMarginBottom, 0f);
+ mSensorTouchAreaCoefficient = a.getFloat(
+ R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f);
+ } finally {
+ a.recycle();
+ }
+
+
+ mScrimRect = new Rect();
+ mScrimPaint = new Paint(0 /* flags */);
+ mScrimPaint.setARGB(110 /* a */, 0 /* r */, 0 /* g */, 0 /* b */);
+
+ mSensorRect = new RectF();
+ mSensorPaint = new Paint(0 /* flags */);
+ mSensorPaint.setColor(Color.WHITE);
+ mSensorPaint.setStyle(Paint.Style.STROKE);
+
+ mTouchableRegion = new Rect();
+ mInsetsListener = internalInsetsInfo -> {
+ internalInsetsInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ internalInsetsInfo.touchableRegion.set(mTouchableRegion);
+ };
+
+ mIsFingerDown = false;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ Log.v(TAG, "onAttachedToWindow");
+
+ final int h = getLayoutParams().height;
+ final int w = getLayoutParams().width;
+ mScrimRect.set(0 /* left */, 0 /* top */, w, h);
+ mSensorX = w / 2f;
+ mSensorY = h - mSensorMarginBottom - mSensorRadius;
+ mSensorRect.set(mSensorX - mSensorRadius, mSensorY - mSensorRadius,
+ mSensorX + mSensorRadius, mSensorY + mSensorRadius);
+ mSensorRect.roundOut(mTouchableRegion);
+
+ getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ Log.v(TAG, "onDetachedFromWindow");
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (mIsFingerDown) {
+ canvas.drawRect(mScrimRect, mScrimPaint);
+ }
+ canvas.drawOval(mSensorRect, mSensorPaint);
+ }
+
+ boolean isValidTouch(float x, float y, float pressure) {
+ return x > (mSensorX - mSensorRadius * mSensorTouchAreaCoefficient)
+ && x < (mSensorX + mSensorRadius * mSensorTouchAreaCoefficient)
+ && y > (mSensorY - mSensorRadius * mSensorTouchAreaCoefficient)
+ && y < (mSensorY + mSensorRadius * mSensorTouchAreaCoefficient);
+ }
+
+ boolean isFingerDown() {
+ return mIsFingerDown;
+ }
+
+ void onFingerDown() {
+ mIsFingerDown = true;
+ mSensorPaint.setStyle(Paint.Style.FILL);
+ postInvalidate();
+ }
+
+ void onFingerUp() {
+ mIsFingerDown = false;
+ mSensorPaint.setStyle(Paint.Style.STROKE);
+ postInvalidate();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index b615885596ee..87489262a420 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -124,8 +124,26 @@ class Bubble implements BubbleViewProvider {
private int mNotificationId;
private int mAppUid = -1;
+ /**
+ * A bubble is created and can be updated. This intent is updated until the user first
+ * expands the bubble. Once the user has expanded the contents, we ignore the intent updates
+ * to prevent restarting the intent & possibly altering UI state in the activity in front of
+ * the user.
+ *
+ * Once the bubble is overflowed, the activity is finished and updates to the
+ * notification are respected. Typically an update to an overflowed bubble would result in
+ * that bubble being added back to the stack anyways.
+ */
@Nullable
private PendingIntent mIntent;
+ private boolean mIntentActive;
+ @Nullable
+ private PendingIntent.CancelListener mIntentCancelListener;
+
+ /**
+ * Sent when the bubble & notification are no longer visible to the user (i.e. no
+ * notification in the shade, no bubble in the stack or overflow).
+ */
@Nullable
private PendingIntent mDeleteIntent;
@@ -137,6 +155,7 @@ class Bubble implements BubbleViewProvider {
final int desiredHeight, final int desiredHeightResId, @Nullable final String title) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
+ mMetadataShortcutId = shortcutInfo.getId();
mShortcutInfo = shortcutInfo;
mKey = key;
mFlags = 0;
@@ -149,13 +168,19 @@ class Bubble implements BubbleViewProvider {
mShowBubbleUpdateDot = false;
}
- /** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
Bubble(@NonNull final NotificationEntry e,
- @Nullable final BubbleController.NotificationSuppressionChangedListener listener) {
+ @Nullable final BubbleController.NotificationSuppressionChangedListener listener,
+ final BubbleController.PendingIntentCanceledListener intentCancelListener) {
Objects.requireNonNull(e);
mKey = e.getKey();
mSuppressionListener = listener;
+ mIntentCancelListener = intent -> {
+ if (mIntent != null) {
+ mIntent.unregisterCancelListener(mIntentCancelListener);
+ }
+ intentCancelListener.onPendingIntentCanceled(this);
+ };
setEntry(e);
}
@@ -237,6 +262,10 @@ class Bubble implements BubbleViewProvider {
mExpandedView = null;
}
mIconView = null;
+ if (mIntent != null) {
+ mIntent.unregisterCancelListener(mIntentCancelListener);
+ }
+ mIntentActive = false;
}
void setPendingIntentCanceled() {
@@ -370,11 +399,24 @@ class Bubble implements BubbleViewProvider {
mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
mIcon = entry.getBubbleMetadata().getIcon();
- mIntent = entry.getBubbleMetadata().getIntent();
+
+ if (!mIntentActive || mIntent == null) {
+ if (mIntent != null) {
+ mIntent.unregisterCancelListener(mIntentCancelListener);
+ }
+ mIntent = entry.getBubbleMetadata().getIntent();
+ if (mIntent != null) {
+ mIntent.registerCancelListener(mIntentCancelListener);
+ }
+ } else if (mIntent != null && entry.getBubbleMetadata().getIntent() == null) {
+ // Was an intent bubble now it's a shortcut bubble... still unregister the listener
+ mIntent.unregisterCancelListener(mIntentCancelListener);
+ mIntent = null;
+ }
mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
}
mIsImportantConversation =
- entry.getChannel() == null ? false : entry.getChannel().isImportantConversation();
+ entry.getChannel() != null && entry.getChannel().isImportantConversation();
}
@Nullable
@@ -394,10 +436,15 @@ class Bubble implements BubbleViewProvider {
}
/**
- * @return if the bubble was ever expanded
+ * Sets if the intent used for this bubble is currently active (i.e. populating an
+ * expanded view, expanded or not).
*/
- boolean getWasAccessed() {
- return mLastAccessed != 0L;
+ void setIntentActive() {
+ mIntentActive = true;
+ }
+
+ boolean isIntentActive() {
+ return mIntentActive;
}
/**
@@ -665,22 +712,6 @@ class Bubble implements BubbleViewProvider {
return Objects.hash(mKey);
}
- @Override
- public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- mPackageName,
- mChannelId,
- mNotificationId,
- index,
- bubbleCount,
- action,
- normalX,
- normalY,
- showInShade(),
- false /* isOngoing (unused) */,
- false /* isAppForeground (unused) */);
- }
-
@Nullable
private static String getTitle(@NonNull final NotificationEntry e) {
final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence(
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 6df8b51c301c..434bf49ac878 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -257,6 +257,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
+ * Listener to be notified when a pending intent has been canceled for a bubble.
+ */
+ public interface PendingIntentCanceledListener {
+ /**
+ * Called when the pending intent for a bubble has been canceled.
+ */
+ void onPendingIntentCanceled(Bubble bubble);
+ }
+
+ /**
* Callback for when the BubbleController wants to interact with the notification pipeline to:
* - Remove a previously bubbled notification
* - Update the notification shade since bubbled notification should/shouldn't be showing
@@ -384,6 +394,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
});
+ mBubbleData.setPendingIntentCancelledListener(bubble -> {
+ if (bubble.getBubbleIntent() == null) {
+ return;
+ }
+ if (bubble.isIntentActive()) {
+ bubble.setPendingIntentCanceled();
+ return;
+ }
+ mHandler.post(
+ () -> removeBubble(bubble.getKey(),
+ BubbleController.DISMISS_INVALID_INTENT));
+ });
mNotificationEntryManager = entryManager;
mNotificationGroupManager = groupManager;
@@ -1096,23 +1118,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Lazy init stack view when a bubble is created
ensureStackViewCreated();
bubble.setInflateSynchronously(mInflateSynchronously);
- bubble.inflate(
- b -> {
- mBubbleData.notificationEntryUpdated(b, suppressFlyout,
- showInShade);
- if (bubble.getBubbleIntent() == null) {
- return;
- }
- bubble.getBubbleIntent().registerCancelListener(pendingIntent -> {
- if (bubble.getWasAccessed()) {
- bubble.setPendingIntentCanceled();
- return;
- }
- mHandler.post(
- () -> removeBubble(bubble.getKey(),
- BubbleController.DISMISS_INVALID_INTENT));
- });
- },
+ bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
mContext, mStackView, mBubbleIconFactory, false /* skipInflation */);
}
@@ -1292,6 +1298,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Collapsing? Do this first before remaining steps.
if (update.expandedChanged && !update.expanded) {
mStackView.setExpanded(false);
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
}
// Do removals, if any.
@@ -1301,7 +1308,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
for (Pair<Bubble, Integer> removed : removedBubbles) {
final Bubble bubble = removed.first;
@DismissReason final int reason = removed.second;
- mStackView.removeBubble(bubble);
+
+ if (mStackView != null) {
+ mStackView.removeBubble(bubble);
+ }
// If the bubble is removed for user switching, leave the notification in place.
if (reason == DISMISS_USER_CHANGED) {
@@ -1378,6 +1388,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (update.expandedChanged && update.expanded) {
if (mStackView != null) {
mStackView.setExpanded(true);
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index acbde9fa3efa..10f4385ed443 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -34,6 +34,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController.DismissReason;
+import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -59,7 +60,7 @@ import javax.inject.Singleton;
@Singleton
public class BubbleData {
- BubbleLogger mLogger = new BubbleLoggerImpl();
+ private BubbleLoggerImpl mLogger = new BubbleLoggerImpl();
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleData" : TAG_BUBBLES;
@@ -137,6 +138,7 @@ public class BubbleData {
@Nullable
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+ private BubbleController.PendingIntentCanceledListener mCancelledListener;
/**
* We track groups with summaries that aren't visibly displayed but still kept around because
@@ -167,6 +169,11 @@ public class BubbleData {
mSuppressionListener = listener;
}
+ public void setPendingIntentCancelledListener(
+ BubbleController.PendingIntentCanceledListener listener) {
+ mCancelledListener = listener;
+ }
+
public boolean hasBubbles() {
return !mBubbles.isEmpty();
}
@@ -236,7 +243,7 @@ public class BubbleData {
bubbleToReturn = mPendingBubbles.get(key);
} else if (entry != null) {
// New bubble
- bubbleToReturn = new Bubble(entry, mSuppressionListener);
+ bubbleToReturn = new Bubble(entry, mSuppressionListener, mCancelledListener);
} else {
// Persisted bubble being promoted
bubbleToReturn = persistedBubble;
@@ -368,6 +375,10 @@ public class BubbleData {
final Predicate<Bubble> invalidBubblesFromPackage = bubble -> {
final boolean bubbleIsFromPackage = packageName.equals(bubble.getPackageName());
+ final boolean isShortcutBubble = bubble.hasMetadataShortcutId();
+ if (!bubbleIsFromPackage || !isShortcutBubble) {
+ return false;
+ }
final boolean hasShortcutIdAndValidShortcut =
bubble.hasMetadataShortcutId()
&& bubble.getShortcutInfo() != null
@@ -472,6 +483,9 @@ public class BubbleData {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "Cancel overflow bubble: " + b);
}
+ if (b != null) {
+ b.stopInflation();
+ }
mLogger.logOverflowRemove(b, reason);
mStateChange.bubbleRemoved(b, reason);
mOverflowBubbles.remove(b);
@@ -479,6 +493,7 @@ public class BubbleData {
return;
}
Bubble bubbleToRemove = mBubbles.get(indexToRemove);
+ bubbleToRemove.stopInflation();
if (mBubbles.size() == 1) {
// Going to become empty, handle specially.
setExpandedInternal(false);
@@ -597,6 +612,30 @@ public class BubbleData {
}
/**
+ * Logs the bubble UI event.
+ *
+ * @param provider The bubble view provider that is being interacted on. Null value indicates
+ * that the user interaction is not specific to one bubble.
+ * @param action The user interaction enum
+ * @param packageName SystemUI package
+ * @param bubbleCount Number of bubbles in the stack
+ * @param bubbleIndex Index of bubble in the stack
+ * @param normalX Normalized x position of the stack
+ * @param normalY Normalized y position of the stack
+ */
+ void logBubbleEvent(@Nullable BubbleViewProvider provider, int action, String packageName,
+ int bubbleCount, int bubbleIndex, float normalX, float normalY) {
+ if (provider == null) {
+ mLogger.logStackUiChanged(packageName, action, bubbleCount, normalX, normalY);
+ } else if (provider.getKey().equals(BubbleOverflow.KEY)) {
+ mLogger.logShowOverflow(packageName, action, bubbleCount, normalX, normalY);
+ } else {
+ mLogger.logBubbleUiChanged((Bubble) provider, packageName, action, bubbleCount, normalX,
+ normalY, bubbleIndex);
+ }
+ }
+
+ /**
* Requests a change to the expanded state.
*
* @param shouldExpand the new requested state
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
deleted file mode 100644
index 9db371e487c7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
-import com.android.systemui.R;
-
-/** Dismiss view that contains a scrim gradient, as well as a dismiss icon, text, and circle. */
-public class BubbleDismissView extends FrameLayout {
- /** Duration for animations involving the dismiss target text/icon. */
- private static final int DISMISS_TARGET_ANIMATION_BASE_DURATION = 150;
- private static final float SCALE_FOR_POP = 1.2f;
- private static final float SCALE_FOR_DISMISS = 0.9f;
-
- private LinearLayout mDismissTarget;
- private ImageView mDismissIcon;
- private View mDismissCircle;
-
- private SpringAnimation mDismissTargetAlphaSpring;
- private SpringAnimation mDismissTargetVerticalSpring;
-
- public BubbleDismissView(Context context) {
- super(context);
- setVisibility(GONE);
-
- LayoutInflater.from(context).inflate(R.layout.bubble_dismiss_target, this, true);
- mDismissTarget = findViewById(R.id.bubble_dismiss_icon_container);
- mDismissIcon = findViewById(R.id.bubble_dismiss_close_icon);
- mDismissCircle = findViewById(R.id.bubble_dismiss_circle);
-
- // Set up the basic target area animations. These are very simple animations that don't need
- // fancy interpolators.
- final AccelerateDecelerateInterpolator interpolator =
- new AccelerateDecelerateInterpolator();
- mDismissIcon.animate()
- .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION)
- .setInterpolator(interpolator);
- mDismissCircle.animate()
- .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION / 2)
- .setInterpolator(interpolator);
-
- mDismissTargetAlphaSpring =
- new SpringAnimation(mDismissTarget, DynamicAnimation.ALPHA)
- .setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_LOW)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
- mDismissTargetVerticalSpring =
- new SpringAnimation(mDismissTarget, DynamicAnimation.TRANSLATION_Y)
- .setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_MEDIUM)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
-
- mDismissTargetAlphaSpring.addEndListener((anim, canceled, alpha, velocity) -> {
- // Since DynamicAnimations end when they're 'nearly' done, we can't rely on alpha being
- // exactly zero when this listener is triggered. However, if it's less than 50% we can
- // safely assume it was animating out rather than in.
- if (alpha < 0.5f) {
- // If the alpha spring was animating the view out, set it to GONE when it's done.
- setVisibility(INVISIBLE);
- }
- });
- }
-
- /** Springs in the dismiss target. */
- void springIn() {
- setVisibility(View.VISIBLE);
-
- // Fade in the dismiss target icon.
- mDismissIcon.animate()
- .setDuration(50)
- .scaleX(1f)
- .scaleY(1f)
- .alpha(1f);
- mDismissTarget.setAlpha(0f);
- mDismissTargetAlphaSpring.animateToFinalPosition(1f);
-
- // Spring up the dismiss target.
- mDismissTarget.setTranslationY(mDismissTarget.getHeight() / 2f);
- mDismissTargetVerticalSpring.animateToFinalPosition(0);
-
- mDismissCircle.setAlpha(0f);
- mDismissCircle.setScaleX(SCALE_FOR_POP);
- mDismissCircle.setScaleY(SCALE_FOR_POP);
-
- // Fade in circle and reduce size.
- mDismissCircle.animate()
- .alpha(1f)
- .scaleX(1f)
- .scaleY(1f);
- }
-
- /** Springs out the dismiss target. */
- void springOut() {
- // Fade out the target icon.
- mDismissIcon.animate()
- .setDuration(50)
- .scaleX(SCALE_FOR_DISMISS)
- .scaleY(SCALE_FOR_DISMISS)
- .alpha(0f);
-
- // Fade out the target.
- mDismissTargetAlphaSpring.animateToFinalPosition(0f);
-
- // Spring the target down a bit.
- mDismissTargetVerticalSpring.animateToFinalPosition(mDismissTarget.getHeight() / 2f);
-
- // Pop out the circle.
- mDismissCircle.animate()
- .scaleX(SCALE_FOR_DISMISS)
- .scaleY(SCALE_FOR_DISMISS)
- .alpha(0f);
- }
-
- /** Returns the Y value of the center of the dismiss target. */
- float getDismissTargetCenterY() {
- return getTop() + mDismissTarget.getTop() + mDismissTarget.getHeight() / 2f;
- }
-
- /** Returns the dismiss target, which contains the text/icon and any added padding. */
- View getDismissTarget() {
- return mDismissTarget;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 51550306a893..e26aa554dd61 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -183,6 +183,9 @@ public class BubbleExpandedView extends LinearLayout {
// Apply flags to make behaviour match documentLaunchMode=always.
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ if (mBubble != null) {
+ mBubble.setIntentActive();
+ }
mActivityView.startActivity(mPendingIntent, fillInIntent, options);
}
} catch (RuntimeException e) {
@@ -496,17 +499,22 @@ public class BubbleExpandedView extends LinearLayout {
}
final float alpha = visibility ? 1f : 0f;
- if (alpha == mActivityView.getAlpha()) {
- return;
- }
-
mPointerView.setAlpha(alpha);
- if (mActivityView != null) {
+
+ if (mActivityView != null && alpha != mActivityView.getAlpha()) {
mActivityView.setAlpha(alpha);
mActivityView.bringToFront();
}
}
+ @Nullable ActivityView getActivityView() {
+ return mActivityView;
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
/**
* Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
* This should be done post-move and post-animation.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index 40a93e1cdc47..d017bc0e31c2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -24,7 +24,8 @@ import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Rect;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -41,15 +42,15 @@ import com.android.systemui.R;
*/
public class BubbleIconFactory extends BaseIconFactory {
+ private int mBadgeSize;
+
protected BubbleIconFactory(Context context) {
super(context, context.getResources().getConfiguration().densityDpi,
context.getResources().getDimensionPixelSize(R.dimen.individual_bubble_size));
- }
-
- int getBadgeSize() {
- return mContext.getResources().getDimensionPixelSize(
+ mBadgeSize = mContext.getResources().getDimensionPixelSize(
com.android.launcher3.icons.R.dimen.profile_badge_size);
}
+
/**
* Returns the drawable that the developer has provided to display in the bubble.
*/
@@ -79,25 +80,34 @@ public class BubbleIconFactory extends BaseIconFactory {
* will include the workprofile indicator on the badge if appropriate.
*/
BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
- Bitmap userBadgedBitmap = createIconBitmap(
- userBadgedAppIcon, 1f, getBadgeSize());
- ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
- if (!isImportantConversation) {
- Canvas c = new Canvas();
- c.setBitmap(userBadgedBitmap);
- shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
- return createIconBitmap(userBadgedBitmap);
- } else {
- float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
+ ShadowGenerator shadowGenerator = new ShadowGenerator(mBadgeSize);
+ Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mBadgeSize);
+
+ if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
+ userBadgedBitmap = Bitmap.createScaledBitmap(
+ getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
+ userBadgedAppIcon.getIntrinsicWidth()),
+ mBadgeSize, mBadgeSize, /* filter */ true);
+ }
+
+ if (isImportantConversation) {
+ final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width);
- int importantConversationColor = mContext.getResources().getColor(
+ final int importantConversationColor = mContext.getResources().getColor(
com.android.settingslib.R.color.important_conversation, null);
Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
Canvas c = new Canvas(badgeAndRing);
- Rect dest = new Rect((int) ringStrokeWidth, (int) ringStrokeWidth,
- c.getHeight() - (int) ringStrokeWidth, c.getWidth() - (int) ringStrokeWidth);
- c.drawBitmap(userBadgedBitmap, null, dest, null);
+
+ final int bitmapTop = (int) ringStrokeWidth;
+ final int bitmapLeft = (int) ringStrokeWidth;
+ final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
+ final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
+
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
+ bitmapHeight, /* filter */ true);
+ c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
+
Paint ringPaint = new Paint();
ringPaint.setStyle(Paint.Style.STROKE);
ringPaint.setColor(importantConversationColor);
@@ -105,11 +115,48 @@ public class BubbleIconFactory extends BaseIconFactory {
ringPaint.setStrokeWidth(ringStrokeWidth);
c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2 - ringStrokeWidth,
ringPaint);
+
shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
return createIconBitmap(badgeAndRing);
+ } else {
+ Canvas c = new Canvas();
+ c.setBitmap(userBadgedBitmap);
+ shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
+ return createIconBitmap(userBadgedBitmap);
}
}
+ public Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
+ Drawable foreground = icon.getForeground();
+ Drawable background = icon.getBackground();
+ Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas();
+ canvas.setBitmap(bitmap);
+
+ // Clip canvas to circle.
+ Path circlePath = new Path();
+ circlePath.addCircle(/* x */ size / 2f,
+ /* y */ size / 2f,
+ /* radius */ size / 2f,
+ Path.Direction.CW);
+ canvas.clipPath(circlePath);
+
+ // Draw background.
+ background.setBounds(0, 0, size, size);
+ background.draw(canvas);
+
+ // Draw foreground. The foreground and background drawables are derived from adaptive icons
+ // Some icon shapes fill more space than others, so adaptive icons are normalized to about
+ // the same size. This size is smaller than the original bounds, so we estimate
+ // the difference in this offset.
+ int offset = size / 5;
+ foreground.setBounds(-offset, -offset, size + offset, size + offset);
+ foreground.draw(canvas);
+
+ canvas.setBitmap(null);
+ return bitmap;
+ }
+
/**
* Returns a {@link BitmapInfo} for the entire bubble icon including the badge.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
index c1dd8c36ff6f..d702cc4e0062 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.bubbles;
import com.android.internal.logging.UiEventLoggerImpl;
+import com.android.systemui.shared.system.SysUiStatsLog;
/**
* Implementation of UiEventLogger for logging bubble UI events.
@@ -64,4 +65,52 @@ public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger
log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
}
}
+
+ void logStackUiChanged(String packageName, int action, int bubbleCount, float normalX,
+ float normalY) {
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ packageName,
+ null /* notification channel */,
+ 0 /* notification ID */,
+ 0 /* bubble position */,
+ bubbleCount,
+ action,
+ normalX,
+ normalY,
+ false /* unread bubble */,
+ false /* on-going bubble */,
+ false /* isAppForeground (unused) */);
+ }
+
+ void logShowOverflow(String packageName, int action, int bubbleCount, float normalX,
+ float normalY) {
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ packageName,
+ BubbleOverflow.KEY /* notification channel */,
+ 0 /* notification ID */,
+ 0 /* bubble position */,
+ bubbleCount,
+ action,
+ normalX,
+ normalY,
+ false /* unread bubble */,
+ false /* on-going bubble */,
+ false /* isAppForeground (unused) */);
+ }
+
+ void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount,
+ float normalX, float normalY, int index) {
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ packageName,
+ bubble.getChannelId() /* notification channel */,
+ bubble.getNotificationId() /* notification ID */,
+ index,
+ bubbleCount,
+ action,
+ normalX,
+ normalY,
+ bubble.showInShade() /* isUnread */,
+ false /* isOngoing (unused) */,
+ false /* isAppForeground (unused) */);
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
deleted file mode 100644
index 86244ba5248a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
-
-/**
- * Educational view to highlight the manage button that allows a user to configure the settings
- * for the bubble. Shown only the first time a user expands a bubble.
- */
-public class BubbleManageEducationView extends LinearLayout {
-
- private View mManageView;
- private TextView mTitleTextView;
- private TextView mDescTextView;
-
- public BubbleManageEducationView(Context context) {
- this(context, null);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mManageView = findViewById(R.id.manage_education_view);
- mTitleTextView = findViewById(R.id.user_education_title);
- mDescTextView = findViewById(R.id.user_education_description);
-
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[] {android.R.attr.colorAccent,
- android.R.attr.textColorPrimaryInverse});
- final int bgColor = ta.getColor(0, Color.BLACK);
- int textColor = ta.getColor(1, Color.WHITE);
- ta.recycle();
-
- textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
- mTitleTextView.setTextColor(textColor);
- mDescTextView.setTextColor(textColor);
- }
-
- /**
- * Specifies the position for the manage view.
- */
- public void setManageViewPosition(int x, int y) {
- mManageView.setTranslationX(x);
- mManageView.setTranslationY(y);
- }
-
- /**
- * @return the height of the view that shows the educational text and pointer.
- */
- public int getManageViewHeight() {
- return mManageView.getHeight();
- }
-
- @Override
- public void setLayoutDirection(int direction) {
- super.setLayoutDirection(direction);
- if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl);
- mTitleTextView.setGravity(Gravity.RIGHT);
- mDescTextView.setGravity(Gravity.RIGHT);
- } else {
- mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg);
- mTitleTextView.setGravity(Gravity.LEFT);
- mDescTextView.setGravity(Gravity.LEFT);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
deleted file mode 100644
index dadcb3a3a7c4..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.View.GONE;
-
-import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.util.PathParser;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-/**
- * Class for showing aged out bubbles.
- */
-public class BubbleOverflow implements BubbleViewProvider {
- public static final String KEY = "Overflow";
-
- private BadgedImageView mOverflowBtn;
- private BubbleExpandedView mExpandedView;
- private LayoutInflater mInflater;
- private Context mContext;
- private Bitmap mIcon;
- private Path mPath;
- private int mBitmapSize;
- private int mIconBitmapSize;
- private int mDotColor;
-
- public BubbleOverflow(Context context) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- }
-
- void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
- updateDimensions();
- mExpandedView = (BubbleExpandedView) mInflater.inflate(
- R.layout.bubble_expanded_view, parentViewGroup /* root */,
- false /* attachToRoot */);
- mExpandedView.setOverflow(true);
- mExpandedView.setStackView(stackView);
- mExpandedView.applyThemeAttrs();
- updateIcon(mContext, parentViewGroup);
- }
-
- void updateDimensions() {
- mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
- mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.bubble_overflow_icon_bitmap_size);
- if (mExpandedView != null) {
- mExpandedView.updateDimensions();
- }
- }
-
- void updateIcon(Context context, ViewGroup parentViewGroup) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
- parentViewGroup /* root */,
- false /* attachToRoot */);
- mOverflowBtn.setContentDescription(mContext.getResources().getString(
- R.string.bubble_overflow_button_content_description));
- Resources res = mContext.getResources();
-
- // Set color for button icon and dot
- TypedValue typedValue = new TypedValue();
- mContext.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
- int colorAccent = mContext.getColor(typedValue.resourceId);
- mOverflowBtn.getDrawable().setTint(colorAccent);
- mDotColor = colorAccent;
-
- // Set color for button and activity background
- ColorDrawable bg = new ColorDrawable(res.getColor(R.color.bubbles_light));
- final int mode = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- if (mode == Configuration.UI_MODE_NIGHT_YES) {
- bg = new ColorDrawable(res.getColor(R.color.bubbles_dark));
- }
-
- // Apply icon inset
- InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(),
- mBitmapSize - mIconBitmapSize /* inset */);
- AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg);
-
- BubbleIconFactory iconFactory = new BubbleIconFactory(mContext);
- mIcon = iconFactory.createBadgedIconBitmap(adaptiveIconDrawable,
- null /* user */,
- true /* shrinkNonAdaptiveIcons */).icon;
-
- // Get path with dot location
- float scale = iconFactory.getNormalizer().getScale(mOverflowBtn.getDrawable(),
- null /* outBounds */, null /* path */, null /* outMaskShape */);
- float radius = DEFAULT_PATH_SIZE / 2f;
- mPath = PathParser.createPathFromPathData(
- mContext.getResources().getString(com.android.internal.R.string.config_icon_mask));
- Matrix matrix = new Matrix();
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */);
- mPath.transform(matrix);
-
- mOverflowBtn.setRenderedBubble(this);
- }
-
- void setVisible(int visible) {
- mOverflowBtn.setVisibility(visible);
- }
-
- @Override
- public BubbleExpandedView getExpandedView() {
- return mExpandedView;
- }
-
- @Override
- public int getDotColor() {
- return mDotColor;
- }
-
- @Override
- public Bitmap getBadgedImage() {
- return mIcon;
- }
-
- @Override
- public boolean showDot() {
- return false;
- }
-
- @Override
- public Path getDotPath() {
- return mPath;
- }
-
- @Override
- public void setContentVisibility(boolean visible) {
- mExpandedView.setContentVisibility(visible);
- }
-
- @Override
- public void logUIEvent(int bubbleCount, int action, float normalX, float normalY,
- int index) {
- // TODO(b/149133814) Log overflow UI events.
- }
-
- @Override
- public View getIconView() {
- return mOverflowBtn;
- }
-
- @Override
- public String getKey() {
- return BubbleOverflow.KEY;
- }
-
- @Override
- public int getDisplayId() {
- return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
new file mode 100644
index 000000000000..155b71b99ff9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.graphics.Path
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.InsetDrawable
+import android.util.PathParser
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import com.android.systemui.R
+
+class BubbleOverflow(
+ private val context: Context,
+ private val stack: BubbleStackView
+) : BubbleViewProvider {
+
+ private var bitmap: Bitmap? = null
+ private var dotPath: Path? = null
+ private var bitmapSize = 0
+ private var iconBitmapSize = 0
+ private var dotColor = 0
+
+ private val inflater: LayoutInflater = LayoutInflater.from(context)
+ private val expandedView: BubbleExpandedView = inflater
+ .inflate(R.layout.bubble_expanded_view, null /* root */, false /* attachToRoot */)
+ as BubbleExpandedView
+ private val overflowBtn: BadgedImageView = inflater
+ .inflate(R.layout.bubble_overflow_button, null /* root */, false /* attachToRoot */)
+ as BadgedImageView
+ init {
+ updateResources()
+ with(expandedView) {
+ setOverflow(true)
+ setStackView(stack)
+ applyThemeAttrs()
+ }
+ with(overflowBtn) {
+ setContentDescription(context.resources.getString(
+ R.string.bubble_overflow_button_content_description))
+ updateBtnTheme()
+ }
+ }
+
+ fun update() {
+ updateResources()
+ expandedView.applyThemeAttrs()
+ // Apply inset and new style to fresh icon drawable.
+ overflowBtn.setImageResource(R.drawable.ic_bubble_overflow_button)
+ updateBtnTheme()
+ }
+
+ fun updateResources() {
+ bitmapSize = context.resources.getDimensionPixelSize(R.dimen.bubble_bitmap_size)
+ iconBitmapSize = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_overflow_icon_bitmap_size)
+ val bubbleSize = context.resources.getDimensionPixelSize(R.dimen.individual_bubble_size)
+ overflowBtn.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
+ expandedView.updateDimensions()
+ }
+
+ fun updateBtnTheme() {
+ val res = context.resources
+
+ // Set overflow button accent color, dot color
+ val typedValue = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
+
+ val colorAccent = res.getColor(typedValue.resourceId)
+ overflowBtn.getDrawable()?.setTint(colorAccent)
+ dotColor = colorAccent
+
+ // Set button and activity background color
+ val nightMode = (res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ == Configuration.UI_MODE_NIGHT_YES)
+ val bg = ColorDrawable(res.getColor(
+ if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
+
+ // Set button icon
+ val iconFactory = BubbleIconFactory(context)
+ val fg = InsetDrawable(overflowBtn.getDrawable(),
+ bitmapSize - iconBitmapSize /* inset */)
+ bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
+ null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+
+ // Set dot path
+ dotPath = PathParser.createPathFromPathData(
+ res.getString(com.android.internal.R.string.config_icon_mask))
+ val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
+ null /* outBounds */, null /* path */, null /* outMaskShape */)
+ val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
+ val matrix = Matrix()
+ matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+ radius /* pivot y */)
+ dotPath?.transform(matrix)
+ overflowBtn.setRenderedBubble(this)
+ }
+
+ fun setVisible(visible: Int) {
+ overflowBtn.visibility = visible
+ }
+
+ override fun getExpandedView(): BubbleExpandedView? {
+ return expandedView
+ }
+
+ override fun getDotColor(): Int {
+ return dotColor
+ }
+
+ override fun getBadgedImage(): Bitmap? {
+ return bitmap
+ }
+
+ override fun showDot(): Boolean {
+ return false
+ }
+
+ override fun getDotPath(): Path? {
+ return dotPath
+ }
+
+ override fun setContentVisibility(visible: Boolean) {
+ expandedView.setContentVisibility(visible)
+ }
+
+ override fun getIconView(): View? {
+ return overflowBtn
+ }
+
+ override fun getKey(): String {
+ return KEY
+ }
+
+ override fun getDisplayId(): Int {
+ return expandedView.virtualDisplayId
+ }
+
+ companion object {
+ @JvmField val KEY = "Overflow"
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 7937ef5d4192..ea12c9598b91 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -31,6 +31,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
+import android.app.ActivityView;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -47,7 +48,6 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
-import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
@@ -94,7 +94,6 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.RelativeTouchListener;
import com.android.systemui.util.animation.PhysicsAnimator;
@@ -117,7 +116,7 @@ public class BubbleStackView extends FrameLayout
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
/** Animation durations for bubble stack user education views. **/
- private static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
+ static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40;
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
@@ -138,9 +137,6 @@ public class BubbleStackView extends FrameLayout
/** Percent to darken the bubbles when they're in the dismiss target. */
private static final float DARKEN_PERCENT = 0.3f;
- /** Duration of the dismiss scrim fading in/out. */
- private static final int DISMISS_TRANSITION_DURATION_MS = 200;
-
/** How long to wait, in milliseconds, before hiding the flyout. */
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
@@ -272,7 +268,6 @@ public class BubbleStackView extends FrameLayout
private int mBubbleTouchPadding;
private int mExpandedViewPadding;
private int mCornerRadius;
- private int mPointerHeight;
private int mStatusBarHeight;
private int mImeOffset;
@Nullable private BubbleViewProvider mExpandedBubble;
@@ -290,14 +285,51 @@ public class BubbleStackView extends FrameLayout
/** Whether we're in the middle of dragging the stack around by touch. */
private boolean mIsDraggingStack = false;
+ /**
+ * The pointer index of the ACTION_DOWN event we received prior to an ACTION_UP. We'll ignore
+ * touches from other pointer indices.
+ */
+ private int mPointerIndexDown = -1;
+
/** Description of current animation controller state. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Stack view state:");
- pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress);
- pw.print(" showingDismiss: "); pw.println(mShowingDismiss);
- pw.print(" isExpansionAnimating: "); pw.println(mIsExpansionAnimating);
+ pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress);
+ pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing());
+ pw.print(" isExpansionAnimating: "); pw.println(mIsExpansionAnimating);
+ pw.print(" expandedContainerVis: "); pw.println(mExpandedViewContainer.getVisibility());
+ pw.print(" expandedContainerAlpha: "); pw.println(mExpandedViewContainer.getAlpha());
+ pw.print(" expandedContainerMatrix: ");
+ pw.println(mExpandedViewContainer.getAnimationMatrix());
+
mStackAnimationController.dump(fd, pw, args);
mExpandedAnimationController.dump(fd, pw, args);
+
+ if (mExpandedBubble != null) {
+ pw.println("Expanded bubble state:");
+ pw.println(" expandedBubbleKey: " + mExpandedBubble.getKey());
+
+ final BubbleExpandedView expandedView = mExpandedBubble.getExpandedView();
+
+ if (expandedView != null) {
+ pw.println(" expandedViewVis: " + expandedView.getVisibility());
+ pw.println(" expandedViewAlpha: " + expandedView.getAlpha());
+ pw.println(" expandedViewTaskId: " + expandedView.getTaskId());
+
+ final ActivityView av = expandedView.getActivityView();
+
+ if (av != null) {
+ pw.println(" activityViewVis: " + av.getVisibility());
+ pw.println(" activityViewAlpha: " + av.getAlpha());
+ } else {
+ pw.println(" activityView is null");
+ }
+ } else {
+ pw.println("Expanded bubble view state: expanded bubble view is null");
+ }
+ } else {
+ pw.println("Expanded bubble state: expanded bubble is null");
+ }
}
private BubbleController.BubbleExpandListener mExpandListener;
@@ -310,7 +342,6 @@ public class BubbleStackView extends FrameLayout
private boolean mViewUpdatedRequested = false;
private boolean mIsExpansionAnimating = false;
private boolean mIsBubbleSwitchAnimating = false;
- private boolean mShowingDismiss = false;
/** The view to desaturate/darken when magneted to the dismiss target. */
@Nullable private View mDesaturateAndDarkenTargetView;
@@ -428,7 +459,7 @@ public class BubbleStackView extends FrameLayout
if (wasFlungOut) {
mExpandedAnimationController.snapBubbleBack(
mExpandedAnimationController.getDraggedOutBubble(), velX, velY);
- hideDismissTarget();
+ mDismissView.hide();
} else {
mExpandedAnimationController.onUnstuckFromTarget();
}
@@ -442,9 +473,9 @@ public class BubbleStackView extends FrameLayout
mExpandedAnimationController.dismissDraggedOutBubble(
mExpandedAnimationController.getDraggedOutBubble() /* bubble */,
- mDismissTargetContainer.getHeight() /* translationYBy */,
+ mDismissView.getHeight() /* translationYBy */,
BubbleStackView.this::dismissMagnetizedObject /* after */);
- hideDismissTarget();
+ mDismissView.hide();
}
};
@@ -465,7 +496,7 @@ public class BubbleStackView extends FrameLayout
if (wasFlungOut) {
mStackAnimationController.flingStackThenSpringToEdge(
mStackAnimationController.getStackPosition().x, velX, velY);
- hideDismissTarget();
+ mDismissView.hide();
} else {
mStackAnimationController.onUnstuckFromTarget();
}
@@ -474,14 +505,14 @@ public class BubbleStackView extends FrameLayout
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
mStackAnimationController.animateStackDismissal(
- mDismissTargetContainer.getHeight() /* translationYBy */,
+ mDismissView.getHeight() /* translationYBy */,
() -> {
resetDesaturationAndDarken();
dismissMagnetizedObject();
}
);
- hideDismissTarget();
+ mDismissView.hide();
}
};
@@ -602,7 +633,7 @@ public class BubbleStackView extends FrameLayout
}
// Show the dismiss target, if we haven't already.
- springInDismissTargetMaybe();
+ mDismissView.show();
// First, see if the magnetized object consumes the event - if so, we shouldn't move the
// bubble since it's stuck to the target.
@@ -644,7 +675,7 @@ public class BubbleStackView extends FrameLayout
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
- hideDismissTarget();
+ mDismissView.hide();
}
mIsDraggingStack = false;
@@ -706,12 +737,7 @@ public class BubbleStackView extends FrameLayout
}
};
- private View mDismissTargetCircle;
- private ViewGroup mDismissTargetContainer;
- private PhysicsAnimator<View> mDismissTargetAnimator;
- private PhysicsAnimator.SpringConfig mDismissTargetSpring = new PhysicsAnimator.SpringConfig(
- SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
-
+ private DismissView mDismissView;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
@Nullable
@@ -722,7 +748,7 @@ public class BubbleStackView extends FrameLayout
private View mUserEducationView;
private boolean mShouldShowManageEducation;
- private BubbleManageEducationView mManageEducationView;
+ private ManageEducationView mManageEducationView;
private boolean mAnimatingManageEducationAway;
private ViewGroup mManageMenu;
@@ -752,7 +778,6 @@ public class BubbleStackView extends FrameLayout
mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding);
- mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
mStatusBarHeight =
res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
@@ -830,34 +855,8 @@ public class BubbleStackView extends FrameLayout
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
- final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
- mDismissTargetCircle = new DismissCircleView(context);
- final FrameLayout.LayoutParams newParams =
- new FrameLayout.LayoutParams(targetSize, targetSize);
- newParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- mDismissTargetCircle.setLayoutParams(newParams);
- mDismissTargetAnimator = PhysicsAnimator.getInstance(mDismissTargetCircle);
-
- mDismissTargetContainer = new FrameLayout(context);
- mDismissTargetContainer.setLayoutParams(new FrameLayout.LayoutParams(
- MATCH_PARENT,
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
- Gravity.BOTTOM));
-
- final int bottomMargin =
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin);
- mDismissTargetContainer.setPadding(0, 0, 0, bottomMargin);
- mDismissTargetContainer.setClipToPadding(false);
- mDismissTargetContainer.setClipChildren(false);
- mDismissTargetContainer.addView(mDismissTargetCircle);
- mDismissTargetContainer.setVisibility(View.INVISIBLE);
- mDismissTargetContainer.setBackgroundResource(
- R.drawable.floating_dismiss_gradient_transition);
- addView(mDismissTargetContainer);
-
- // Start translated down so the target springs up.
- mDismissTargetCircle.setTranslationY(
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height));
+ mDismissView = new DismissView(context);
+ addView(mDismissView);
final ContentResolver contentResolver = getContext().getContentResolver();
final int dismissRadius = Settings.Secure.getInt(
@@ -865,13 +864,23 @@ public class BubbleStackView extends FrameLayout
// Save the MagneticTarget instance for the newly set up view - we'll add this to the
// MagnetizedObjects.
- mMagneticTarget = new MagnetizedObject.MagneticTarget(mDismissTargetCircle, dismissRadius);
+ mMagneticTarget = new MagnetizedObject.MagneticTarget(
+ mDismissView.getCircle(), dismissRadius);
setClipChildren(false);
setFocusable(true);
mBubbleContainer.bringToFront();
- setUpOverflow();
+ mBubbleOverflow = new BubbleOverflow(getContext(), this);
+ mBubbleContainer.addView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() /* index */,
+ new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ updateOverflow();
+ mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
+ setSelectedBubble(mBubbleOverflow);
+ showManageMenu(false);
+ });
mOnImeVisibilityChanged = onImeVisibilityChanged;
mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
@@ -897,7 +906,7 @@ public class BubbleStackView extends FrameLayout
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
mStackAnimationController.updateResources(mOrientation);
- mBubbleOverflow.updateDimensions();
+ mBubbleOverflow.updateResources();
// Need to update the padding around the view
WindowInsets insets = getRootWindowInsets();
@@ -1126,12 +1135,9 @@ public class BubbleStackView extends FrameLayout
Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation);
}
if (mShouldShowManageEducation) {
- mManageEducationView = (BubbleManageEducationView)
- mInflater.inflate(R.layout.bubbles_manage_button_education, this,
+ mManageEducationView = (ManageEducationView)
+ mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */,
false /* attachToRoot */);
- mManageEducationView.setVisibility(GONE);
- mManageEducationView.setElevation(mBubbleElevation);
- mManageEducationView.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
addView(mManageEducationView);
}
}
@@ -1151,32 +1157,21 @@ public class BubbleStackView extends FrameLayout
addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
}
- private void setUpOverflow() {
- int overflowBtnIndex = 0;
- if (mBubbleOverflow == null) {
- mBubbleOverflow = new BubbleOverflow(getContext());
- mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
- } else {
- mBubbleContainer.removeView(mBubbleOverflow.getIconView());
- mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
- overflowBtnIndex = mBubbleContainer.getChildCount();
- }
- mBubbleContainer.addView(mBubbleOverflow.getIconView(), overflowBtnIndex,
- new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mBubbleOverflow.getIconView().setOnClickListener(v -> {
- setSelectedBubble(mBubbleOverflow);
- showManageMenu(false);
- });
+ private void updateOverflow() {
+ mBubbleOverflow.update();
+ mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() - 1 /* index */);
updateOverflowVisibility();
}
+
/**
* Handle theme changes.
*/
public void onThemeChanged() {
setUpFlyout();
- setUpOverflow();
setUpUserEducation();
setUpManageMenu();
+ updateOverflow();
updateExpandedViewTheme();
}
@@ -1225,7 +1220,7 @@ public class BubbleStackView extends FrameLayout
/** Respond to the display size change by recalculating view size and location. */
public void onDisplaySizeChanged() {
- setUpOverflow();
+ updateOverflow();
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getRealSize(mDisplaySize);
@@ -1243,12 +1238,7 @@ public class BubbleStackView extends FrameLayout
}
mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
mStackAnimationController.updateResources(mOrientation);
-
- final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
- mDismissTargetCircle.getLayoutParams().width = targetSize;
- mDismissTargetCircle.getLayoutParams().height = targetSize;
- mDismissTargetCircle.requestLayout();
-
+ mDismissView.updateResources();
mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2);
}
@@ -1549,13 +1539,25 @@ public class BubbleStackView extends FrameLayout
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "setSelectedBubble: " + bubbleToSelect);
}
- if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) {
+
+ if (bubbleToSelect == null) {
+ mBubbleData.setShowingOverflow(false);
return;
}
- if (bubbleToSelect == null || bubbleToSelect.getKey() != BubbleOverflow.KEY) {
- mBubbleData.setShowingOverflow(false);
- } else {
+
+ // Ignore this new bubble only if it is the exact same bubble object. Otherwise, we'll want
+ // to re-render it even if it has the same key (equals() returns true). If the currently
+ // expanded bubble is removed and instantly re-added, we'll get back a new Bubble instance
+ // with the same key (with newly inflated expanded views), and we need to render those new
+ // views.
+ if (mExpandedBubble == bubbleToSelect) {
+ return;
+ }
+
+ if (bubbleToSelect.getKey() == BubbleOverflow.KEY) {
mBubbleData.setShowingOverflow(true);
+ } else {
+ mBubbleData.setShowingOverflow(false);
}
if (mIsExpanded && mIsExpansionAnimating) {
@@ -1629,6 +1631,13 @@ public class BubbleStackView extends FrameLayout
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "setExpanded: " + shouldExpand);
}
+
+ if (!shouldExpand) {
+ // If we're collapsing, release the animating-out surface immediately since we have no
+ // need for it, and this ensures it cannot remain visible as we collapse.
+ releaseAnimatingOutBubbleBuffer();
+ }
+
if (shouldExpand == mIsExpanded) {
return;
}
@@ -1741,28 +1750,8 @@ public class BubbleStackView extends FrameLayout
&& mManageEducationView.getVisibility() != VISIBLE
&& mIsExpanded
&& mExpandedBubble.getExpandedView() != null) {
- mManageEducationView.setAlpha(0);
- mManageEducationView.setVisibility(VISIBLE);
- mManageEducationView.post(() -> {
- mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
- final int viewHeight = mManageEducationView.getManageViewHeight();
- final int inset = getResources().getDimensionPixelSize(
- R.dimen.bubbles_manage_education_top_inset);
- mManageEducationView.bringToFront();
- mManageEducationView.setManageViewPosition(0, mTempRect.top - viewHeight + inset);
- mManageEducationView.animate()
- .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
- .setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
- mManageEducationView.findViewById(R.id.manage).setOnClickListener(view -> {
- mExpandedBubble.getExpandedView().findViewById(R.id.settings_button)
- .performClick();
- maybeShowManageEducation(false);
- });
- mManageEducationView.findViewById(R.id.got_it).setOnClickListener(view ->
- maybeShowManageEducation(false));
- mManageEducationView.setOnClickListener(view ->
- maybeShowManageEducation(false));
- });
+ mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect,
+ () -> maybeShowManageEducation(false) /* run on click */);
Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
} else if (!show
&& mManageEducationView.getVisibility() == VISIBLE
@@ -2177,6 +2166,18 @@ public class BubbleStackView extends FrameLayout
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() != MotionEvent.ACTION_DOWN && ev.getActionIndex() != mPointerIndexDown) {
+ // Ignore touches from additional pointer indices.
+ return false;
+ }
+
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mPointerIndexDown = ev.getActionIndex();
+ } else if (ev.getAction() == MotionEvent.ACTION_UP
+ || ev.getAction() == MotionEvent.ACTION_CANCEL) {
+ mPointerIndexDown = -1;
+ }
+
boolean dispatched = super.dispatchTouchEvent(ev);
// If a new bubble arrives while the collapsed stack is being dragged, it will be positioned
@@ -2295,48 +2296,6 @@ public class BubbleStackView extends FrameLayout
}
}
- /** Animates in the dismiss target. */
- private void springInDismissTargetMaybe() {
- if (mShowingDismiss) {
- return;
- }
-
- mShowingDismiss = true;
-
- mDismissTargetContainer.bringToFront();
- mDismissTargetContainer.setZ(Short.MAX_VALUE - 1);
- mDismissTargetContainer.setVisibility(VISIBLE);
-
- ((TransitionDrawable) mDismissTargetContainer.getBackground()).startTransition(
- DISMISS_TRANSITION_DURATION_MS);
-
- mDismissTargetAnimator.cancel();
- mDismissTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y, 0f, mDismissTargetSpring)
- .start();
- }
-
- /**
- * Animates the dismiss target out, as well as the circle that encircles the bubbles, if they
- * were dragged into the target and encircled.
- */
- private void hideDismissTarget() {
- if (!mShowingDismiss) {
- return;
- }
-
- mShowingDismiss = false;
-
- ((TransitionDrawable) mDismissTargetContainer.getBackground()).reverseTransition(
- DISMISS_TRANSITION_DURATION_MS);
-
- mDismissTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y, mDismissTargetContainer.getHeight(),
- mDismissTargetSpring)
- .withEndActions(() -> mDismissTargetContainer.setVisibility(View.INVISIBLE))
- .start();
- }
-
/** Animates the flyout collapsed (to dot), or the reverse, starting with the given velocity. */
private void animateFlyoutCollapsed(boolean collapsed, float velX) {
final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
@@ -2358,7 +2317,7 @@ public class BubbleStackView extends FrameLayout
* Calculates the y position of the expanded view when it is expanded.
*/
float getExpandedViewY() {
- return getStatusBarHeight() + mBubbleSize + mBubblePaddingTop + mPointerHeight;
+ return getStatusBarHeight() + mBubbleSize + mBubblePaddingTop;
}
/**
@@ -2631,7 +2590,7 @@ public class BubbleStackView extends FrameLayout
* expanded bubble.
*/
private void screenshotAnimatingOutBubbleIntoSurface(Consumer<Boolean> onComplete) {
- if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
+ if (!mIsExpanded || mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
// You can't animate null.
onComplete.accept(false);
return;
@@ -2824,23 +2783,13 @@ public class BubbleStackView extends FrameLayout
* @param action the user interaction enum.
*/
private void logBubbleEvent(@Nullable BubbleViewProvider provider, int action) {
- if (provider == null || provider.getKey().equals(BubbleOverflow.KEY)) {
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- mContext.getApplicationInfo().packageName,
- provider == null ? null : BubbleOverflow.KEY /* notification channel */,
- 0 /* notification ID */,
- 0 /* bubble position */,
- getBubbleCount(),
- action,
- getNormalizedXPosition(),
- getNormalizedYPosition(),
- false /* unread bubble */,
- false /* on-going bubble */,
- false /* isAppForeground (unused) */);
- return;
- }
- provider.logUIEvent(getBubbleCount(), action, getNormalizedXPosition(),
- getNormalizedYPosition(), getBubbleIndex(provider));
+ mBubbleData.logBubbleEvent(provider,
+ action,
+ mContext.getApplicationInfo().packageName,
+ getBubbleCount(),
+ getBubbleIndex(provider),
+ getNormalizedXPosition(),
+ getNormalizedYPosition());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
index ca3e2e27fa9a..f1a01be48397 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -32,8 +32,6 @@ interface BubbleViewProvider {
@Nullable View getIconView();
- void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index);
-
String getKey();
Bitmap getBadgedImage();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
new file mode 100644
index 000000000000..71faf4a2eeb7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
@@ -0,0 +1,85 @@
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.graphics.drawable.TransitionDrawable
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
+import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
+import com.android.systemui.R
+import com.android.systemui.util.DismissCircleView
+import com.android.systemui.util.animation.PhysicsAnimator
+
+/*
+ * View that handles interactions between DismissCircleView and BubbleStackView.
+ */
+class DismissView(context: Context) : FrameLayout(context) {
+
+ var circle = DismissCircleView(context).apply {
+ val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ val newParams = LayoutParams(targetSize, targetSize)
+ newParams.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ setLayoutParams(newParams)
+ setTranslationY(
+ resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height).toFloat())
+ }
+
+ var isShowing = false
+ private val animator = PhysicsAnimator.getInstance(circle)
+ private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY);
+ private val DISMISS_SCRIM_FADE_MS = 200
+ init {
+ setLayoutParams(LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
+ Gravity.BOTTOM))
+ setPadding(0, 0, 0, resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+ setClipToPadding(false)
+ setClipChildren(false)
+ setVisibility(View.INVISIBLE)
+ setBackgroundResource(
+ R.drawable.floating_dismiss_gradient_transition)
+ addView(circle)
+ }
+
+ /**
+ * Animates this view in.
+ */
+ fun show() {
+ if (isShowing) return
+ isShowing = true
+ bringToFront()
+ setZ(Short.MAX_VALUE - 1f)
+ setVisibility(View.VISIBLE)
+ (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
+ animator.cancel()
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
+ .start()
+ }
+
+ /**
+ * Animates this view out, as well as the circle that encircles the bubbles, if they
+ * were dragged into the target and encircled.
+ */
+ fun hide() {
+ if (!isShowing) return
+ isShowing = false
+ (getBackground() as TransitionDrawable).reverseTransition(DISMISS_SCRIM_FADE_MS)
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
+ spring)
+ .withEndActions({ setVisibility(View.INVISIBLE) })
+ .start()
+ }
+
+ fun updateResources() {
+ val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ circle.layoutParams.width = targetSize
+ circle.layoutParams.height = targetSize
+ circle.requestLayout()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
new file mode 100644
index 000000000000..c58ab31c4561
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.View
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+
+/**
+ * Educational view to highlight the manage button that allows a user to configure the settings
+ * for the bubble. Shown only the first time a user expands a bubble.
+ */
+class ManageEducationView @JvmOverloads constructor(
+ context: Context?,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
+ private val manageButton by lazy { findViewById<Button>(R.id.manage) }
+ private val gotItButton by lazy { findViewById<Button>(R.id.got_it) }
+ private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) }
+ private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) }
+ private var isInflated = false
+
+ init {
+ this.visibility = View.GONE
+ this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
+ this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+ }
+
+ override fun setLayoutDirection(direction: Int) {
+ super.setLayoutDirection(direction)
+ // setLayoutDirection runs before onFinishInflate
+ // so skip if views haven't inflated; otherwise we'll get NPEs
+ if (!isInflated) return
+ setDirection()
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ isInflated = true
+ setDirection()
+ setTextColor()
+ }
+
+ private fun setTextColor() {
+ val typedArray = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
+ android.R.attr.textColorPrimaryInverse))
+ val bgColor = typedArray.getColor(0 /* index */, Color.BLACK)
+ var textColor = typedArray.getColor(1 /* index */, Color.WHITE)
+ typedArray.recycle()
+ textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
+ titleTextView.setTextColor(textColor)
+ descTextView.setTextColor(textColor)
+ }
+
+ fun setDirection() {
+ manageView.setBackgroundResource(
+ if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL)
+ R.drawable.bubble_stack_user_education_bg_rtl
+ else R.drawable.bubble_stack_user_education_bg)
+ titleTextView.gravity = Gravity.START
+ descTextView.gravity = Gravity.START
+ }
+
+ fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) {
+ alpha = 0f
+ visibility = View.VISIBLE
+ post {
+ expandedView.getManageButtonBoundsOnScreen(rect)
+ with(hideMenu) {
+ manageButton
+ .setOnClickListener {
+ expandedView.findViewById<View>(R.id.settings_button).performClick()
+ this.run()
+ }
+ gotItButton.setOnClickListener { this.run() }
+ setOnClickListener { this.run() }
+ }
+ with(manageView) {
+ translationX = 0f
+ val inset = resources.getDimensionPixelSize(
+ R.dimen.bubbles_manage_education_top_inset)
+ translationY = (rect.top - manageView.height + inset).toFloat()
+ }
+ bringToFront()
+ animate()
+ .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong())
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .alpha(1f)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index 98a7cc23c67e..6e6f82b714ff 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -441,7 +441,10 @@ public class PhysicsAnimationLayout extends FrameLayout {
// Cancel physics animations on the view.
for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) {
- getAnimationFromView(property, view).cancel();
+ final DynamicAnimation animationFromView = getAnimationFromView(property, view);
+ if (animationFromView != null) {
+ animationFromView.cancel();
+ }
}
}
@@ -499,13 +502,13 @@ public class PhysicsAnimationLayout extends FrameLayout {
* Retrieves the animation of the given property from the view at the given index via the view
* tag system.
*/
- private SpringAnimation getAnimationAtIndex(
+ @Nullable private SpringAnimation getAnimationAtIndex(
DynamicAnimation.ViewProperty property, int index) {
return getAnimationFromView(property, getChildAt(index));
}
/** Retrieves the animation of the given property from the view via the view tag system. */
- private SpringAnimation getAnimationFromView(
+ @Nullable private SpringAnimation getAnimationFromView(
DynamicAnimation.ViewProperty property, View view) {
return (SpringAnimation) view.getTag(getTagIdForProperty(property));
}
@@ -536,8 +539,10 @@ public class PhysicsAnimationLayout extends FrameLayout {
final float offset = mController.getOffsetForChainedPropertyAnimation(property);
if (nextAnimInChain < getChildCount()) {
- getAnimationAtIndex(property, nextAnimInChain)
- .animateToFinalPosition(value + offset);
+ final SpringAnimation nextAnim = getAnimationAtIndex(property, nextAnimInChain);
+ if (nextAnim != null) {
+ nextAnim.animateToFinalPosition(value + offset);
+ }
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index ab7af43f91d2..f35322bd2a77 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.hardware.SensorManager;
import android.net.Uri;
import android.provider.DeviceConfig;
-import android.util.DisplayMetrics;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
@@ -62,7 +61,7 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
private final ProximitySensor mProximitySensor;
- private final DisplayMetrics mDisplayMetrics;
+ private final FalsingDataProvider mFalsingDataProvider;
private FalsingManager mInternalFalsingManager;
private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener;
private final DeviceConfigProxy mDeviceConfig;
@@ -74,18 +73,19 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
- DisplayMetrics displayMetrics, ProximitySensor proximitySensor,
+ ProximitySensor proximitySensor,
DeviceConfigProxy deviceConfig, DockManager dockManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DumpManager dumpManager,
@UiBackground Executor uiBgExecutor,
- StatusBarStateController statusBarStateController) {
- mDisplayMetrics = displayMetrics;
+ StatusBarStateController statusBarStateController,
+ FalsingDataProvider falsingDataProvider) {
mProximitySensor = proximitySensor;
mDockManager = dockManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mUiBgExecutor = uiBgExecutor;
mStatusBarStateController = statusBarStateController;
+ mFalsingDataProvider = falsingDataProvider;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
@@ -143,7 +143,7 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
} else {
mInternalFalsingManager = new BrightLineFalsingManager(
- new FalsingDataProvider(mDisplayMetrics),
+ mFalsingDataProvider,
mKeyguardUpdateMonitor,
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 95d5ad9a7a94..a50f9ce9713b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -132,7 +132,9 @@ public class BrightLineFalsingManager implements FalsingManager {
}
private void registerSensors() {
- mProximitySensor.register(mSensorEventListener);
+ if (!mDataProvider.isWirelessCharging()) {
+ mProximitySensor.register(mSensorEventListener);
+ }
}
private void unregisterSensors() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index 5494c644c22c..ea46441c8fbe 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -22,10 +22,13 @@ import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import com.android.systemui.classifier.Classifier;
+import com.android.systemui.statusbar.policy.BatteryController;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Acts as a cache and utility class for FalsingClassifiers.
*/
@@ -36,6 +39,7 @@ public class FalsingDataProvider {
private final int mWidthPixels;
private final int mHeightPixels;
+ private final BatteryController mBatteryController;
private final float mXdpi;
private final float mYdpi;
@@ -50,11 +54,13 @@ public class FalsingDataProvider {
private MotionEvent mFirstRecentMotionEvent;
private MotionEvent mLastMotionEvent;
- public FalsingDataProvider(DisplayMetrics displayMetrics) {
+ @Inject
+ public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController) {
mXdpi = displayMetrics.xdpi;
mYdpi = displayMetrics.ydpi;
mWidthPixels = displayMetrics.widthPixels;
mHeightPixels = displayMetrics.heightPixels;
+ mBatteryController = batteryController;
FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi());
FalsingClassifier.logInfo("width, height: " + getWidthPixels() + ", " + getHeightPixels());
@@ -177,6 +183,11 @@ public class FalsingDataProvider {
return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY();
}
+ /** Returns true if phone is being charged without a cable. */
+ boolean isWirelessCharging() {
+ return mBatteryController.isWirelessCharging();
+ }
+
private void recalculateData() {
if (!mDirty) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 58807f0f7025..aa3e193ddba2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -49,6 +49,11 @@ open class ControlsBindingControllerImpl @Inject constructor(
private const val SUGGESTED_STRUCTURES = 6L
private const val SUGGESTED_CONTROLS_REQUEST =
ControlsControllerImpl.SUGGESTED_CONTROLS_PER_STRUCTURE * SUGGESTED_STRUCTURES
+
+ private val emptyCallback = object : ControlsBindingController.LoadCallback {
+ override fun accept(controls: List<Control>) {}
+ override fun error(message: String) {}
+ }
}
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
@@ -283,7 +288,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
}
private inner class LoadSubscriber(
- val callback: ControlsBindingController.LoadCallback,
+ var callback: ControlsBindingController.LoadCallback,
val requestLimit: Long
) : IControlsSubscriber.Stub() {
val loadedControls = ArrayList<Control>()
@@ -337,6 +342,10 @@ open class ControlsBindingControllerImpl @Inject constructor(
if (isTerminated.get()) return
_loadCancelInternal = {}
+
+ // Reassign the callback to clear references to other areas of code. Binders such as
+ // this may not be GC'd right away, so do not hold onto these references.
+ callback = emptyCallback
currentProvider?.cancelLoadTimeout()
backgroundExecutor.execute {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index ec8bfc6fa2ae..977e46ac3b44 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -76,7 +76,8 @@ class ControlsProviderLifecycleManager(
private const val LOAD_TIMEOUT_SECONDS = 20L // seconds
private const val MAX_BIND_RETRIES = 5
private const val DEBUG = true
- private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE
+ private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
+ Context.BIND_NOT_PERCEPTIBLE
}
private val intent = Intent().apply {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
index 15d15e8ffbc7..4ed610675093 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
@@ -41,7 +41,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.LifecycleActivity
import javax.inject.Inject
-class ControlsRequestDialog @Inject constructor(
+open class ControlsRequestDialog @Inject constructor(
private val controller: ControlsController,
private val broadcastDispatcher: BroadcastDispatcher,
private val controlsListingController: ControlsListingController
@@ -51,7 +51,7 @@ class ControlsRequestDialog @Inject constructor(
private const val TAG = "ControlsRequestDialog"
}
- private lateinit var component: ComponentName
+ private lateinit var controlComponent: ComponentName
private lateinit var control: Control
private var dialog: Dialog? = null
private val callback = object : ControlsListingController.ControlsListingCallback {
@@ -86,7 +86,7 @@ class ControlsRequestDialog @Inject constructor(
finish()
}
- component = intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME) ?: run {
+ controlComponent = intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME) ?: run {
Log.e(TAG, "Request did not contain componentName")
finish()
return
@@ -103,7 +103,7 @@ class ControlsRequestDialog @Inject constructor(
super.onResume()
val label = verifyComponentAndGetLabel()
if (label == null) {
- Log.e(TAG, "The component specified (${component.flattenToString()} " +
+ Log.e(TAG, "The component specified (${controlComponent.flattenToString()} " +
"is not a valid ControlsProviderService")
finish()
return
@@ -127,16 +127,16 @@ class ControlsRequestDialog @Inject constructor(
}
private fun verifyComponentAndGetLabel(): CharSequence? {
- return controlsListingController.getAppLabel(component)
+ return controlsListingController.getAppLabel(controlComponent)
}
private fun isCurrentFavorite(): Boolean {
- val favorites = controller.getFavoritesForComponent(component)
+ val favorites = controller.getFavoritesForComponent(controlComponent)
return favorites.any { it.controls.any { it.controlId == control.controlId } }
}
fun createDialog(label: CharSequence): Dialog {
- val renderInfo = RenderInfo.lookup(this, component, control.deviceType)
+ val renderInfo = RenderInfo.lookup(this, controlComponent, control.deviceType)
val frame = LayoutInflater.from(this).inflate(R.layout.controls_dialog, null).apply {
requireViewById<ImageView>(R.id.icon).apply {
setImageDrawable(renderInfo.icon)
@@ -170,7 +170,7 @@ class ControlsRequestDialog @Inject constructor(
override fun onClick(dialog: DialogInterface?, which: Int) {
if (which == Dialog.BUTTON_POSITIVE) {
controller.addFavorite(
- componentName,
+ controlComponent,
control.structure ?: "",
ControlInfo(control.controlId, control.title, control.subtitle, control.deviceType)
)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index c073642afa4e..22d6b6bb75c3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -128,15 +128,22 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
private fun bouncerOrRun(action: Action) {
- if (!keyguardStateController.isUnlocked()) {
- context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
+ if (keyguardStateController.isShowing()) {
+ var closeGlobalActions = !keyguardStateController.isUnlocked()
+ if (closeGlobalActions) {
+ context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
- // pending actions will only run after the control state has been refreshed
- pendingAction = action
+ // pending actions will only run after the control state has been refreshed
+ pendingAction = action
+ }
activityStarter.dismissKeyguardThenExecute({
Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
- globalActionsComponent.handleShowGlobalActionsMenu()
+ if (closeGlobalActions) {
+ globalActionsComponent.handleShowGlobalActionsMenu()
+ } else {
+ action.invoke()
+ }
true
}, { pendingAction = null }, true /* afterKeyguardGone */)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 56d0fa237b82..6e8d63b2c516 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -18,7 +18,9 @@ package com.android.systemui.dagger;
import android.content.BroadcastReceiver;
-import com.android.systemui.screenshot.GlobalScreenshot.ActionProxyReceiver;
+import com.android.systemui.screenshot.ActionProxyReceiver;
+import com.android.systemui.screenshot.DeleteScreenshotReceiver;
+import com.android.systemui.screenshot.SmartActionsReceiver;
import dagger.Binds;
import dagger.Module;
@@ -30,10 +32,31 @@ import dagger.multibindings.IntoMap;
*/
@Module
public abstract class DefaultBroadcastReceiverBinder {
- /** */
+ /**
+ *
+ */
@Binds
@IntoMap
@ClassKey(ActionProxyReceiver.class)
public abstract BroadcastReceiver bindActionProxyReceiver(
ActionProxyReceiver broadcastReceiver);
+
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(DeleteScreenshotReceiver.class)
+ public abstract BroadcastReceiver bindDeleteScreenshotReceiver(
+ DeleteScreenshotReceiver broadcastReceiver);
+
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(SmartActionsReceiver.class)
+ public abstract BroadcastReceiver bindSmartActionsReceiver(
+ SmartActionsReceiver broadcastReceiver);
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index a30d9346c5ee..3d31070905d0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -22,7 +22,6 @@ import android.app.INotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -78,7 +77,7 @@ import dagger.Provides;
*
* See SystemUI/docs/dagger.md
*/
-@Module
+@Module(includes = {NightDisplayListenerModule.class})
public class DependencyProvider {
@Singleton
@@ -153,13 +152,6 @@ public class DependencyProvider {
@Singleton
@Provides
- public NightDisplayListener provideNightDisplayListener(Context context,
- @Background Handler bgHandler) {
- return new NightDisplayListener(context, bgHandler);
- }
-
- @Singleton
- @Provides
public PluginManager providePluginManager(Context context) {
return new PluginManagerImpl(context, new PluginInitializerImpl());
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/NightDisplayListenerModule.java b/packages/SystemUI/src/com/android/systemui/dagger/NightDisplayListenerModule.java
new file mode 100644
index 000000000000..7091105e63d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/NightDisplayListenerModule.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger;
+
+import android.content.Context;
+import android.hardware.display.NightDisplayListener;
+import android.os.Handler;
+import android.os.UserHandle;
+
+import com.android.systemui.dagger.qualifiers.Background;
+
+import javax.inject.Inject;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Module for providing a {@link NightDisplayListener}.
+ */
+@Module
+public class NightDisplayListenerModule {
+
+ /**
+ * Provides a {@link NightDisplayListener}.
+ *
+ * The provided listener is associated with the user as returned by
+ * {@link android.app.ActivityManager#getCurrentUser}, making an IPC call on its creation.
+ * If the current user is known, prefer using a {@link Builder}.
+ */
+ @Provides
+ public NightDisplayListener provideNightDisplayListener(Context context,
+ @Background Handler bgHandler) {
+ return new NightDisplayListener(context, bgHandler);
+ }
+
+ /**
+ * Builder to create instances of {@link NightDisplayListener}.
+ *
+ * It uses {@link UserHandle#USER_SYSTEM} as the default user.
+ */
+ public static class Builder {
+ private final Context mContext;
+ private final Handler mBgHandler;
+ private int mUserId = UserHandle.USER_SYSTEM;
+
+ @Inject
+ public Builder(Context context, @Background Handler bgHandler) {
+ mContext = context;
+ mBgHandler = bgHandler;
+ }
+
+ /**
+ * Set the userId for this builder
+ */
+ public Builder setUser(int userId) {
+ mUserId = userId;
+ return this;
+ }
+
+ /**
+ * Build a {@link NightDisplayListener} for the set user.
+ */
+ public NightDisplayListener build() {
+ return new NightDisplayListener(mContext, mUserId, mBgHandler);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 1e99a7b733e2..251ce1304930 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -39,6 +39,7 @@ import android.content.pm.ShortcutManager;
import android.content.res.Resources;
import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
+import android.hardware.display.ColorDisplayManager;
import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.media.MediaRouter2Manager;
@@ -106,6 +107,12 @@ public class SystemServicesModule {
@Provides
@Singleton
+ static ColorDisplayManager provideColorDisplayManager(Context context) {
+ return context.getSystemService(ColorDisplayManager.class);
+ }
+
+ @Provides
+ @Singleton
static ConnectivityManager provideConnectivityManagager(Context context) {
return context.getSystemService(ConnectivityManager.class);
}
@@ -312,6 +319,7 @@ public class SystemServicesModule {
@Provides
@Singleton
+ @Nullable
static WifiManager provideWifiManager(Context context) {
return context.getSystemService(WifiManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index cd0ba290db46..803e56db8ff3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -59,6 +59,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.wmshell.WindowManagerShellModule;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -71,7 +72,7 @@ import dagger.Provides;
* A dagger module for injecting default implementations of components of System UI that may be
* overridden by the System UI implementation.
*/
-@Module(includes = {DividerModule.class, QSModule.class})
+@Module(includes = {DividerModule.class, QSModule.class, WindowManagerShellModule.class})
public abstract class SystemUIDefaultModule {
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 0873328c2382..fce545b421d5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -24,6 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.assist.AssistModule;
+import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
@@ -44,6 +45,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.SensorModule;
+import com.android.systemui.util.settings.SettingsUtilModule;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;
@@ -64,10 +66,12 @@ import dagger.Provides;
LogModule.class,
PeopleHubModule.class,
SensorModule.class,
- SettingsModule.class
+ SettingsModule.class,
+ SettingsUtilModule.class
},
subcomponents = {StatusBarComponent.class,
NotificationRowComponent.class,
+ DozeComponent.class,
ExpandableNotificationRowComponent.class})
public abstract class SystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index 6b8fcd562e6f..900c11f0830e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -19,16 +19,17 @@ package com.android.systemui.dagger;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import android.content.ContentProvider;
+import android.content.Context;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactory;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.onehanded.dagger.OneHandedModule;
+import com.android.systemui.pip.phone.PipMenuActivity;
import com.android.systemui.pip.phone.dagger.PipModule;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.InjectionInflationController;
@@ -36,6 +37,7 @@ import com.android.systemui.util.InjectionInflationController;
import javax.inject.Named;
import javax.inject.Singleton;
+import dagger.BindsInstance;
import dagger.Component;
/**
@@ -49,13 +51,23 @@ import dagger.Component;
OneHandedModule.class,
PipModule.class,
SystemServicesModule.class,
- SystemUIFactory.ContextHolder.class,
SystemUIBinder.class,
SystemUIModule.class,
SystemUIDefaultModule.class})
public interface SystemUIRootComponent {
/**
+ * Builder for a SystemUIRootComponent.
+ */
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ Builder context(Context context);
+
+ SystemUIRootComponent build();
+ }
+
+ /**
* Provides a BootCompleteCache.
*/
@Singleton
@@ -77,7 +89,7 @@ public interface SystemUIRootComponent {
* Main dependency providing module.
*/
@Singleton
- Dependency.DependencyInjector createDependency();
+ Dependency createDependency();
/** */
@Singleton
@@ -121,4 +133,9 @@ public interface SystemUIRootComponent {
* Member injection into the supplied argument.
*/
void inject(KeyguardSliceProvider keyguardSliceProvider);
+
+ /**
+ * Member injection into the supplied argument.
+ */
+ void inject(PipMenuActivity pipMenuActivity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index 4fea45c39d5d..60ee806d0a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -51,6 +51,14 @@ public class AlwaysOnDisplayPolicy {
static final String KEY_WALLPAPER_VISIBILITY_MS = "wallpaper_visibility_timeout";
static final String KEY_WALLPAPER_FADE_OUT_MS = "wallpaper_fade_out_duration";
+
+ /**
+ * Integer used to dim the screen while dozing.
+ *
+ * @see R.integer.config_screenBrightnessDoze
+ */
+ public int defaultDozeBrightness;
+
/**
* Integer array to map ambient brightness type to real screen brightness.
*
@@ -165,6 +173,8 @@ public class AlwaysOnDisplayPolicy {
DEFAULT_WALLPAPER_FADE_OUT_MS);
wallpaperVisibilityDuration = mParser.getLong(KEY_WALLPAPER_VISIBILITY_MS,
DEFAULT_WALLPAPER_VISIBILITY_MS);
+ defaultDozeBrightness = resources.getInteger(
+ com.android.internal.R.integer.config_screenBrightnessDoze);
screenBrightnessArray = mParser.getIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
resources.getIntArray(
R.array.config_doze_brightness_sensor_to_brightness));
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
index abd41d4318bd..5eb9808ebd7c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
@@ -16,20 +16,22 @@
package com.android.systemui.doze;
-import android.content.Context;
-
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
+import com.android.systemui.doze.dagger.DozeScope;
+
+import javax.inject.Inject;
/**
* Controls removing Keyguard authorization when the phone goes to sleep.
*/
+@DozeScope
public class DozeAuthRemover implements DozeMachine.Part {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- public DozeAuthRemover(Context context) {
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+ @Inject
+ public DozeAuthRemover(KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index 554457b3564a..2a3d67fd7a8d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -22,33 +22,41 @@ import android.util.Log;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeMachine.State;
+import com.android.systemui.doze.dagger.DozeScope;
import java.io.PrintWriter;
+import javax.inject.Inject;
+
/**
* Handles dock events for ambient state changes.
*/
+@DozeScope
public class DozeDockHandler implements DozeMachine.Part {
private static final String TAG = "DozeDockHandler";
private static final boolean DEBUG = DozeService.DEBUG;
private final AmbientDisplayConfiguration mConfig;
- private final DozeMachine mMachine;
+ private DozeMachine mMachine;
private final DockManager mDockManager;
private final DockEventListener mDockEventListener;
private int mDockState = DockManager.STATE_NONE;
- DozeDockHandler(AmbientDisplayConfiguration config, DozeMachine machine,
- DockManager dockManager) {
- mMachine = machine;
+ @Inject
+ DozeDockHandler(AmbientDisplayConfiguration config, DockManager dockManager) {
mConfig = config;
mDockManager = dockManager;
mDockEventListener = new DockEventListener();
}
@Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
+ @Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case INITIALIZED:
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
deleted file mode 100644
index 3bac196ca59f..000000000000
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.doze;
-
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.IWallpaperManager;
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.Handler;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dock.DockManager;
-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.statusbar.policy.BatteryController;
-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;
-
-import javax.inject.Inject;
-
-public class DozeFactory {
-
- private final FalsingManager mFalsingManager;
- private final DozeLog mDozeLog;
- private final DozeParameters mDozeParameters;
- private final BatteryController mBatteryController;
- private final AsyncSensorManager mAsyncSensorManager;
- private final AlarmManager mAlarmManager;
- private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final DockManager mDockManager;
- private final IWallpaperManager mWallpaperManager;
- private final ProximitySensor mProximitySensor;
- private final ProximitySensor.ProximityCheck mProximityCheck;
- private final DelayedWakeLock.Builder mDelayedWakeLockBuilder;
- private final Handler mHandler;
- private final BiometricUnlockController mBiometricUnlockController;
- private final BroadcastDispatcher mBroadcastDispatcher;
- private final DozeHost mDozeHost;
-
- @Inject
- public DozeFactory(FalsingManager falsingManager, DozeLog dozeLog,
- DozeParameters dozeParameters, BatteryController batteryController,
- AsyncSensorManager asyncSensorManager, AlarmManager alarmManager,
- WakefulnessLifecycle wakefulnessLifecycle, KeyguardUpdateMonitor keyguardUpdateMonitor,
- DockManager dockManager, @Nullable IWallpaperManager wallpaperManager,
- ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proximityCheck,
- DelayedWakeLock.Builder delayedWakeLockBuilder, @Main Handler handler,
- BiometricUnlockController biometricUnlockController,
- BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) {
- mFalsingManager = falsingManager;
- mDozeLog = dozeLog;
- mDozeParameters = dozeParameters;
- mBatteryController = batteryController;
- mAsyncSensorManager = asyncSensorManager;
- mAlarmManager = alarmManager;
- mWakefulnessLifecycle = wakefulnessLifecycle;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mDockManager = dockManager;
- mWallpaperManager = wallpaperManager;
- mProximitySensor = proximitySensor;
- mProximityCheck = proximityCheck;
- mDelayedWakeLockBuilder = delayedWakeLockBuilder;
- mHandler = handler;
- mBiometricUnlockController = biometricUnlockController;
- mBroadcastDispatcher = broadcastDispatcher;
- mDozeHost = dozeHost;
- }
-
- /** Creates a DozeMachine with its parts for {@code dozeService}. */
- DozeMachine assembleMachine(DozeService dozeService) {
- AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(dozeService);
- WakeLock wakeLock = mDelayedWakeLockBuilder.setHandler(mHandler).setTag("Doze").build();
-
- DozeMachine.Service wrappedService = dozeService;
- wrappedService = new DozeBrightnessHostForwarder(wrappedService, mDozeHost);
- wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(
- wrappedService, mDozeParameters);
- wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(
- wrappedService, mDozeParameters);
-
- DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock,
- mWakefulnessLifecycle, mBatteryController, mDozeLog, mDockManager,
- mDozeHost);
- machine.setParts(new DozeMachine.Part[]{
- new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()),
- new DozeFalsingManagerAdapter(mFalsingManager),
- createDozeTriggers(dozeService, mAsyncSensorManager, mDozeHost,
- mAlarmManager, config, mDozeParameters, wakeLock,
- machine, mDockManager, mDozeLog, mProximityCheck),
- createDozeUi(dozeService, mDozeHost, wakeLock, machine, mHandler,
- mAlarmManager, mDozeParameters, mDozeLog),
- new DozeScreenState(wrappedService, mHandler, mDozeHost, mDozeParameters,
- wakeLock),
- createDozeScreenBrightness(dozeService, wrappedService, mAsyncSensorManager,
- mDozeHost, mDozeParameters, mHandler),
- new DozeWallpaperState(mWallpaperManager, mBiometricUnlockController,
- mDozeParameters),
- new DozeDockHandler(config, machine, mDockManager),
- new DozeAuthRemover(dozeService)
- });
-
- return machine;
- }
-
- private DozeMachine.Part createDozeScreenBrightness(Context context,
- DozeMachine.Service service, SensorManager sensorManager, DozeHost host,
- DozeParameters params, Handler handler) {
- Sensor sensor = DozeSensors.findSensorWithType(sensorManager,
- context.getString(R.string.doze_brightness_sensor_type));
- return new DozeScreenBrightness(context, service, sensorManager, sensor,
- mBroadcastDispatcher, host, handler, params.getPolicy());
- }
-
- private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
- DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
- DozeParameters params, WakeLock wakeLock,
- DozeMachine machine, DockManager dockManager, DozeLog dozeLog,
- ProximitySensor.ProximityCheck proximityCheck) {
- boolean allowPulseTriggers = true;
- return new DozeTriggers(context, machine, host, alarmManager, config, params,
- sensorManager, wakeLock, allowPulseTriggers, dockManager,
- mProximitySensor, proximityCheck, dozeLog, mBroadcastDispatcher);
-
- }
-
- private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
- DozeMachine machine, Handler handler, AlarmManager alarmManager,
- DozeParameters params, DozeLog dozeLog) {
- return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params,
- mKeyguardUpdateMonitor, dozeLog);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java
index 250308d4c3a6..94b8ba305d0c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java
@@ -16,15 +16,20 @@
package com.android.systemui.doze;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.plugins.FalsingManager;
+import javax.inject.Inject;
+
/**
* Notifies FalsingManager of whether or not AOD is showing.
*/
+@DozeScope
public class DozeFalsingManagerAdapter implements DozeMachine.Part {
private final FalsingManager mFalsingManager;
+ @Inject
public DozeFalsingManagerAdapter(FalsingManager falsingManager) {
mFalsingManager = falsingManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index ae7d82ac4a5e..1ef806c8bd68 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -28,6 +28,8 @@ import android.view.Display;
import com.android.internal.util.Preconditions;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -38,6 +40,8 @@ import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
import java.util.ArrayList;
+import javax.inject.Inject;
+
/**
* Orchestrates all things doze.
*
@@ -46,6 +50,7 @@ import java.util.ArrayList;
*
* During state transitions and in certain states, DozeMachine holds a wake lock.
*/
+@DozeScope
public class DozeMachine {
static final String TAG = "DozeMachine";
@@ -146,9 +151,11 @@ public class DozeMachine {
private boolean mWakeLockHeldForCurrentState = false;
private DockManager mDockManager;
- public DozeMachine(Service service, AmbientDisplayConfiguration config, WakeLock wakeLock,
- WakefulnessLifecycle wakefulnessLifecycle, BatteryController batteryController,
- DozeLog dozeLog, DockManager dockManager, DozeHost dozeHost) {
+ @Inject
+ public DozeMachine(@WrappedService Service service, AmbientDisplayConfiguration config,
+ WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle,
+ BatteryController batteryController, DozeLog dozeLog, DockManager dockManager,
+ DozeHost dozeHost, Part[] parts) {
mDozeService = service;
mConfig = config;
mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -157,6 +164,10 @@ public class DozeMachine {
mDozeLog = dozeLog;
mDockManager = dockManager;
mDozeHost = dozeHost;
+ mParts = parts;
+ for (Part part : parts) {
+ part.setDozeMachine(this);
+ }
}
/**
@@ -168,12 +179,6 @@ public class DozeMachine {
}
}
- /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */
- public void setParts(Part[] parts) {
- Preconditions.checkState(mParts == null);
- mParts = parts;
- }
-
/**
* Requests transitioning to {@code requestedState}.
*
@@ -359,9 +364,6 @@ public class DozeMachine {
Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
return mState;
}
- if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) {
- return State.DOZE;
- }
if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
return mState;
@@ -432,6 +434,9 @@ public class DozeMachine {
/** Alerts that the screenstate is being changed. */
default void onScreenState(int state) {}
+
+ /** Sets the {@link DozeMachine} when this Part is associated with one. */
+ default void setDozeMachine(DozeMachine dozeMachine) {}
}
/** A wrapper interface for {@link android.service.dreams.DreamService} */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
index 58f144830650..2b4067865415 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
@@ -19,25 +19,35 @@ package com.android.systemui.doze;
import android.app.AlarmManager;
import android.os.Handler;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.util.AlarmTimeout;
+import javax.inject.Inject;
+
/**
* Moves the doze machine from the pausing to the paused state after a timeout.
*/
+@DozeScope
public class DozePauser implements DozeMachine.Part {
public static final String TAG = DozePauser.class.getSimpleName();
private final AlarmTimeout mPauseTimeout;
- private final DozeMachine mMachine;
+ private DozeMachine mMachine;
private final AlwaysOnDisplayPolicy mPolicy;
- public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager,
+ @Inject
+ public DozePauser(@Main Handler handler, AlarmManager alarmManager,
AlwaysOnDisplayPolicy policy) {
- mMachine = machine;
mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler);
mPolicy = policy;
}
@Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
+ @Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case DOZE_AOD_PAUSING:
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 64cfb4bcd058..342818de3d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -19,7 +19,6 @@ package com.android.systemui.doze;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -31,12 +30,17 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.view.Display;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.doze.dagger.BrightnessSensor;
+import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.doze.dagger.WrappedService;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+
+import javax.inject.Inject;
/**
* Controls the screen brightness when dozing.
*/
+@DozeScope
public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
SensorEventListener {
private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties
@@ -51,10 +55,8 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
private final Handler mHandler;
private final SensorManager mSensorManager;
private final Sensor mLightSensor;
- private final BroadcastDispatcher mBroadcastDispatcher;
private final int[] mSensorToBrightness;
private final int[] mSensorToScrimOpacity;
- private final boolean mDebuggable;
private boolean mRegistered;
private int mDefaultDozeBrightness;
@@ -71,40 +73,20 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
private int mDebugBrightnessBucket = -1;
private DozeMachine.State mState;
- @VisibleForTesting
- public DozeScreenBrightness(Context context, DozeMachine.Service service,
- SensorManager sensorManager, Sensor lightSensor,
- BroadcastDispatcher broadcastDispatcher, DozeHost host,
- Handler handler, int defaultDozeBrightness, int[] sensorToBrightness,
- int[] sensorToScrimOpacity, boolean debuggable) {
+ @Inject
+ public DozeScreenBrightness(Context context, @WrappedService DozeMachine.Service service,
+ AsyncSensorManager sensorManager, @BrightnessSensor Sensor lightSensor,
+ DozeHost host, Handler handler, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy) {
mContext = context;
mDozeService = service;
mSensorManager = sensorManager;
mLightSensor = lightSensor;
- mBroadcastDispatcher = broadcastDispatcher;
mDozeHost = host;
mHandler = handler;
- mDebuggable = debuggable;
-
- mDefaultDozeBrightness = defaultDozeBrightness;
- mSensorToBrightness = sensorToBrightness;
- mSensorToScrimOpacity = sensorToScrimOpacity;
-
- if (mDebuggable) {
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_AOD_BRIGHTNESS);
- mBroadcastDispatcher.registerReceiverWithHandler(this, filter, handler, UserHandle.ALL);
- }
- }
- public DozeScreenBrightness(Context context, DozeMachine.Service service,
- SensorManager sensorManager, Sensor lightSensor,
- BroadcastDispatcher broadcastDispatcher, DozeHost host, Handler handler,
- AlwaysOnDisplayPolicy policy) {
- this(context, service, sensorManager, lightSensor, broadcastDispatcher, host, handler,
- context.getResources().getInteger(
- com.android.internal.R.integer.config_screenBrightnessDoze),
- policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS);
+ mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness;
+ mSensorToBrightness = alwaysOnDisplayPolicy.screenBrightnessArray;
+ mSensorToScrimOpacity = alwaysOnDisplayPolicy.dimmingScrimArray;
}
@Override
@@ -139,9 +121,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
private void onDestroy() {
setLightSensorEnabled(false);
- if (mDebuggable) {
- mBroadcastDispatcher.unregisterReceiver(this);
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 915359374bfe..8c50a16b566f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -26,13 +26,19 @@ import android.os.Handler;
import android.util.Log;
import android.view.Display;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import javax.inject.Inject;
+
/**
* Controls the screen when dozing.
*/
+@DozeScope
public class DozeScreenState implements DozeMachine.Part {
private static final boolean DEBUG = DozeService.DEBUG;
@@ -59,8 +65,9 @@ public class DozeScreenState implements DozeMachine.Part {
private int mPendingScreenState = Display.STATE_UNKNOWN;
private SettableWakeLock mWakeLock;
- public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host,
- DozeParameters parameters, WakeLock wakeLock) {
+ @Inject
+ public DozeScreenState(@WrappedService DozeMachine.Service service, @Main Handler handler,
+ DozeHost host, DozeParameters parameters, WakeLock wakeLock) {
mDozeService = service;
mHandler = handler;
mParameters = parameters;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index aebf41b884c4..524d9c8536b8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -21,8 +21,6 @@ import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_
import android.annotation.AnyThread;
import android.app.ActivityManager;
-import android.app.AlarmManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
@@ -50,6 +48,7 @@ import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
@@ -64,13 +63,11 @@ public class DozeSensors {
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
private final Context mContext;
- private final AlarmManager mAlarmManager;
private final AsyncSensorManager mSensorManager;
- private final ContentResolver mResolver;
- private final DozeParameters mDozeParameters;
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
+ private final SecureSettings mSecureSettings;
private final Callback mCallback;
@VisibleForTesting
protected TriggerSensor[] mSensors;
@@ -98,18 +95,16 @@ public class DozeSensors {
}
}
- public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
+ DozeSensors(Context context, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
- ProximitySensor proximitySensor) {
+ ProximitySensor proximitySensor, SecureSettings secureSettings) {
mContext = context;
- mAlarmManager = alarmManager;
mSensorManager = sensorManager;
- mDozeParameters = dozeParameters;
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
- mResolver = mContext.getContentResolver();
+ mSecureSettings = secureSettings;
mCallback = callback;
mProximitySensor = proximitySensor;
@@ -206,7 +201,10 @@ public class DozeSensors {
return findSensorWithType(mSensorManager, type);
}
- static Sensor findSensorWithType(SensorManager sensorManager, String type) {
+ /**
+ * Utility method to find a {@link Sensor} for the supplied string type.
+ */
+ public static Sensor findSensorWithType(SensorManager sensorManager, String type) {
if (TextUtils.isEmpty(type)) {
return null;
}
@@ -243,7 +241,7 @@ public class DozeSensors {
}
if (!anyListening) {
- mResolver.unregisterContentObserver(mSettingsObserver);
+ mSecureSettings.unregisterContentObserver(mSettingsObserver);
} else if (!mSettingRegistered) {
for (TriggerSensor s : mSensors) {
s.registerSettingsObserver(mSettingsObserver);
@@ -402,7 +400,7 @@ public class DozeSensors {
} else if (TextUtils.isEmpty(mSetting)) {
return true;
}
- return Settings.Secure.getIntForUser(mResolver, mSetting, mSettingDefault ? 1 : 0,
+ return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
UserHandle.USER_CURRENT) != 0;
}
@@ -446,9 +444,8 @@ public class DozeSensors {
public void registerSettingsObserver(ContentObserver settingsObserver) {
if (mConfigured && !TextUtils.isEmpty(mSetting)) {
- mResolver.registerContentObserver(
- Settings.Secure.getUriFor(mSetting), false /* descendants */,
- mSettingsObserver, UserHandle.USER_ALL);
+ mSecureSettings.registerContentObserverForUser(
+ mSetting, mSettingsObserver, UserHandle.USER_ALL);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index d2bebb7b27d1..19b0ea1db04e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -22,6 +22,7 @@ import android.os.SystemClock;
import android.service.dreams.DreamService;
import android.util.Log;
+import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.plugins.DozeServicePlugin;
import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
import com.android.systemui.plugins.PluginListener;
@@ -36,16 +37,16 @@ public class DozeService extends DreamService
implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> {
private static final String TAG = "DozeService";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final DozeFactory mDozeFactory;
+ private final DozeComponent.Builder mDozeComponentBuilder;
private DozeMachine mDozeMachine;
private DozeServicePlugin mDozePlugin;
private PluginManager mPluginManager;
@Inject
- public DozeService(DozeFactory dozeFactory, PluginManager pluginManager) {
+ public DozeService(DozeComponent.Builder dozeComponentBuilder, PluginManager pluginManager) {
+ mDozeComponentBuilder = dozeComponentBuilder;
setDebug(DEBUG);
- mDozeFactory = dozeFactory;
mPluginManager = pluginManager;
}
@@ -56,7 +57,8 @@ public class DozeService extends DreamService
setWindowless(true);
mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */);
- mDozeMachine = mDozeFactory.assembleMachine(this);
+ DozeComponent dozeComponent = mDozeComponentBuilder.build(this);
+ mDozeMachine = dozeComponent.getDozeMachine();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index eb2463b02ae4..e38dce05a32e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,19 +41,24 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
import java.util.Optional;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
/**
* Handles triggers for ambient state changes.
*/
+@DozeScope
public class DozeTriggers implements DozeMachine.Part {
private static final String TAG = "DozeTriggers";
@@ -73,7 +78,7 @@ public class DozeTriggers implements DozeMachine.Part {
private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
private final Context mContext;
- private final DozeMachine mMachine;
+ private DozeMachine mMachine;
private final DozeLog mDozeLog;
private final DozeSensors mDozeSensors;
private final DozeHost mDozeHost;
@@ -153,22 +158,24 @@ public class DozeTriggers implements DozeMachine.Part {
}
}
- public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
+ @Inject
+ public DozeTriggers(Context context, DozeHost dozeHost,
AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters dozeParameters, AsyncSensorManager sensorManager,
- WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
+ WakeLock wakeLock, DockManager dockManager,
ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
- DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) {
+ DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
+ SecureSettings secureSettings) {
mContext = context;
- mMachine = machine;
mDozeHost = dozeHost;
mConfig = config;
mDozeParameters = dozeParameters;
mSensorManager = sensorManager;
mWakeLock = wakeLock;
- mAllowPulseTriggers = allowPulseTriggers;
- mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor);
+ mAllowPulseTriggers = true;
+ mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
+ config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
+ secureSettings);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
mProxCheck = proxCheck;
@@ -177,6 +184,11 @@ public class DozeTriggers implements DozeMachine.Part {
}
@Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
+ @Override
public void destroy() {
mDozeSensors.destroy();
}
@@ -399,10 +411,13 @@ public class DozeTriggers implements DozeMachine.Part {
break;
case DOZE_PULSING:
case DOZE_PULSING_BRIGHT:
- case DOZE_AOD_DOCKED:
mWantProx = true;
mWantTouchScreenSensors = false;
break;
+ case DOZE_AOD_DOCKED:
+ mWantProx = false;
+ mWantTouchScreenSensors = false;
+ break;
case DOZE_PULSE_DONE:
mDozeSensors.requestTemporaryDisable();
// A pulse will temporarily disable sensors that require a touch screen.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 1c056215f1cb..0fdaae82e2d0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -29,15 +29,20 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
import java.util.Calendar;
+import javax.inject.Inject;
+
/**
* The policy controlling doze.
*/
+@DozeScope
public class DozeUi implements DozeMachine.Part {
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
@@ -45,7 +50,7 @@ public class DozeUi implements DozeMachine.Part {
private final DozeHost mHost;
private final Handler mHandler;
private final WakeLock mWakeLock;
- private final DozeMachine mMachine;
+ private DozeMachine mMachine;
private final AlarmTimeout mTimeTicker;
private final boolean mCanAnimateTransition;
private final DozeParameters mDozeParameters;
@@ -64,12 +69,12 @@ public class DozeUi implements DozeMachine.Part {
private long mLastTimeTickElapsed = 0;
- public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
- WakeLock wakeLock, DozeHost host, Handler handler,
+ @Inject
+ public DozeUi(Context context, AlarmManager alarmManager,
+ WakeLock wakeLock, DozeHost host, @Main Handler handler,
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
DozeLog dozeLog) {
mContext = context;
- mMachine = machine;
mWakeLock = wakeLock;
mHost = host;
mHandler = handler;
@@ -80,6 +85,11 @@ public class DozeUi implements DozeMachine.Part {
mDozeLog = dozeLog;
}
+ @Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
/**
* Decide if we're taking over the screen-off animation
* when the device was configured to skip doze after screen off.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 7aeb7851bbd1..d5b6cb1a6250 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -21,15 +21,19 @@ import android.app.IWallpaperManager;
import android.os.RemoteException;
import android.util.Log;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
import java.io.PrintWriter;
+import javax.inject.Inject;
+
/**
* Propagates doze state to wallpaper engine.
*/
+@DozeScope
public class DozeWallpaperState implements DozeMachine.Part {
private static final String TAG = "DozeWallpaperState";
@@ -41,8 +45,9 @@ public class DozeWallpaperState implements DozeMachine.Part {
private final BiometricUnlockController mBiometricUnlockController;
private boolean mIsAmbientMode;
+ @Inject
public DozeWallpaperState(
- IWallpaperManager wallpaperManagerService,
+ @Nullable IWallpaperManager wallpaperManagerService,
BiometricUnlockController biometricUnlockController,
DozeParameters parameters) {
mWallpaperManagerService = wallpaperManagerService;
diff --git a/libs/hwui/GlFunctorLifecycleListener.h b/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java
index 5adc46961c8b..5af8af366b69 100644
--- a/libs/hwui/GlFunctorLifecycleListener.h
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,17 @@
* limitations under the License.
*/
-#pragma once
+package com.android.systemui.doze.dagger;
-#include <utils/Functor.h>
-#include <utils/RefBase.h>
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
-namespace android {
-namespace uirenderer {
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
-class GlFunctorLifecycleListener : public VirtualLightRefBase {
-public:
- virtual ~GlFunctorLifecycleListener() {}
- virtual void onGlFunctorReleased(Functor* functor) = 0;
-};
+import javax.inject.Qualifier;
-} // namespace uirenderer
-} // namespace android
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface BrightnessSensor {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java
new file mode 100644
index 000000000000..e925927e7564
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze.dagger;
+
+import com.android.systemui.doze.DozeMachine;
+import com.android.systemui.doze.DozeService;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Dagger component for items that require a {@link DozeService}.
+ */
+@Subcomponent(modules = {DozeModule.class})
+@DozeScope
+public interface DozeComponent {
+ /** Simple Builder for {@link DozeComponent}. */
+ @Subcomponent.Factory
+ interface Builder {
+ DozeComponent build(@BindsInstance DozeService dozeService);
+ }
+
+ /** Supply a {@link DozeMachine}. */
+ @DozeScope
+ DozeMachine getDozeMachine();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
new file mode 100644
index 000000000000..a12e280fcca6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze.dagger;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.os.Handler;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.doze.DozeAuthRemover;
+import com.android.systemui.doze.DozeBrightnessHostForwarder;
+import com.android.systemui.doze.DozeDockHandler;
+import com.android.systemui.doze.DozeFalsingManagerAdapter;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeMachine;
+import com.android.systemui.doze.DozePauser;
+import com.android.systemui.doze.DozeScreenBrightness;
+import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.doze.DozeScreenStatePreventingAdapter;
+import com.android.systemui.doze.DozeSensors;
+import com.android.systemui.doze.DozeService;
+import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter;
+import com.android.systemui.doze.DozeTriggers;
+import com.android.systemui.doze.DozeUi;
+import com.android.systemui.doze.DozeWallpaperState;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.wakelock.DelayedWakeLock;
+import com.android.systemui.util.wakelock.WakeLock;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for use with {@link com.android.systemui.doze.dagger.DozeComponent}. */
+@Module
+public abstract class DozeModule {
+ @Provides
+ @DozeScope
+ @WrappedService
+ static DozeMachine.Service providesWrappedService(DozeService dozeService, DozeHost dozeHost,
+ DozeParameters dozeParameters) {
+ DozeMachine.Service wrappedService = dozeService;
+ wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost);
+ wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(
+ wrappedService, dozeParameters);
+ wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(
+ wrappedService, dozeParameters);
+
+ return wrappedService;
+ }
+
+ @Provides
+ @DozeScope
+ static WakeLock providesDozeWakeLock(DelayedWakeLock.Builder delayedWakeLockBuilder,
+ @Main Handler handler) {
+ return delayedWakeLockBuilder.setHandler(handler).setTag("Doze").build();
+ }
+
+ @Provides
+ static DozeMachine.Part[] providesDozeMachinePartes(DozePauser dozePauser,
+ DozeFalsingManagerAdapter dozeFalsingManagerAdapter, DozeTriggers dozeTriggers,
+ DozeUi dozeUi, DozeScreenState dozeScreenState,
+ DozeScreenBrightness dozeScreenBrightness, DozeWallpaperState dozeWallpaperState,
+ DozeDockHandler dozeDockHandler, DozeAuthRemover dozeAuthRemover) {
+ return new DozeMachine.Part[]{
+ dozePauser,
+ dozeFalsingManagerAdapter,
+ dozeTriggers,
+ dozeUi,
+ dozeScreenState,
+ dozeScreenBrightness,
+ dozeWallpaperState,
+ dozeDockHandler,
+ dozeAuthRemover
+ };
+ }
+
+ @Provides
+ @BrightnessSensor
+ static Sensor providesBrightnessSensor(AsyncSensorManager sensorManager, Context context) {
+ return DozeSensors.findSensorWithType(sensorManager,
+ context.getString(R.string.doze_brightness_sensor_type));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
new file mode 100644
index 000000000000..7a8b8166a969
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for singleton items within the StatusBarComponent.
+ */
+@Documented
+@Retention(RUNTIME)
+@Scope
+public @interface DozeScope {}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java
new file mode 100644
index 000000000000..ca8a158f21a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface WrappedService {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index eed52004923d..ef51abb1404d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -2129,7 +2129,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private boolean mShowing;
private float mScrimAlpha;
private ResetOrientationData mResetOrientationData;
- private boolean mHadTopUi;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final NotificationShadeDepthController mDepthController;
private final SysUiState mSysUiState;
@@ -2397,8 +2396,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
public void show() {
super.show();
mShowing = true;
- mHadTopUi = mNotificationShadeWindowController.getForceHasTopUi();
- mNotificationShadeWindowController.setForceHasTopUi(true);
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
.commitUpdate(mContext.getDisplayId());
@@ -2499,7 +2497,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
dismissOverflow(true);
dismissPowerOptions(true);
if (mControlsUiController != null) mControlsUiController.hide();
- mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi);
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
mDepthController.updateGlobalDialogVisibility(0, null /* view */);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
.commitUpdate(mContext.getDisplayId());
@@ -2637,10 +2635,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
private boolean shouldShowControls() {
- return (mKeyguardStateController.isUnlocked() || mShowLockScreenCardsAndControls)
- && controlsAvailable()
- && mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id)
- != STRONG_AUTH_REQUIRED_AFTER_BOOT;
+ boolean showOnLockScreen = mShowLockScreenCardsAndControls && mLockPatternUtils
+ .getStrongAuthForUser(getCurrentUser().id) != STRONG_AUTH_REQUIRED_AFTER_BOOT;
+ return controlsAvailable()
+ && (mKeyguardStateController.isUnlocked() || showOnLockScreen);
}
private boolean controlsAvailable() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 53251ed4362d..75f4809d752f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -589,7 +589,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
if (targetUserId != ActivityManager.getCurrentUser()) {
return;
}
-
+ if (DEBUG) Log.d(TAG, "keyguardDone");
tryKeyguardDone();
}
@@ -608,6 +608,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
@Override
public void keyguardDonePending(boolean strongAuth, int targetUserId) {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
+ if (DEBUG) Log.d(TAG, "keyguardDonePending");
if (targetUserId != ActivityManager.getCurrentUser()) {
Trace.endSection();
return;
@@ -626,6 +627,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
@Override
public void keyguardGone() {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardGone");
+ if (DEBUG) Log.d(TAG, "keyguardGone");
mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false);
mKeyguardDisplayManager.hide();
Trace.endSection();
@@ -876,7 +878,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
// explicitly DO NOT want to call
// mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false)
// here, since that will mess with the device lock state.
- mUpdateMonitor.setKeyguardGoingAway(false);
+ mUpdateMonitor.dispatchKeyguardGoingAway(false);
// Lock immediately based on setting if secure (user has a pin/pattern/password).
// This also "locks" the device when not secure to provide easy access to the
@@ -1690,9 +1692,14 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
};
private void tryKeyguardDone() {
+ if (DEBUG) {
+ Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - "
+ + mHideAnimationRun + " animRunning - " + mHideAnimationRunning);
+ }
if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) {
handleKeyguardDone();
} else if (!mHideAnimationRun) {
+ if (DEBUG) Log.d(TAG, "tryKeyguardDone: starting pre-hide animation");
mHideAnimationRun = true;
mHideAnimationRunning = true;
mKeyguardViewControllerLazy.get()
@@ -1919,6 +1926,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
};
private final Runnable mHideAnimationFinishedRunnable = () -> {
+ Log.e(TAG, "mHideAnimationFinishedRunnable#run");
mHideAnimationRunning = false;
tryKeyguardDone();
};
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index 78d70877a90e..e8dba8f3b585 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -217,10 +217,10 @@ class LogBuffer(
private fun dumpMessage(message: LogMessage, pw: PrintWriter) {
pw.print(DATE_FORMAT.format(message.timestamp))
pw.print(" ")
- pw.print(message.level)
+ pw.print(message.level.shortString)
pw.print(" ")
pw.print(message.tag)
- pw.print(" ")
+ pw.print(": ")
pw.println(message.printer(message))
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt b/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
index 7b9af0f91200..53f231c9f9d2 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
@@ -21,11 +21,14 @@ import android.util.Log
/**
* Enum version of @Log.Level
*/
-enum class LogLevel(@Log.Level val nativeLevel: Int) {
- VERBOSE(Log.VERBOSE),
- DEBUG(Log.DEBUG),
- INFO(Log.INFO),
- WARNING(Log.WARN),
- ERROR(Log.ERROR),
- WTF(Log.ASSERT)
+enum class LogLevel(
+ @Log.Level val nativeLevel: Int,
+ val shortString: String
+) {
+ VERBOSE(Log.VERBOSE, "V"),
+ DEBUG(Log.DEBUG, "D"),
+ INFO(Log.INFO, "I"),
+ WARNING(Log.WARN, "W"),
+ ERROR(Log.ERROR, "E"),
+ WTF(Log.ASSERT, "WTF")
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 8ef20f89085c..f9c6307394e8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -64,6 +64,9 @@ class KeyguardMediaController @Inject constructor(
// Let's now initialize this view, which also creates the host view for us.
mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN)
mediaView.setContentView(mediaHost.hostView)
+
+ // Ensure the visibility is correct
+ updateVisibility()
}
private fun updateVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index e2215d57a094..075318b3f1f7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -2,8 +2,11 @@ package com.android.systemui.media
import android.content.Context
import android.content.Intent
+import android.content.res.Configuration
import android.graphics.Color
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
+import android.util.Log
+import android.util.MathUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -23,6 +26,7 @@ import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton
+private const val TAG = "MediaCarouselController"
private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
/**
@@ -37,9 +41,8 @@ class MediaCarouselController @Inject constructor(
private val mediaHostStatesManager: MediaHostStatesManager,
private val activityStarter: ActivityStarter,
@Main executor: DelayableExecutor,
- mediaManager: MediaDataCombineLatest,
+ mediaManager: MediaDataFilter,
configurationController: ConfigurationController,
- mediaDataManager: MediaDataManager,
falsingManager: FalsingManager
) {
/**
@@ -96,7 +99,6 @@ class MediaCarouselController @Inject constructor(
* The measured height of the carousel
*/
private var carouselMeasureHeight: Int = 0
- private var playerWidthPlusPadding: Int = 0
private var desiredHostState: MediaHostState? = null
private val mediaCarousel: MediaScrollView
private val mediaCarouselScrollHandler: MediaCarouselScrollHandler
@@ -108,6 +110,15 @@ class MediaCarouselController @Inject constructor(
private val pageIndicator: PageIndicator
private val visualStabilityCallback: VisualStabilityManager.Callback
private var needsReordering: Boolean = false
+ private var isRtl: Boolean = false
+ set(value) {
+ if (value != field) {
+ field = value
+ mediaFrame.layoutDirection =
+ if (value) View.LAYOUT_DIRECTION_RTL else View.LAYOUT_DIRECTION_LTR
+ mediaCarouselScrollHandler.scrollToStart()
+ }
+ }
private var currentlyExpanded = true
set(value) {
if (field != value) {
@@ -126,6 +137,11 @@ class MediaCarouselController @Inject constructor(
override fun onOverlayChanged() {
inflateSettingsButton()
}
+
+ override fun onConfigChanged(newConfig: Configuration?) {
+ if (newConfig == null) return
+ isRtl = newConfig.layoutDirection == View.LAYOUT_DIRECTION_RTL
+ }
}
init {
@@ -133,8 +149,9 @@ class MediaCarouselController @Inject constructor(
mediaCarousel = mediaFrame.requireViewById(R.id.media_carousel_scroller)
pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator)
mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator,
- executor, mediaDataManager::onSwipeToDismiss, this::updatePageIndicatorLocation,
+ executor, mediaManager::onSwipeToDismiss, this::updatePageIndicatorLocation,
falsingManager)
+ isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
inflateSettingsButton()
mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
configurationController.addCallback(configListener)
@@ -144,7 +161,7 @@ class MediaCarouselController @Inject constructor(
reorderAllPlayers()
}
// Let's reset our scroll position
- mediaCarousel.scrollX = 0
+ mediaCarouselScrollHandler.scrollToStart()
}
visualStabilityManager.addReorderingAllowedCallback(visualStabilityCallback,
true /* persistent */)
@@ -196,8 +213,13 @@ class MediaCarouselController @Inject constructor(
}
private fun inflateMediaCarousel(): ViewGroup {
- return LayoutInflater.from(context).inflate(R.layout.media_carousel,
+ val mediaCarousel = LayoutInflater.from(context).inflate(R.layout.media_carousel,
UniqueObjectHostView(context), false) as ViewGroup
+ // Because this is inflated when not attached to the true view hierarchy, it resolves some
+ // potential issues to force that the layout direction is defined by the locale
+ // (rather than inherited from the parent, which would resolve to LTR when unattached).
+ mediaCarousel.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+ return mediaCarousel
}
private fun reorderAllPlayers() {
@@ -216,7 +238,9 @@ class MediaCarouselController @Inject constructor(
val oldData = mediaPlayers[oldKey]
if (oldData != null) {
val oldData = mediaPlayers.remove(oldKey)
- mediaPlayers.put(key, oldData!!)
+ mediaPlayers.put(key, oldData!!)?.let {
+ Log.wtf(TAG, "new key $key already exists when migrating from $oldKey")
+ }
}
var existingPlayer = mediaPlayers[key]
if (existingPlayer == null) {
@@ -228,6 +252,7 @@ class MediaCarouselController @Inject constructor(
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
existingPlayer.view?.player?.setLayoutParams(lp)
+ existingPlayer.bind(data)
existingPlayer.setListening(currentlyExpanded)
updatePlayerToState(existingPlayer, noAnimation = true)
if (existingPlayer.isPlaying) {
@@ -235,19 +260,26 @@ class MediaCarouselController @Inject constructor(
} else {
mediaContent.addView(existingPlayer.view?.player)
}
- } else if (existingPlayer.isPlaying &&
- mediaContent.indexOfChild(existingPlayer.view?.player) != 0) {
- if (visualStabilityManager.isReorderingAllowed) {
- mediaContent.removeView(existingPlayer.view?.player)
- mediaContent.addView(existingPlayer.view?.player, 0)
- } else {
- needsReordering = true
+ } else {
+ existingPlayer.bind(data)
+ if (existingPlayer.isPlaying &&
+ mediaContent.indexOfChild(existingPlayer.view?.player) != 0) {
+ if (visualStabilityManager.isReorderingAllowed) {
+ mediaContent.removeView(existingPlayer.view?.player)
+ mediaContent.addView(existingPlayer.view?.player, 0)
+ } else {
+ needsReordering = true
+ }
}
}
- existingPlayer?.bind(data)
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
mediaCarousel.requiresRemeasuring = true
+ // Check postcondition: mediaContent should have the same number of children as there are
+ // elements in mediaPlayers.
+ if (mediaPlayers.size != mediaContent.childCount) {
+ Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
+ }
}
private fun removePlayer(key: String) {
@@ -274,10 +306,11 @@ class MediaCarouselController @Inject constructor(
private fun updatePageIndicator() {
val numPages = mediaContent.getChildCount()
- pageIndicator.setNumPages(numPages, Color.WHITE)
+ pageIndicator.setNumPages(numPages)
if (numPages == 1) {
pageIndicator.setLocation(0f)
}
+ updatePageIndicatorAlpha()
}
/**
@@ -308,13 +341,40 @@ class MediaCarouselController @Inject constructor(
updatePlayerToState(mediaPlayer, immediately)
}
maybeResetSettingsCog()
+ updatePageIndicatorAlpha()
+ }
+ }
+
+ private fun updatePageIndicatorAlpha() {
+ val hostStates = mediaHostStatesManager.mediaHostStates
+ val endIsVisible = hostStates[currentEndLocation]?.visible ?: false
+ val startIsVisible = hostStates[currentStartLocation]?.visible ?: false
+ val startAlpha = if (startIsVisible) 1.0f else 0.0f
+ val endAlpha = if (endIsVisible) 1.0f else 0.0f
+ var alpha = 1.0f
+ if (!endIsVisible || !startIsVisible) {
+ var progress = currentTransitionProgress
+ if (!endIsVisible) {
+ progress = 1.0f - progress
+ }
+ // Let's fade in quickly at the end where the view is visible
+ progress = MathUtils.constrain(
+ MathUtils.map(0.95f, 1.0f, 0.0f, 1.0f, progress),
+ 0.0f,
+ 1.0f)
+ alpha = MathUtils.lerp(startAlpha, endAlpha, progress)
}
+ pageIndicator.alpha = alpha
}
private fun updatePageIndicatorLocation() {
// Update the location of the page indicator, carousel clipping
- pageIndicator.translationX = (currentCarouselWidth - pageIndicator.width) / 2.0f +
- mediaCarouselScrollHandler.contentTranslation
+ val translationX = if (isRtl) {
+ (pageIndicator.width - currentCarouselWidth) / 2.0f
+ } else {
+ (currentCarouselWidth - pageIndicator.width) / 2.0f
+ }
+ pageIndicator.translationX = translationX + mediaCarouselScrollHandler.contentTranslation
val layoutParams = pageIndicator.layoutParams as ViewGroup.MarginLayoutParams
pageIndicator.translationY = (currentCarouselHeight - pageIndicator.height -
layoutParams.bottomMargin).toFloat()
@@ -328,13 +388,16 @@ class MediaCarouselController @Inject constructor(
var height = 0
for (mediaPlayer in mediaPlayers.values) {
val controller = mediaPlayer.mediaViewController
- width = Math.max(width, controller.currentWidth)
- height = Math.max(height, controller.currentHeight)
+ // When transitioning the view to gone, the view gets smaller, but the translation
+ // Doesn't, let's add the translation
+ width = Math.max(width, controller.currentWidth + controller.translationX.toInt())
+ height = Math.max(height, controller.currentHeight + controller.translationY.toInt())
}
if (width != currentCarouselWidth || height != currentCarouselHeight) {
currentCarouselWidth = width
currentCarouselHeight = height
- mediaCarouselScrollHandler.setCarouselBounds(currentCarouselWidth, currentCarouselHeight)
+ mediaCarouselScrollHandler.setCarouselBounds(
+ currentCarouselWidth, currentCarouselHeight)
updatePageIndicatorLocation()
}
}
@@ -348,7 +411,7 @@ class MediaCarouselController @Inject constructor(
if (currentlyShowingOnlyActive != endShowsActive ||
((currentTransitionProgress != 1.0f && currentTransitionProgress != 0.0f) &&
startShowsActive != endShowsActive)) {
- /// Whenever we're transitioning from between differing states or the endstate differs
+ // Whenever we're transitioning from between differing states or the endstate differs
// we reset the translation
currentlyShowingOnlyActive = endShowsActive
mediaCarouselScrollHandler.resetTranslation(animate = true)
@@ -413,17 +476,18 @@ class MediaCarouselController @Inject constructor(
val width = desiredHostState?.measurementInput?.width ?: 0
val height = desiredHostState?.measurementInput?.height ?: 0
if (width != carouselMeasureWidth && width != 0 ||
- height != carouselMeasureWidth && height != 0) {
+ height != carouselMeasureHeight && height != 0) {
carouselMeasureWidth = width
carouselMeasureHeight = height
- playerWidthPlusPadding = carouselMeasureWidth + context.resources.getDimensionPixelSize(
- R.dimen.qs_media_padding)
- mediaCarouselScrollHandler.playerWidthPlusPadding = playerWidthPlusPadding
+ val playerWidthPlusPadding = carouselMeasureWidth +
+ context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
// Let's remeasure the carousel
val widthSpec = desiredHostState?.measurementInput?.widthMeasureSpec ?: 0
val heightSpec = desiredHostState?.measurementInput?.heightMeasureSpec ?: 0
mediaCarousel.measure(widthSpec, heightSpec)
mediaCarousel.layout(0, 0, width, mediaCarousel.measuredHeight)
+ // Update the padding after layout; view widths are used in RTL to calculate scrollX
+ mediaCarouselScrollHandler.playerWidthPlusPadding = playerWidthPlusPadding
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index 993c05fbbd6f..3096908aca21 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -59,6 +59,10 @@ class MediaCarouselScrollHandler(
private val falsingManager: FalsingManager
) {
/**
+ * Is the view in RTL
+ */
+ val isRtl: Boolean get() = scrollView.isLayoutRtl
+ /**
* Do we need falsing protection?
*/
var falsingProtectionNeeded: Boolean = false
@@ -121,14 +125,14 @@ class MediaCarouselScrollHandler(
field = value
// The player width has changed, let's update the scroll position to make sure
// it's still at the same place
- var newScroll = activeMediaIndex * playerWidthPlusPadding
+ var newRelativeScroll = activeMediaIndex * playerWidthPlusPadding
if (scrollIntoCurrentMedia > playerWidthPlusPadding) {
- newScroll += playerWidthPlusPadding -
+ newRelativeScroll += playerWidthPlusPadding -
(scrollIntoCurrentMedia - playerWidthPlusPadding)
} else {
- newScroll += scrollIntoCurrentMedia
+ newRelativeScroll += scrollIntoCurrentMedia
}
- scrollView.scrollX = newScroll
+ scrollView.relativeScrollX = newRelativeScroll
}
/**
@@ -184,8 +188,9 @@ class MediaCarouselScrollHandler(
if (playerWidthPlusPadding == 0) {
return
}
- onMediaScrollingChanged(scrollX / playerWidthPlusPadding,
- scrollX % playerWidthPlusPadding)
+ val relativeScrollX = scrollView.relativeScrollX
+ onMediaScrollingChanged(relativeScrollX / playerWidthPlusPadding,
+ relativeScrollX % playerWidthPlusPadding)
}
}
@@ -222,15 +227,23 @@ class MediaCarouselScrollHandler(
Math.abs(contentTranslation))
val settingsTranslation = (1.0f - settingsOffset) * -settingsButton.width *
SETTINGS_BUTTON_TRANSLATION_FRACTION
- val newTranslationX: Float
- if (contentTranslation > 0) {
- newTranslationX = settingsTranslation
+ val newTranslationX = if (isRtl) {
+ // In RTL, the 0-placement is on the right side of the view, not the left...
+ if (contentTranslation > 0) {
+ -(scrollView.width - settingsTranslation - settingsButton.width)
+ } else {
+ -settingsTranslation
+ }
} else {
- newTranslationX = scrollView.width - settingsTranslation - settingsButton.width
+ if (contentTranslation > 0) {
+ settingsTranslation
+ } else {
+ scrollView.width - settingsTranslation - settingsButton.width
+ }
}
val rotation = (1.0f - settingsOffset) * 50
settingsButton.rotation = rotation * -Math.signum(contentTranslation)
- val alpha = MathUtils.map(0.5f, 1.0f, 0.0f, 1.0f, settingsOffset)
+ val alpha = MathUtils.saturate(MathUtils.map(0.5f, 1.0f, 0.0f, 1.0f, settingsOffset))
settingsButton.alpha = alpha
settingsButton.visibility = if (alpha != 0.0f) View.VISIBLE else View.INVISIBLE
settingsButton.translationX = newTranslationX
@@ -259,26 +272,26 @@ class MediaCarouselScrollHandler(
}
if (isUp || motionEvent.action == MotionEvent.ACTION_CANCEL) {
// It's an up and the fling didn't take it above
- val pos = scrollView.scrollX % playerWidthPlusPadding
- val scollXAmount: Int
- if (pos > playerWidthPlusPadding / 2) {
- scollXAmount = playerWidthPlusPadding - pos
+ val relativePos = scrollView.relativeScrollX % playerWidthPlusPadding
+ val scrollXAmount: Int
+ if (relativePos > playerWidthPlusPadding / 2) {
+ scrollXAmount = playerWidthPlusPadding - relativePos
} else {
- scollXAmount = -1 * pos
+ scrollXAmount = -1 * relativePos
}
- if (scollXAmount != 0) {
+ if (scrollXAmount != 0) {
// Delay the scrolling since scrollView calls springback which cancels
// the animation again..
mainExecutor.execute {
- scrollView.smoothScrollBy(scollXAmount, 0)
+ scrollView.smoothScrollBy(if (isRtl) -scrollXAmount else scrollXAmount, 0)
}
}
val currentTranslation = scrollView.getContentTranslation()
if (currentTranslation != 0.0f) {
// We started a Swipe but didn't end up with a fling. Let's either go to the
// dismissed position or go back.
- val springBack = Math.abs(currentTranslation) < getMaxTranslation() / 2
- || isFalseTouch()
+ val springBack = Math.abs(currentTranslation) < getMaxTranslation() / 2 ||
+ isFalseTouch()
val newTranslation: Float
if (springBack) {
newTranslation = 0.0f
@@ -313,9 +326,11 @@ class MediaCarouselScrollHandler(
return gestureDetector.onTouchEvent(motionEvent)
}
- fun onScroll(down: MotionEvent,
- lastMotion: MotionEvent,
- distanceX: Float): Boolean {
+ fun onScroll(
+ down: MotionEvent,
+ lastMotion: MotionEvent,
+ distanceX: Float
+ ): Boolean {
val totalX = lastMotion.x - down.x
val currentTranslation = scrollView.getContentTranslation()
if (currentTranslation != 0.0f ||
@@ -339,8 +354,8 @@ class MediaCarouselScrollHandler(
} // Otherwise we don't have do do anything, and will remove the unrubberbanded
// translation
}
- if (Math.signum(newTranslation) != Math.signum(currentTranslation)
- && currentTranslation != 0.0f) {
+ if (Math.signum(newTranslation) != Math.signum(currentTranslation) &&
+ currentTranslation != 0.0f) {
// We crossed the 0.0 threshold of the translation. Let's see if we're allowed
// to scroll into the new direction
if (scrollView.canScrollHorizontally(-newTranslation.toInt())) {
@@ -394,9 +409,10 @@ class MediaCarouselScrollHandler(
scrollView.animationTargetX = newTranslation
} else {
// We're flinging the player! Let's go either to the previous or to the next player
- val pos = scrollView.scrollX
+ val pos = scrollView.relativeScrollX
val currentIndex = if (playerWidthPlusPadding > 0) pos / playerWidthPlusPadding else 0
- var destIndex = if (vX <= 0) currentIndex + 1 else currentIndex
+ val flungTowardEnd = if (isRtl) vX > 0 else vX < 0
+ var destIndex = if (flungTowardEnd) currentIndex + 1 else currentIndex
destIndex = Math.max(0, destIndex)
destIndex = Math.min(mediaContent.getChildCount() - 1, destIndex)
val view = mediaContent.getChildAt(destIndex)
@@ -438,8 +454,14 @@ class MediaCarouselScrollHandler(
activeMediaIndex = newIndex
updatePlayerVisibilities()
}
- val location = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
+ val relativeLocation = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
scrollInAmount.toFloat() / playerWidthPlusPadding else 0f
+ // Fix the location, because PageIndicator does not handle RTL internally
+ val location = if (isRtl) {
+ mediaContent.childCount - relativeLocation - 1
+ } else {
+ relativeLocation
+ }
pageIndicator.setLocation(location)
updateClipToOutline()
}
@@ -480,13 +502,20 @@ class MediaCarouselScrollHandler(
* where it was and update our scroll position.
*/
fun onPrePlayerRemoved(removed: MediaControlPanel) {
- val beforeActive = mediaContent.indexOfChild(removed.view?.player) <= activeMediaIndex
+ val removedIndex = mediaContent.indexOfChild(removed.view?.player)
+ // If the removed index is less than the activeMediaIndex, then we need to decrement it.
+ // RTL has no effect on this, because indices are always relative (start-to-end).
+ // Update the index 'manually' since we won't always get a call to onMediaScrollingChanged
+ val beforeActive = removedIndex <= activeMediaIndex
if (beforeActive) {
- // also update the index here since the scroll below might not always lead
- // to a scrolling changed
activeMediaIndex = Math.max(0, activeMediaIndex - 1)
- scrollView.scrollX = Math.max(scrollView.scrollX -
- playerWidthPlusPadding, 0)
+ }
+ // If the removed media item is "left of" the active one (in an absolute sense), we need to
+ // scroll the view to keep that player in view. This is because scroll position is always
+ // calculated from left to right.
+ val leftOfActive = if (isRtl) !beforeActive else beforeActive
+ if (leftOfActive) {
+ scrollView.scrollX = Math.max(scrollView.scrollX - playerWidthPlusPadding, 0)
}
}
@@ -501,6 +530,13 @@ class MediaCarouselScrollHandler(
}
}
+ /**
+ * Reset the MediaScrollView to the start.
+ */
+ fun scrollToStart() {
+ scrollView.relativeScrollX = 0
+ }
+
companion object {
private val CONTENT_TRANSLATION = object : FloatPropertyCompat<MediaCarouselScrollHandler>(
"contentTranslation") {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 8c9cb1b240bf..dafc52ad8025 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -23,6 +23,7 @@ import android.media.session.MediaSession
/** State of a media view. */
data class MediaData(
+ val userId: Int,
val initialized: Boolean = false,
val backgroundColor: Int,
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index 11cbc482459a..d0642ccf9714 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -33,23 +33,31 @@ class MediaDataCombineLatest @Inject constructor(
init {
dataSource.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- if (oldKey != null && !oldKey.equals(key)) {
- val s = entries[oldKey]?.second
- entries[key] = data to entries[oldKey]?.second
- entries.remove(oldKey)
+ if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
+ entries[key] = data to entries.remove(oldKey)?.second
+ update(key, oldKey)
} else {
entries[key] = data to entries[key]?.second
+ update(key, key)
}
- update(key, oldKey)
}
override fun onMediaDataRemoved(key: String) {
remove(key)
}
})
deviceSource.addListener(object : MediaDeviceManager.Listener {
- override fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) {
- entries[key] = entries[key]?.first to data
- update(key, key)
+ override fun onMediaDeviceChanged(
+ key: String,
+ oldKey: String?,
+ data: MediaDeviceData?
+ ) {
+ if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
+ entries[key] = entries.remove(oldKey)?.first to data
+ update(key, oldKey)
+ } else {
+ entries[key] = entries[key]?.first to data
+ update(key, key)
+ }
}
override fun onKeyRemoved(key: String) {
remove(key)
@@ -58,6 +66,17 @@ class MediaDataCombineLatest @Inject constructor(
}
/**
+ * Get a map of all non-null data entries
+ */
+ fun getData(): Map<String, MediaData> {
+ return entries.filter {
+ (key, pair) -> pair.first != null && pair.second != null
+ }.mapValues {
+ (key, pair) -> pair.first!!.copy(device = pair.second)
+ }
+ }
+
+ /**
* Add a listener for [MediaData] changes that has been combined with latest [MediaDeviceData].
*/
fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
new file mode 100644
index 000000000000..24ca9708a4e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.util.Log
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.CurrentUserTracker
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private const val TAG = "MediaDataFilter"
+private const val DEBUG = true
+
+/**
+ * Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user
+ * switches (removing entries for the previous user, adding back entries for the current user)
+ *
+ * This is added downstream of [MediaDataManager] since we may still need to handle callbacks from
+ * background users (e.g. timeouts) that UI classes should ignore.
+ * Instead, UI classes should listen to this so they can stay in sync with the current user.
+ */
+@Singleton
+class MediaDataFilter @Inject constructor(
+ private val dataSource: MediaDataCombineLatest,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val mediaResumeListener: MediaResumeListener,
+ private val mediaDataManager: MediaDataManager,
+ private val lockscreenUserManager: NotificationLockscreenUserManager,
+ @Main private val executor: Executor
+) : MediaDataManager.Listener {
+ private val userTracker: CurrentUserTracker
+ private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
+
+ // The filtered mediaEntries, which will be a subset of all mediaEntries in MediaDataManager
+ private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
+
+ init {
+ userTracker = object : CurrentUserTracker(broadcastDispatcher) {
+ override fun onUserSwitched(newUserId: Int) {
+ // Post this so we can be sure lockscreenUserManager already got the broadcast
+ executor.execute { handleUserSwitched(newUserId) }
+ }
+ }
+ userTracker.startTracking()
+ dataSource.addListener(this)
+ }
+
+ override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+ if (!lockscreenUserManager.isCurrentProfile(data.userId)) {
+ return
+ }
+
+ if (oldKey != null) {
+ mediaEntries.remove(oldKey)
+ }
+ mediaEntries.put(key, data)
+
+ // Notify listeners
+ val listenersCopy = listeners.toSet()
+ listenersCopy.forEach {
+ it.onMediaDataLoaded(key, oldKey, data)
+ }
+ }
+
+ override fun onMediaDataRemoved(key: String) {
+ mediaEntries.remove(key)?.let {
+ // Only notify listeners if something actually changed
+ val listenersCopy = listeners.toSet()
+ listenersCopy.forEach {
+ it.onMediaDataRemoved(key)
+ }
+ }
+ }
+
+ @VisibleForTesting
+ internal fun handleUserSwitched(id: Int) {
+ // If the user changes, remove all current MediaData objects and inform listeners
+ val listenersCopy = listeners.toSet()
+ val keyCopy = mediaEntries.keys.toMutableList()
+ // Clear the list first, to make sure callbacks from listeners if we have any entries
+ // are up to date
+ mediaEntries.clear()
+ keyCopy.forEach {
+ if (DEBUG) Log.d(TAG, "Removing $it after user change")
+ listenersCopy.forEach { listener ->
+ listener.onMediaDataRemoved(it)
+ }
+ }
+
+ dataSource.getData().forEach { (key, data) ->
+ if (lockscreenUserManager.isCurrentProfile(data.userId)) {
+ if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
+ mediaEntries.put(key, data)
+ listenersCopy.forEach { listener ->
+ listener.onMediaDataLoaded(key, null, data)
+ }
+ }
+ }
+ }
+
+ /**
+ * Invoked when the user has dismissed the media carousel
+ */
+ fun onSwipeToDismiss() {
+ if (DEBUG) Log.d(TAG, "Media carousel swiped away")
+ val mediaKeys = mediaEntries.keys.toSet()
+ mediaKeys.forEach {
+ mediaDataManager.setTimedOut(it, timedOut = true)
+ }
+ }
+
+ /**
+ * Are there any media notifications active?
+ */
+ fun hasActiveMedia() = mediaEntries.any { it.value.active }
+
+ /**
+ * Are there any media entries we should display?
+ * If resumption is enabled, this will include inactive players
+ * If resumption is disabled, we only want to show active players
+ */
+ fun hasAnyMedia() = if (mediaResumeListener.isResumptionEnabled()) {
+ mediaEntries.isNotEmpty()
+ } else {
+ hasActiveMedia()
+ }
+
+ /**
+ * Add a listener for filtered [MediaData] changes
+ */
+ fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener)
+
+ /**
+ * Remove a listener that was registered with addListener
+ */
+ fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener)
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 5052386e65e1..d82150f2346b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -63,11 +63,12 @@ private val ART_URIS = arrayOf(
)
private const val TAG = "MediaDataManager"
+private const val DEBUG = true
private const val DEFAULT_LUMINOSITY = 0.25f
private const val LUMINOSITY_THRESHOLD = 0.05f
private const val SATURATION_MULTIPLIER = 0.8f
-private val LOADING = MediaData(false, 0, null, null, null, null, null,
+private val LOADING = MediaData(-1, false, 0, null, null, null, null, null,
emptyList(), emptyList(), "INVALID", null, null, null, true, null)
fun isMediaNotification(sbn: StatusBarNotification): Boolean {
@@ -116,15 +117,6 @@ class MediaDataManager(
broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context))
- private val userChangeReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (Intent.ACTION_USER_SWITCHED == intent.action) {
- // Remove all controls, regardless of state
- clearData()
- }
- }
- }
-
private val appChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
@@ -152,9 +144,6 @@ class MediaDataManager(
mediaResumeListener.setManager(this)
addListener(mediaResumeListener)
- val userFilter = IntentFilter(Intent.ACTION_USER_SWITCHED)
- broadcastDispatcher.registerReceiver(userChangeReceiver, userFilter, null, UserHandle.ALL)
-
val suspendFilter = IntentFilter(Intent.ACTION_PACKAGES_SUSPENDED)
broadcastDispatcher.registerReceiver(appChangeReceiver, suspendFilter, null, UserHandle.ALL)
@@ -169,7 +158,6 @@ class MediaDataManager(
fun destroy() {
context.unregisterReceiver(appChangeReceiver)
- broadcastDispatcher.unregisterReceiver(userChangeReceiver)
}
fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
@@ -190,17 +178,6 @@ class MediaDataManager(
}
}
- private fun clearData() {
- // Called on user change. Remove all current MediaData objects and inform listeners
- val listenersCopy = listeners.toSet()
- mediaEntries.forEach {
- listenersCopy.forEach { listener ->
- listener.onMediaDataRemoved(it.key)
- }
- }
- mediaEntries.clear()
- }
-
private fun removeAllForPackage(packageName: String) {
Assert.isMainThread()
val listenersCopy = listeners.toSet()
@@ -221,6 +198,7 @@ class MediaDataManager(
}
fun addResumptionControls(
+ userId: Int,
desc: MediaDescription,
action: Runnable,
token: MediaSession.Token,
@@ -235,7 +213,8 @@ class MediaDataManager(
mediaEntries.put(packageName, resumeData)
}
backgroundExecutor.execute {
- loadMediaDataInBgForResumption(desc, action, token, appName, appIntent, packageName)
+ loadMediaDataInBgForResumption(userId, desc, action, token, appName, appIntent,
+ packageName)
}
}
@@ -275,21 +254,23 @@ class MediaDataManager(
fun removeListener(listener: Listener) = listeners.remove(listener)
/**
- * Called whenever the player has been paused or stopped for a while.
+ * Called whenever the player has been paused or stopped for a while, or swiped from QQS.
* This will make the player not active anymore, hiding it from QQS and Keyguard.
* @see MediaData.active
*/
- private fun setTimedOut(token: String, timedOut: Boolean) {
+ internal fun setTimedOut(token: String, timedOut: Boolean) {
mediaEntries[token]?.let {
if (it.active == !timedOut) {
return
}
it.active = !timedOut
+ if (DEBUG) Log.d(TAG, "Updating $token timedOut: $timedOut")
onMediaDataLoaded(token, token, it)
}
}
private fun loadMediaDataInBgForResumption(
+ userId: Int,
desc: MediaDescription,
resumeAction: Runnable,
token: MediaSession.Token,
@@ -304,7 +285,9 @@ class MediaDataManager(
return
}
- Log.d(TAG, "adding track from browser: $desc")
+ if (DEBUG) {
+ Log.d(TAG, "adding track for $userId from browser: $desc")
+ }
// Album art
var artworkBitmap = desc.iconBitmap
@@ -320,7 +303,7 @@ class MediaDataManager(
val mediaAction = getResumeMediaAction(resumeAction)
foregroundExecutor.execute {
- onMediaDataLoaded(packageName, null, MediaData(true, bgColor, appName,
+ onMediaDataLoaded(packageName, null, MediaData(userId, true, bgColor, appName,
null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
packageName, token, appIntent, device = null, active = false,
resumeAction = resumeAction, resumption = true, notificationKey = packageName,
@@ -337,18 +320,13 @@ class MediaDataManager(
as MediaSession.Token?
val metadata = mediaControllerFactory.create(token).metadata
- if (metadata == null) {
- // TODO: handle this better, removing media notification
- return
- }
-
// Foreground and Background colors computed from album art
val notif: Notification = sbn.notification
- var artworkBitmap = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART)
+ var artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
if (artworkBitmap == null) {
- artworkBitmap = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
+ artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
}
- if (artworkBitmap == null) {
+ if (artworkBitmap == null && metadata != null) {
artworkBitmap = loadBitmapFromUri(metadata)
}
val artWorkIcon = if (artworkBitmap == null) {
@@ -384,16 +362,16 @@ class MediaDataManager(
val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context)
// Song name
- var song: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
+ var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
if (song == null) {
- song = metadata.getString(MediaMetadata.METADATA_KEY_TITLE)
+ song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
}
if (song == null) {
song = HybridGroupManager.resolveTitle(notif)
}
// Artist name
- var artist: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST)
+ var artist: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST)
if (artist == null) {
artist = HybridGroupManager.resolveText(notif)
}
@@ -409,7 +387,7 @@ class MediaDataManager(
if (actions != null) {
for ((index, action) in actions.withIndex()) {
if (action.getIcon() == null) {
- Log.i(TAG, "No icon for action $index ${action.title}")
+ if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}")
actionsToShowCollapsed.remove(index)
continue
}
@@ -436,10 +414,11 @@ class MediaDataManager(
val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
val active = mediaEntries[key]?.active ?: true
- onMediaDataLoaded(key, oldKey, MediaData(true, bgColor, app, smallIconDrawable, artist,
- song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token,
- notif.contentIntent, null, active, resumeAction = resumeAction,
- notificationKey = key, hasCheckedForResume = hasCheckedForResume))
+ onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
+ smallIconDrawable, artist, song, artWorkIcon, actionIcons,
+ actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
+ active, resumeAction = resumeAction, notificationKey = key,
+ hasCheckedForResume = hasCheckedForResume))
}
}
@@ -452,7 +431,7 @@ class MediaDataManager(
if (!TextUtils.isEmpty(uriString)) {
val albumArt = loadBitmapFromUri(Uri.parse(uriString))
if (albumArt != null) {
- Log.d(TAG, "loaded art from $uri")
+ if (DEBUG) Log.d(TAG, "loaded art from $uri")
return albumArt
}
}
@@ -537,22 +516,36 @@ class MediaDataManager(
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
- if (useMediaResumption && mediaEntries.get(key)?.resumeAction != null) {
- Log.d(TAG, "Not removing $key because resumable")
- // Move to resume key aka package name
- val data = mediaEntries.remove(key)!!
- val resumeAction = getResumeMediaAction(data.resumeAction!!)
- val updated = data.copy(token = null, actions = listOf(resumeAction),
+ val removed = mediaEntries.remove(key)
+ if (useMediaResumption && removed?.resumeAction != null) {
+ if (DEBUG) Log.d(TAG, "Not removing $key because resumable")
+ // Move to resume key (aka package name) if that key doesn't already exist.
+ val resumeAction = getResumeMediaAction(removed.resumeAction!!)
+ val updated = removed.copy(token = null, actions = listOf(resumeAction),
actionsToShowInCompact = listOf(0), active = false, resumption = true)
- mediaEntries.put(data.packageName, updated)
- // Notify listeners of "new" controls
+ val pkg = removed?.packageName
+ val migrate = mediaEntries.put(pkg, updated) == null
+ // Notify listeners of "new" controls when migrating or removed and update when not
val listenersCopy = listeners.toSet()
- listenersCopy.forEach {
- it.onMediaDataLoaded(data.packageName, key, updated)
+ if (migrate) {
+ listenersCopy.forEach {
+ it.onMediaDataLoaded(pkg, key, updated)
+ }
+ } else {
+ // Since packageName is used for the key of the resumption controls, it is
+ // possible that another notification has already been reused for the resumption
+ // controls of this package. In this case, rather than renaming this player as
+ // packageName, just remove it and then send a update to the existing resumption
+ // controls.
+ listenersCopy.forEach {
+ it.onMediaDataRemoved(key)
+ }
+ listenersCopy.forEach {
+ it.onMediaDataLoaded(pkg, pkg, updated)
+ }
}
return
}
- val removed = mediaEntries.remove(key)
if (removed != null) {
val listenersCopy = listeners.toSet()
listenersCopy.forEach {
@@ -561,18 +554,6 @@ class MediaDataManager(
}
}
- /**
- * Are there any media notifications active?
- */
- fun hasActiveMedia() = mediaEntries.any { it.value.active }
-
- /**
- * Are there any media entries we should display?
- * If resumption is enabled, this will include inactive players
- * If resumption is disabled, we only want to show active players
- */
- fun hasAnyMedia() = if (useMediaResumption) mediaEntries.isNotEmpty() else hasActiveMedia()
-
fun setMediaResumptionEnabled(isEnabled: Boolean) {
if (useMediaResumption == isEnabled) {
return
@@ -593,16 +574,6 @@ class MediaDataManager(
}
}
- /**
- * Invoked when the user has dismissed the media carousel
- */
- fun onSwipeToDismiss() {
- val mediaKeys = mediaEntries.keys.toSet()
- mediaKeys.forEach {
- setTimedOut(it, timedOut = true)
- }
- }
-
interface Listener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 7ae2dc5c0941..ae7f66b5ac48 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -19,8 +19,12 @@ package com.android.systemui.media
import android.content.Context
import android.media.MediaRouter2Manager
import android.media.session.MediaController
+import androidx.annotation.AnyThread
+import androidx.annotation.MainThread
+import androidx.annotation.WorkerThread
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
@@ -39,11 +43,12 @@ class MediaDeviceManager @Inject constructor(
private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager,
@Main private val fgExecutor: Executor,
+ @Background private val bgExecutor: Executor,
private val mediaDataManager: MediaDataManager,
private val dumpManager: DumpManager
) : MediaDataManager.Listener, Dumpable {
private val listeners: MutableSet<Listener> = mutableSetOf()
- private val entries: MutableMap<String, Token> = mutableMapOf()
+ private val entries: MutableMap<String, Entry> = mutableMapOf()
init {
mediaDataManager.addListener(this)
@@ -71,7 +76,8 @@ class MediaDeviceManager @Inject constructor(
val controller = data.token?.let {
MediaController(context, it)
}
- entry = Token(key, controller, localMediaManagerFactory.create(data.packageName))
+ entry = Entry(key, oldKey, controller,
+ localMediaManagerFactory.create(data.packageName))
entries[key] = entry
entry.start()
}
@@ -98,26 +104,29 @@ class MediaDeviceManager @Inject constructor(
}
}
- private fun processDevice(key: String, device: MediaDevice?) {
+ @MainThread
+ private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) {
val enabled = device != null
val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
listeners.forEach {
- it.onMediaDeviceChanged(key, data)
+ it.onMediaDeviceChanged(key, oldKey, data)
}
}
interface Listener {
/** Called when the route has changed for a given notification. */
- fun onMediaDeviceChanged(key: String, data: MediaDeviceData?)
+ fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?)
/** Called when the notification was removed. */
fun onKeyRemoved(key: String)
}
- private inner class Token(
+ private inner class Entry(
val key: String,
+ val oldKey: String?,
val controller: MediaController?,
val localMediaManager: LocalMediaManager
) : LocalMediaManager.DeviceCallback {
+
val token
get() = controller?.sessionToken
private var started = false
@@ -125,20 +134,27 @@ class MediaDeviceManager @Inject constructor(
set(value) {
if (!started || value != field) {
field = value
- processDevice(key, value)
+ fgExecutor.execute {
+ processDevice(key, oldKey, value)
+ }
}
}
- fun start() {
+
+ @AnyThread
+ fun start() = bgExecutor.execute {
localMediaManager.registerCallback(this)
localMediaManager.startScan()
updateCurrent()
started = true
}
- fun stop() {
+
+ @AnyThread
+ fun stop() = bgExecutor.execute {
started = false
localMediaManager.stopScan()
localMediaManager.unregisterCallback(this)
}
+
fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
val route = controller?.let {
mr2manager.getRoutingSessionForMediaController(it)
@@ -150,14 +166,18 @@ class MediaDeviceManager @Inject constructor(
println(" route=$route")
}
}
- override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute {
+
+ override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute {
updateCurrent()
}
+
override fun onSelectedDeviceStateChanged(device: MediaDevice, state: Int) {
- fgExecutor.execute {
+ bgExecutor.execute {
updateCurrent()
}
}
+
+ @WorkerThread
private fun updateCurrent() {
val device = localMediaManager.getCurrentConnectedDevice()
controller?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index c41e6104833e..fc33391a9ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -40,6 +40,28 @@ import javax.inject.Inject
import javax.inject.Singleton
/**
+ * Similarly to isShown but also excludes views that have 0 alpha
+ */
+val View.isShownNotFaded: Boolean
+ get() {
+ var current: View = this
+ while (true) {
+ if (current.visibility != View.VISIBLE) {
+ return false
+ }
+ if (current.alpha == 0.0f) {
+ return false
+ }
+ val parent = current.parent ?: return false // We are not attached to the view root
+ if (parent !is View) {
+ // we reached the viewroot, hurray
+ return true
+ }
+ current = parent
+ }
+ }
+
+/**
* This manager is responsible for placement of the unique media view between the different hosts
* and animate the positions of the views to achieve seamless transitions.
*/
@@ -329,7 +351,7 @@ class MediaHierarchyManager @Inject constructor(
applyTargetStateIfNotAnimating()
} else if (animate) {
animator.cancel()
- if (currentAttachmentLocation == IN_OVERLAY ||
+ if (currentAttachmentLocation != previousLocation ||
!previousHost.hostView.isAttachedToWindow) {
// Let's animate to the new position, starting from the current position
// We also go in here in case the view was detached, since the bounds wouldn't
@@ -341,10 +363,12 @@ class MediaHierarchyManager @Inject constructor(
animationStartBounds.set(previousHost.currentBounds)
}
adjustAnimatorForTransition(desiredLocation, previousLocation)
- rootView?.let {
- // Let's delay the animation start until we finished laying out
- animationPending = true
- it.postOnAnimation(startAnimation)
+ if (!animationPending) {
+ rootView?.let {
+ // Let's delay the animation start until we finished laying out
+ animationPending = true
+ it.postOnAnimation(startAnimation)
+ }
}
} else {
cancelAnimationAndApplyDesiredState()
@@ -366,7 +390,7 @@ class MediaHierarchyManager @Inject constructor(
// non-trivial reattaching logic happening that will make the view not-shown earlier
return true
}
- return mediaFrame.isShown || animator.isRunning || animationPending
+ return mediaFrame.isShownNotFaded || animator.isRunning || animationPending
}
private fun adjustAnimatorForTransition(desiredLocation: Int, previousLocation: Int) {
@@ -403,15 +427,23 @@ class MediaHierarchyManager @Inject constructor(
}
/**
- * Updates the state that the view wants to be in at the end of the animation.
+ * Updates the bounds that the view wants to be in at the end of the animation.
*/
private fun updateTargetState() {
if (isCurrentlyInGuidedTransformation()) {
val progress = getTransformationProgress()
- val currentHost = getHost(desiredLocation)!!
- val previousHost = getHost(previousLocation)!!
- val newBounds = currentHost.currentBounds
- val previousBounds = previousHost.currentBounds
+ var endHost = getHost(desiredLocation)!!
+ var starthost = getHost(previousLocation)!!
+ // If either of the hosts are invisible, let's keep them at the other host location to
+ // have a nicer disappear animation. Otherwise the currentBounds of the state might
+ // be undefined
+ if (!endHost.visible) {
+ endHost = starthost
+ } else if (!starthost.visible) {
+ starthost = endHost
+ }
+ val newBounds = endHost.currentBounds
+ val previousBounds = starthost.currentBounds
targetBounds = interpolateBounds(previousBounds, newBounds, progress)
} else {
val bounds = getHost(desiredLocation)?.currentBounds ?: return
@@ -462,7 +494,9 @@ class MediaHierarchyManager @Inject constructor(
val previousHost = getHost(previousLocation)
if (currentHost?.location == LOCATION_QS) {
if (previousHost?.location == LOCATION_QQS) {
- return qsExpansion
+ if (previousHost.visible || statusbarState != StatusBarState.KEYGUARD) {
+ return qsExpansion
+ }
}
}
return -1.0f
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 1ae9d3ff4ca5..3598719fcb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -1,10 +1,10 @@
package com.android.systemui.media
-import android.graphics.PointF
import android.graphics.Rect
import android.util.ArraySet
import android.view.View
import android.view.View.OnAttachStateChangeListener
+import com.android.systemui.util.animation.DisappearParameters
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.MeasurementOutput
import com.android.systemui.util.animation.UniqueObjectHostView
@@ -14,8 +14,7 @@ import javax.inject.Inject
class MediaHost @Inject constructor(
private val state: MediaHostStateHolder,
private val mediaHierarchyManager: MediaHierarchyManager,
- private val mediaDataManager: MediaDataManager,
- private val mediaDataManagerCombineLatest: MediaDataCombineLatest,
+ private val mediaDataFilter: MediaDataFilter,
private val mediaHostStatesManager: MediaHostStatesManager
) : MediaHostState by state {
lateinit var hostView: UniqueObjectHostView
@@ -80,12 +79,12 @@ class MediaHost @Inject constructor(
// be a delay until the views and the controllers are initialized, leaving us
// with either a blank view or the controllers not yet initialized and the
// measuring wrong
- mediaDataManagerCombineLatest.addListener(listener)
+ mediaDataFilter.addListener(listener)
updateViewVisibility()
}
override fun onViewDetachedFromWindow(v: View?) {
- mediaDataManagerCombineLatest.removeListener(listener)
+ mediaDataFilter.removeListener(listener)
}
})
@@ -100,7 +99,7 @@ class MediaHost @Inject constructor(
}
// This will trigger a state change that ensures that we now have a state available
state.measurementInput = input
- return mediaHostStatesManager.getPlayerDimensions(state)
+ return mediaHostStatesManager.updateCarouselDimensions(location, state)
}
}
@@ -114,9 +113,9 @@ class MediaHost @Inject constructor(
private fun updateViewVisibility() {
visible = if (showsOnlyActiveMedia) {
- mediaDataManager.hasActiveMedia()
+ mediaDataFilter.hasActiveMedia()
} else {
- mediaDataManager.hasAnyMedia()
+ mediaDataFilter.hasAnyMedia()
}
val newVisibility = if (visible) View.VISIBLE else View.GONE
if (newVisibility != hostView.visibility) {
@@ -128,8 +127,6 @@ class MediaHost @Inject constructor(
}
class MediaHostStateHolder @Inject constructor() : MediaHostState {
- private var gonePivot: PointF = PointF()
-
override var measurementInput: MeasurementInput? = null
set(value) {
if (value?.equals(field) != true) {
@@ -172,15 +169,18 @@ class MediaHost @Inject constructor(
changedListener?.invoke()
}
- override fun getPivotX(): Float = gonePivot.x
- override fun getPivotY(): Float = gonePivot.y
- override fun setGonePivot(x: Float, y: Float) {
- if (gonePivot.equals(x, y)) {
- return
+ override var disappearParameters: DisappearParameters = DisappearParameters()
+ set(value) {
+ val newHash = value.hashCode()
+ if (lastDisappearHash.equals(newHash)) {
+ return
+ }
+ field = value
+ lastDisappearHash = newHash
+ changedListener?.invoke()
}
- gonePivot.set(x, y)
- changedListener?.invoke()
- }
+
+ private var lastDisappearHash = disappearParameters.hashCode()
/**
* A listener for all changes. This won't be copied over when invoking [copy]
@@ -196,7 +196,7 @@ class MediaHost @Inject constructor(
mediaHostState.showsOnlyActiveMedia = showsOnlyActiveMedia
mediaHostState.measurementInput = measurementInput?.copy()
mediaHostState.visible = visible
- mediaHostState.gonePivot.set(gonePivot)
+ mediaHostState.disappearParameters = disappearParameters.deepCopy()
mediaHostState.falsingProtectionNeeded = falsingProtectionNeeded
return mediaHostState
}
@@ -220,7 +220,7 @@ class MediaHost @Inject constructor(
if (falsingProtectionNeeded != other.falsingProtectionNeeded) {
return false
}
- if (!gonePivot.equals(other.getPivotX(), other.getPivotY())) {
+ if (!disappearParameters.equals(other.disappearParameters)) {
return false
}
return true
@@ -232,12 +232,23 @@ class MediaHost @Inject constructor(
result = 31 * result + falsingProtectionNeeded.hashCode()
result = 31 * result + showsOnlyActiveMedia.hashCode()
result = 31 * result + if (visible) 1 else 2
- result = 31 * result + gonePivot.hashCode()
+ result = 31 * result + disappearParameters.hashCode()
return result
}
}
}
+/**
+ * A description of a media host state that describes the behavior whenever the media carousel
+ * is hosted. The HostState notifies the media players of changes to their properties, who
+ * in turn will create view states from it.
+ * When adding a new property to this, make sure to update the listener and notify them
+ * about the changes.
+ * In case you need to have a different rendering based on the state, you can add a new
+ * constraintState to the [MediaViewController]. Otherwise, similar host states will resolve
+ * to the same viewstate, a behavior that is described in [CacheKey]. Make sure to only update
+ * that key if the underlying view needs to have a different measurement.
+ */
interface MediaHostState {
/**
@@ -268,23 +279,11 @@ interface MediaHostState {
var falsingProtectionNeeded: Boolean
/**
- * Sets the pivot point when clipping the height or width.
- * Clipping happens when animating visibility when we're visible in QS but not on QQS,
- * for example.
- */
- fun setGonePivot(x: Float, y: Float)
-
- /**
- * x position of pivot, from 0 to 1
- * @see [setGonePivot]
- */
- fun getPivotX(): Float
-
- /**
- * y position of pivot, from 0 to 1
- * @see [setGonePivot]
+ * The parameters how the view disappears from this location when going to a host that's not
+ * visible. If modified, make sure to set this value again on the host to ensure the values
+ * are propagated
*/
- fun getPivotY(): Float
+ var disappearParameters: DisappearParameters
/**
* Get a copy of this view state, deepcopying all appropriate members
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
index f90af2a01de0..d3954b70ca71 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
@@ -32,6 +32,12 @@ class MediaHostStatesManager @Inject constructor() {
private val controllers: MutableSet<MediaViewController> = mutableSetOf()
/**
+ * The overall sizes of the carousel. This is needed to make sure all players in the carousel
+ * have equal size.
+ */
+ val carouselSizes: MutableMap<Int, MeasurementOutput> = mutableMapOf()
+
+ /**
* A map with all media states of all locations.
*/
val mediaHostStates: MutableMap<Int, MediaHostState> = mutableMapOf()
@@ -45,6 +51,7 @@ class MediaHostStatesManager @Inject constructor() {
if (!hostState.equals(currentState)) {
val newState = hostState.copy()
mediaHostStates.put(location, newState)
+ updateCarouselDimensions(location, hostState)
// First update all the controllers to ensure they get the chance to measure
for (controller in controllers) {
controller.stateCallback.onHostStateChanged(location, newState)
@@ -61,7 +68,10 @@ class MediaHostStatesManager @Inject constructor() {
* Get the dimensions of all players combined, which determines the overall height of the
* media carousel and the media hosts.
*/
- fun getPlayerDimensions(hostState: MediaHostState): MeasurementOutput {
+ fun updateCarouselDimensions(
+ @MediaLocation location: Int,
+ hostState: MediaHostState
+ ): MeasurementOutput {
val result = MeasurementOutput(0, 0)
for (controller in controllers) {
val measurement = controller.getMeasurementsForState(hostState)
@@ -74,6 +84,7 @@ class MediaHostStatesManager @Inject constructor() {
}
}
}
+ carouselSizes[location] = result
return result
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 0cc1e7bb1b56..4ec746fcb153 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -56,7 +56,7 @@ class MediaResumeListener @Inject constructor(
private lateinit var mediaDataManager: MediaDataManager
private var mediaBrowser: ResumeMediaBrowser? = null
- private var currentUserId: Int
+ private var currentUserId: Int = context.userId
private val userChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -65,7 +65,6 @@ class MediaResumeListener @Inject constructor(
} else if (Intent.ACTION_USER_SWITCHED == intent.action) {
currentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
loadSavedComponents()
- loadMediaResumptionControls()
}
}
}
@@ -89,13 +88,12 @@ class MediaResumeListener @Inject constructor(
}
Log.d(TAG, "Adding resume controls $desc")
- mediaDataManager.addResumptionControls(desc, resumeAction, token, appName.toString(),
- appIntent, component.packageName)
+ mediaDataManager.addResumptionControls(currentUserId, desc, resumeAction, token,
+ appName.toString(), appIntent, component.packageName)
}
}
init {
- currentUserId = context.userId
if (useMediaResumption) {
val unlockFilter = IntentFilter()
unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED)
@@ -118,6 +116,8 @@ class MediaResumeListener @Inject constructor(
}, Settings.Secure.MEDIA_CONTROLS_RESUME)
}
+ fun isResumptionEnabled() = useMediaResumption
+
private fun loadSavedComponents() {
// Make sure list is empty (if we switched users)
resumeComponents.clear()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaScrollView.kt b/packages/SystemUI/src/com/android/systemui/media/MediaScrollView.kt
index a079b06a0b10..b8872250bb6c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaScrollView.kt
@@ -15,7 +15,10 @@ import com.android.systemui.util.animation.physicsAnimator
* when only measuring children but not the parent, when trying to apply a new scroll position
*/
class MediaScrollView @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+)
: HorizontalScrollView(context, attrs, defStyleAttr) {
lateinit var contentContainer: ViewGroup
@@ -38,6 +41,26 @@ class MediaScrollView @JvmOverloads constructor(
}
/**
+ * Convert between the absolute (left-to-right) and relative (start-to-end) scrollX of the media
+ * carousel. The player indices are always relative (start-to-end) and the scrollView.scrollX
+ * is always absolute. This function is its own inverse.
+ */
+ private fun transformScrollX(scrollX: Int): Int = if (isLayoutRtl) {
+ contentContainer.width - width - scrollX
+ } else {
+ scrollX
+ }
+
+ /**
+ * Get the layoutDirection-relative (start-to-end) scroll X position of the carousel.
+ */
+ var relativeScrollX: Int
+ get() = transformScrollX(scrollX)
+ set(value) {
+ scrollX = transformScrollX(value)
+ }
+
+ /**
* Allow all scrolls to go through, use base implementation
*/
override fun scrollTo(x: Int, y: Int) {
@@ -55,15 +78,15 @@ class MediaScrollView @JvmOverloads constructor(
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
- var intercept = false;
+ var intercept = false
touchListener?.let {
intercept = it.onInterceptTouchEvent(ev)
}
- return super.onInterceptTouchEvent(ev) || intercept;
+ return super.onInterceptTouchEvent(ev) || intercept
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
- var touch = false;
+ var touch = false
touchListener?.let {
touch = it.onTouchEvent(ev)
}
@@ -75,9 +98,17 @@ class MediaScrollView @JvmOverloads constructor(
contentContainer = getChildAt(0) as ViewGroup
}
- override fun overScrollBy(deltaX: Int, deltaY: Int, scrollX: Int, scrollY: Int,
- scrollRangeX: Int, scrollRangeY: Int, maxOverScrollX: Int,
- maxOverScrollY: Int, isTouchEvent: Boolean): Boolean {
+ override fun overScrollBy(
+ deltaX: Int,
+ deltaY: Int,
+ scrollX: Int,
+ scrollY: Int,
+ scrollRangeX: Int,
+ scrollRangeY: Int,
+ maxOverScrollX: Int,
+ maxOverScrollY: Int,
+ isTouchEvent: Boolean
+ ): Boolean {
if (getContentTranslation() != 0.0f) {
// When we're dismissing we ignore all the scrolling
return false
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 9a134dbe0264..8662aacfdab2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -54,7 +54,32 @@ class MediaTimeoutListener @Inject constructor(
if (mediaListeners.containsKey(key)) {
return
}
+ // Having an old key means that we're migrating from/to resumption. We should invalidate
+ // the old listener and create a new one.
+ val migrating = oldKey != null && key != oldKey
+ var wasPlaying = false
+ if (migrating) {
+ if (mediaListeners.containsKey(oldKey)) {
+ val oldListener = mediaListeners.remove(oldKey)
+ wasPlaying = oldListener?.playing ?: false
+ oldListener?.destroy()
+ if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption")
+ } else {
+ Log.w(TAG, "Old key $oldKey for player $key doesn't exist. Continuing...")
+ }
+ }
mediaListeners[key] = PlaybackStateListener(key, data)
+
+ // If a player becomes active because of a migration, we'll need to broadcast its state.
+ // Doing it now would lead to reentrant callbacks, so let's wait until we're done.
+ if (migrating && mediaListeners[key]?.playing != wasPlaying) {
+ mainExecutor.execute {
+ if (mediaListeners[key]?.playing == true) {
+ if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key")
+ timeoutCallback.invoke(key, false /* timedOut */)
+ }
+ }
+ }
}
override fun onMediaDataRemoved(key: String) {
@@ -71,7 +96,7 @@ class MediaTimeoutListener @Inject constructor(
) : MediaController.Callback() {
var timedOut = false
- private var playing: Boolean? = null
+ var playing: Boolean? = null
// Resume controls may have null token
private val mediaController = if (data.token != null) {
@@ -83,7 +108,9 @@ class MediaTimeoutListener @Inject constructor(
init {
mediaController?.registerCallback(this)
- onPlaybackStateChanged(mediaController?.playbackState)
+ // Let's register the cancellations, but not dispatch events now.
+ // Timeouts didn't happen yet and reentrant events are troublesome.
+ processState(mediaController?.playbackState, dispatchEvents = false)
}
fun destroy() {
@@ -91,8 +118,12 @@ class MediaTimeoutListener @Inject constructor(
}
override fun onPlaybackStateChanged(state: PlaybackState?) {
+ processState(state, dispatchEvents = true)
+ }
+
+ private fun processState(state: PlaybackState?, dispatchEvents: Boolean) {
if (DEBUG) {
- Log.v(TAG, "onPlaybackStateChanged: $state")
+ Log.v(TAG, "processState: $state")
}
val isPlaying = state != null && isPlayingState(state.state)
@@ -116,12 +147,16 @@ class MediaTimeoutListener @Inject constructor(
Log.v(TAG, "Execute timeout for $key")
}
timedOut = true
- timeoutCallback(key, timedOut)
+ if (dispatchEvents) {
+ timeoutCallback(key, timedOut)
+ }
}, PAUSED_MEDIA_TIMEOUT)
} else {
expireMediaTimeout(key, "playback started - $state, $key")
timedOut = false
- timeoutCallback(key, timedOut)
+ if (dispatchEvents) {
+ timeoutCallback(key, timedOut)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index fc22c026974a..38817d7b579e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -18,7 +18,6 @@ package com.android.systemui.media
import android.content.Context
import android.content.res.Configuration
-import android.graphics.PointF
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -53,7 +52,7 @@ class MediaViewController @Inject constructor(
/**
* A map containing all viewStates for all locations of this mediaState
*/
- private val viewStates: MutableMap<MediaHostState, TransitionViewState?> = mutableMapOf()
+ private val viewStates: MutableMap<CacheKey, TransitionViewState?> = mutableMapOf()
/**
* The ending location of the view where it ends when all animations and transitions have
@@ -80,9 +79,19 @@ class MediaViewController @Inject constructor(
private val tmpState = TransitionViewState()
/**
- * Temporary variable to avoid unnecessary allocations.
+ * A temporary state used to store intermediate measurements.
+ */
+ private val tmpState2 = TransitionViewState()
+
+ /**
+ * A temporary state used to store intermediate measurements.
+ */
+ private val tmpState3 = TransitionViewState()
+
+ /**
+ * A temporary cache key to be used to look up cache entries
*/
- private val tmpPoint = PointF()
+ private val tmpKey = CacheKey()
/**
* The current width of the player. This might not factor in case the player is animating
@@ -96,6 +105,24 @@ class MediaViewController @Inject constructor(
var currentHeight: Int = 0
/**
+ * Get the translationX of the layout
+ */
+ var translationX: Float = 0.0f
+ private set
+ get() {
+ return transitionLayout?.translationX ?: 0.0f
+ }
+
+ /**
+ * Get the translationY of the layout
+ */
+ var translationY: Float = 0.0f
+ private set
+ get() {
+ return transitionLayout?.translationY ?: 0.0f
+ }
+
+ /**
* A callback for RTL config changes
*/
private val configurationListener = object : ConfigurationController.ConfigurationListener {
@@ -179,15 +206,21 @@ class MediaViewController @Inject constructor(
* Obtain a new viewState for a given media state. This usually returns a cached state, but if
* it's not available, it will recreate one by measuring, which may be expensive.
*/
- private fun obtainViewState(state: MediaHostState): TransitionViewState? {
- val viewState = viewStates[state]
+ private fun obtainViewState(state: MediaHostState?): TransitionViewState? {
+ if (state == null || state.measurementInput == null) {
+ return null
+ }
+ // Only a subset of the state is relevant to get a valid viewState. Let's get the cachekey
+ var cacheKey = getKey(state, tmpKey)
+ val viewState = viewStates[cacheKey]
if (viewState != null) {
// we already have cached this measurement, let's continue
return viewState
}
-
+ // Copy the key since this might call recursively into it and we're using tmpKey
+ cacheKey = cacheKey.copy()
val result: TransitionViewState?
- if (transitionLayout != null && state.measurementInput != null) {
+ if (transitionLayout != null) {
// Let's create a new measurement
if (state.expansion == 0.0f || state.expansion == 1.0f) {
result = transitionLayout!!.calculateViewState(
@@ -198,7 +231,7 @@ class MediaViewController @Inject constructor(
// We don't want to cache interpolated or null states as this could quickly fill up
// our cache. We only cache the start and the end states since the interpolation
// is cheap
- viewStates[state.copy()] = result
+ viewStates[cacheKey] = result
} else {
// This is an interpolated state
val startState = state.copy().also { it.expansion = 0.0f }
@@ -208,14 +241,10 @@ class MediaViewController @Inject constructor(
val startViewState = obtainViewState(startState) as TransitionViewState
val endState = state.copy().also { it.expansion = 1.0f }
val endViewState = obtainViewState(endState) as TransitionViewState
- tmpPoint.set(startState.getPivotX(), startState.getPivotY())
- result = TransitionViewState()
- layoutController.getInterpolatedState(
+ result = layoutController.getInterpolatedState(
startViewState,
endViewState,
- state.expansion,
- tmpPoint,
- result)
+ state.expansion)
}
} else {
result = null
@@ -223,6 +252,15 @@ class MediaViewController @Inject constructor(
return result
}
+ private fun getKey(state: MediaHostState, result: CacheKey): CacheKey {
+ result.apply {
+ heightMeasureSpec = state.measurementInput?.heightMeasureSpec ?: 0
+ widthMeasureSpec = state.measurementInput?.widthMeasureSpec ?: 0
+ expansion = state.expansion
+ }
+ return result
+ }
+
/**
* Attach a view to this controller. This may perform measurements if it's not available yet
* and should therefore be done carefully.
@@ -230,7 +268,6 @@ class MediaViewController @Inject constructor(
fun attach(transitionLayout: TransitionLayout) {
this.transitionLayout = transitionLayout
layoutController.attach(transitionLayout)
- ensureAllMeasurements()
if (currentEndLocation == -1) {
return
}
@@ -270,69 +307,75 @@ class MediaViewController @Inject constructor(
val shouldAnimate = animateNextStateChange && !applyImmediately
- var startHostState = mediaHostStatesManager.mediaHostStates[startLocation]
- var endHostState = mediaHostStatesManager.mediaHostStates[endLocation]
- var swappedStartState = false
- var swappedEndState = false
-
- // if we're going from or to a non visible state, let's grab the visible one and animate
- // the view being clipped instead.
- if (endHostState?.visible != true) {
- endHostState = startHostState
- swappedEndState = true
- }
- if (startHostState?.visible != true) {
- startHostState = endHostState
- swappedStartState = true
- }
- if (startHostState == null || endHostState == null) {
- return
- }
-
- var endViewState = obtainViewState(endHostState) ?: return
- if (swappedEndState) {
- endViewState = endViewState.copy()
- endViewState.height = 0
- }
+ val endHostState = mediaHostStatesManager.mediaHostStates[endLocation] ?: return
+ val startHostState = mediaHostStatesManager.mediaHostStates[startLocation]
// Obtain the view state that we'd want to be at the end
// The view might not be bound yet or has never been measured and in that case will be
// reset once the state is fully available
+ var endViewState = obtainViewState(endHostState) ?: return
+ endViewState = updateViewStateToCarouselSize(endViewState, endLocation, tmpState2)!!
layoutController.setMeasureState(endViewState)
- // If the view isn't bound, we can drop the animation, otherwise we'll executute it
+ // If the view isn't bound, we can drop the animation, otherwise we'll execute it
animateNextStateChange = false
if (transitionLayout == null) {
return
}
+ val result: TransitionViewState
var startViewState = obtainViewState(startHostState)
- if (swappedStartState) {
- startViewState = startViewState?.copy()
- startViewState?.height = 0
- }
-
- val result: TransitionViewState?
- result = if (transitionProgress == 1.0f || startViewState == null) {
- endViewState
- } else if (transitionProgress == 0.0f) {
- startViewState
- } else {
- if (swappedEndState || swappedStartState) {
- tmpPoint.set(startHostState.getPivotX(), startHostState.getPivotY())
+ startViewState = updateViewStateToCarouselSize(startViewState, startLocation, tmpState3)
+
+ if (!endHostState.visible) {
+ // Let's handle the case where the end is gone first. In this case we take the
+ // start viewState and will make it gone
+ if (startViewState == null || startHostState == null || !startHostState.visible) {
+ // the start isn't a valid state, let's use the endstate directly
+ result = endViewState
} else {
- tmpPoint.set(0.0f, 0.0f)
+ // Let's get the gone presentation from the start state
+ result = layoutController.getGoneState(startViewState,
+ startHostState.disappearParameters,
+ transitionProgress,
+ tmpState)
}
- layoutController.getInterpolatedState(startViewState, endViewState, transitionProgress,
- tmpPoint, tmpState)
- tmpState
+ } else if (startHostState != null && !startHostState.visible) {
+ // We have a start state and it is gone.
+ // Let's get presentation from the endState
+ result = layoutController.getGoneState(endViewState, endHostState.disappearParameters,
+ 1.0f - transitionProgress,
+ tmpState)
+ } else if (transitionProgress == 1.0f || startViewState == null) {
+ // We're at the end. Let's use that state
+ result = endViewState
+ } else if (transitionProgress == 0.0f) {
+ // We're at the start. Let's use that state
+ result = startViewState
+ } else {
+ result = layoutController.getInterpolatedState(startViewState, endViewState,
+ transitionProgress, tmpState)
}
- currentWidth = result.width
- currentHeight = result.height
layoutController.setState(result, applyImmediately, shouldAnimate, animationDuration,
animationDelay)
}
+ private fun updateViewStateToCarouselSize(
+ viewState: TransitionViewState?,
+ location: Int,
+ outState: TransitionViewState
+ ) : TransitionViewState? {
+ val result = viewState?.copy(outState) ?: return null
+ val overrideSize = mediaHostStatesManager.carouselSizes[location]
+ overrideSize?.let {
+ // To be safe we're using a maximum here. The override size should always be set
+ // properly though.
+ result.height = Math.max(it.measuredHeight, result.height)
+ result.width = Math.max(it.measuredWidth, result.width)
+ }
+ return result
+ }
+
/**
* Retrieves the [TransitionViewState] and [MediaHostState] of a [@MediaLocation].
* In the event of [location] not being visible, [locationWhenHidden] will be used instead.
@@ -370,12 +413,24 @@ class MediaViewController @Inject constructor(
* Clear all existing measurements and refresh the state to match the view.
*/
fun refreshState() {
- if (!firstRefresh) {
- // Let's clear all of our measurements and recreate them!
- viewStates.clear()
- setCurrentState(currentStartLocation, currentEndLocation, currentTransitionProgress,
- applyImmediately = true)
+ // Let's clear all of our measurements and recreate them!
+ viewStates.clear()
+ if (firstRefresh) {
+ // This is the first bind, let's ensure we pre-cache all measurements. Otherwise
+ // We'll just load these on demand.
+ ensureAllMeasurements()
+ firstRefresh = false
}
- firstRefresh = false
+ setCurrentState(currentStartLocation, currentEndLocation, currentTransitionProgress,
+ applyImmediately = true)
}
}
+
+/**
+ * An internal key for the cache of mediaViewStates. This is a subset of the full host state.
+ */
+private data class CacheKey(
+ var widthMeasureSpec: Int = -1,
+ var heightMeasureSpec: Int = -1,
+ var expansion: Float = 0.0f
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
index 1842564a4574..68b6785849aa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
@@ -94,7 +94,7 @@ public class ResumeMediaBrowser {
// a request with EXTRA_RECENT; if they don't, no resume controls
MediaBrowser.MediaItem child = children.get(0);
MediaDescription desc = child.getDescription();
- if (child.isPlayable()) {
+ if (child.isPlayable() && mMediaBrowser != null) {
mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(),
ResumeMediaBrowser.this);
} else {
@@ -129,7 +129,7 @@ public class ResumeMediaBrowser {
@Override
public void onConnected() {
Log.d(TAG, "Service connected for " + mComponentName);
- if (mMediaBrowser.isConnected()) {
+ if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
String root = mMediaBrowser.getRoot();
if (!TextUtils.isEmpty(root)) {
mCallback.onConnected();
@@ -187,7 +187,7 @@ public class ResumeMediaBrowser {
@Override
public void onConnected() {
Log.d(TAG, "Connected for restart " + mMediaBrowser.isConnected());
- if (!mMediaBrowser.isConnected()) {
+ if (mMediaBrowser == null || !mMediaBrowser.isConnected()) {
mCallback.onError();
return;
}
@@ -246,7 +246,7 @@ public class ResumeMediaBrowser {
@Override
public void onConnected() {
Log.d(TAG, "connected");
- if (!mMediaBrowser.isConnected()
+ if (mMediaBrowser == null || !mMediaBrowser.isConnected()
|| TextUtils.isEmpty(mMediaBrowser.getRoot())) {
mCallback.onError();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index 1dca3f1297b1..9e326aaec3c1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -71,7 +71,7 @@ private fun PlaybackState.computePosition(duration: Long): Long {
/** ViewModel for seek bar in QS media player. */
class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: RepeatableExecutor) {
- private var _data = Progress(false, false, null, null)
+ private var _data = Progress(false, false, null, 0)
set(value) {
field = value
_progress.postValue(value)
@@ -186,10 +186,10 @@ class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: R
val mediaMetadata = controller?.metadata
val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L
val position = playbackState?.position?.toInt()
- val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt()
+ val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
val enabled = if (playbackState == null ||
playbackState?.getState() == PlaybackState.STATE_NONE ||
- (duration != null && duration <= 0)) false else true
+ (duration <= 0)) false else true
_data = Progress(enabled, seekAvailable, position, duration)
checkIfPollingNeeded()
}
@@ -408,6 +408,6 @@ class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: R
val enabled: Boolean,
val seekAvailable: Boolean,
val elapsedTime: Int?,
- val duration: Int?
+ val duration: Int
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
index 5d9439f5f127..c581ac677532 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
@@ -16,6 +16,8 @@
package com.android.systemui.onehanded;
+import android.view.SurfaceControl;
+
/**
* Additional callback interface for OneHanded animation
*/
@@ -30,7 +32,7 @@ public interface OneHandedAnimationCallback {
/**
* Called when OneHanded animation is ended.
*/
- default void onOneHandedAnimationEnd(
+ default void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
index 08f08c585224..1926c44abcba 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
@@ -19,16 +19,16 @@ package com.android.systemui.onehanded;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
-import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceControl;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.view.animation.OvershootInterpolator;
import androidx.annotation.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
import javax.inject.Inject;
@@ -39,18 +39,6 @@ public class OneHandedAnimationController {
private static final float FRACTION_START = 0f;
private static final float FRACTION_END = 1f;
- public static final int ANIM_TYPE_TRANSLATE = 0;
- public static final int ANIM_TYPE_SCALE = 1;
-
- // Note: ANIM_TYPE_SCALE reserve for the future development
- @IntDef(prefix = {"ANIM_TYPE_"}, value = {
- ANIM_TYPE_TRANSLATE,
- ANIM_TYPE_SCALE
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AnimationType {
- }
-
public static final int TRANSITION_DIRECTION_NONE = 0;
public static final int TRANSITION_DIRECTION_TRIGGER = 1;
public static final int TRANSITION_DIRECTION_EXIT = 2;
@@ -64,42 +52,57 @@ public class OneHandedAnimationController {
public @interface TransitionDirection {
}
- private final Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mOvershootInterpolator;
private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
- private OneHandedTransitionAnimator mCurrentAnimator;
+ private final HashMap<SurfaceControl, OneHandedTransitionAnimator> mAnimatorMap =
+ new HashMap<>();
/**
* Constructor of OneHandedAnimationController
*/
@Inject
- public OneHandedAnimationController(Context context,
+ public OneHandedAnimationController(
OneHandedSurfaceTransactionHelper surfaceTransactionHelper) {
mSurfaceTransactionHelper = surfaceTransactionHelper;
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
+ mOvershootInterpolator = new OvershootInterpolator();
}
@SuppressWarnings("unchecked")
OneHandedTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds,
Rect endBounds) {
- if (mCurrentAnimator == null) {
- mCurrentAnimator = setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds));
- } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_TRANSLATE
- && mCurrentAnimator.isRunning()) {
- mCurrentAnimator.updateEndValue(endBounds);
+ final OneHandedTransitionAnimator animator = mAnimatorMap.get(leash);
+ if (animator == null) {
+ mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
+ OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
+ } else if (animator.isRunning()) {
+ animator.updateEndValue(endBounds);
} else {
- mCurrentAnimator.cancel();
- mCurrentAnimator = setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds));
+ animator.cancel();
+ mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
+ OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
+ }
+ return mAnimatorMap.get(leash);
+ }
+
+ HashMap<SurfaceControl, OneHandedTransitionAnimator> getAnimatorMap() {
+ return mAnimatorMap;
+ }
+
+ boolean isAnimatorsConsumed() {
+ return mAnimatorMap.isEmpty();
+ }
+
+ void removeAnimator(SurfaceControl key) {
+ final OneHandedTransitionAnimator animator = mAnimatorMap.remove(key);
+ if (animator != null && animator.isRunning()) {
+ animator.cancel();
}
- return mCurrentAnimator;
}
OneHandedTransitionAnimator setupOneHandedTransitionAnimator(
OneHandedTransitionAnimator animator) {
animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
- animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setInterpolator(mOvershootInterpolator);
animator.setFloatValues(FRACTION_START, FRACTION_END);
return animator;
}
@@ -114,7 +117,6 @@ public class OneHandedAnimationController {
ValueAnimator.AnimatorListener {
private final SurfaceControl mLeash;
- private final @AnimationType int mAnimationType;
private T mStartValue;
private T mEndValue;
private T mCurrentValue;
@@ -127,10 +129,8 @@ public class OneHandedAnimationController {
private @TransitionDirection int mTransitionDirection;
private int mTransitionOffset;
- private OneHandedTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
- T startValue, T endValue) {
+ private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) {
mLeash = leash;
- mAnimationType = animationType;
mStartValue = startValue;
mEndValue = endValue;
addListener(this);
@@ -150,8 +150,10 @@ public class OneHandedAnimationController {
@Override
public void onAnimationEnd(Animator animation) {
mCurrentValue = mEndValue;
+ final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ onEndTransaction(mLeash, tx);
if (mOneHandedAnimationCallback != null) {
- mOneHandedAnimationCallback.onOneHandedAnimationEnd(this);
+ mOneHandedAnimationCallback.onOneHandedAnimationEnd(tx, this);
}
}
@@ -196,6 +198,10 @@ public class OneHandedAnimationController {
return this;
}
+ SurfaceControl getLeash() {
+ return mLeash;
+ }
+
Rect getDestinationBounds() {
return (Rect) mEndValue;
}
@@ -227,11 +233,6 @@ public class OneHandedAnimationController {
return mEndValue;
}
- @AnimationType
- int getAnimationType() {
- return mAnimationType;
- }
-
void setCurrentValue(T value) {
mCurrentValue = value;
}
@@ -250,11 +251,9 @@ public class OneHandedAnimationController {
@VisibleForTesting
static OneHandedTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
Rect startValue, Rect endValue) {
- // At R, we only support translate type first.
- final int animType = ANIM_TYPE_TRANSLATE;
- return new OneHandedTransitionAnimator<Rect>(leash, animType,
- new Rect(startValue), new Rect(endValue)) {
+ return new OneHandedTransitionAnimator<Rect>(leash, new Rect(startValue),
+ new Rect(endValue)) {
private final Rect mTmpRect = new Rect();
@@ -282,7 +281,7 @@ public class OneHandedAnimationController {
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
getSurfaceTransactionHelper()
.alpha(tx, leash, 1f)
- .crop(tx, leash, getStartValue())
+ .translate(tx, leash, getEndValue().top - getStartValue().top)
.round(tx, leash);
tx.apply();
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
index 063d7c843aa5..c0b9258f39fd 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -38,11 +38,12 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.systemui.Dumpable;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -68,22 +69,20 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
@VisibleForTesting
static final int MSG_OFFSET_FINISH = 3;
- private final Handler mUpdateHandler;
+ private final Rect mLastVisualDisplayBounds = new Rect();
+ private final Rect mDefaultDisplayBounds = new Rect();
+ private Handler mUpdateHandler;
private boolean mIsInOneHanded;
private int mEnterExitAnimationDurationMs;
@VisibleForTesting
- DisplayAreaInfo mDisplayAreaInfo;
+ HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap();
private DisplayController mDisplayController;
private OneHandedAnimationController mAnimationController;
- private SurfaceControl mLeash;
- private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
- private OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
- private Rect mLastVisualDisplayBounds;
- private Rect mDefaultDisplayBounds;
+ private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
@VisibleForTesting
OneHandedAnimationCallback mOneHandedAnimationCallback =
@@ -94,17 +93,23 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
}
@Override
- public void onOneHandedAnimationEnd(
+ public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
- obtainArgsFromAnimator(animator)));
+ mAnimationController.removeAnimator(animator.getLeash());
+ if (mAnimationController.isAnimatorsConsumed()) {
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
+ obtainArgsFromAnimator(animator)));
+ }
}
@Override
public void onOneHandedAnimationCancel(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
- obtainArgsFromAnimator(animator)));
+ mAnimationController.removeAnimator(animator.getLeash());
+ if (mAnimationController.isAnimatorsConsumed()) {
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
+ obtainArgsFromAnimator(animator)));
+ }
}
};
@@ -112,34 +117,25 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
private Handler.Callback mUpdateCallback = (msg) -> {
SomeArgs args = (SomeArgs) msg.obj;
final Rect currentBounds = args.arg1 != null ? (Rect) args.arg1 : mDefaultDisplayBounds;
- final int xOffset = args.argi1;
final int yOffset = args.argi2;
final int direction = args.argi3;
- if (mLeash == null || !mLeash.isValid()) {
- Log.w(TAG, "mLeash is null, abort transition");
- return false;
- }
switch (msg.what) {
case MSG_RESET_IMMEDIATE:
- final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimator(mLeash, currentBounds,
- mDefaultDisplayBounds);
- if (animator != null && animator.isRunning()) {
- animator.cancel();
- }
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- tx.setPosition(mLeash, 0, 0)
- .setWindowCrop(mLeash, -1/* reset */, -1/* reset */)
- .apply();
- finishOffset(currentBounds, yOffset, direction);
+ resetWindowsOffset();
+ mDefaultDisplayBounds.set(currentBounds);
+ mLastVisualDisplayBounds.set(currentBounds);
+ finishOffset(0, TRANSITION_DIRECTION_EXIT);
break;
case MSG_OFFSET_ANIMATE:
- offsetWindows(currentBounds, 0, yOffset, direction, mEnterExitAnimationDurationMs);
+ final Rect toBounds = new Rect(mDefaultDisplayBounds.left,
+ mDefaultDisplayBounds.top + yOffset,
+ mDefaultDisplayBounds.right,
+ mDefaultDisplayBounds.bottom + yOffset);
+ offsetWindows(currentBounds, toBounds, direction, mEnterExitAnimationDurationMs);
break;
case MSG_OFFSET_FINISH:
- finishOffset(currentBounds, yOffset, direction);
+ finishOffset(yOffset, direction);
break;
}
args.recycle();
@@ -152,17 +148,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
@Inject
public OneHandedDisplayAreaOrganizer(Context context,
DisplayController displayController,
- OneHandedAnimationController animationController,
- OneHandedSurfaceTransactionHelper surfaceTransactionHelper) {
-
+ OneHandedAnimationController animationController) {
mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
mAnimationController = animationController;
mDisplayController = displayController;
+ mDefaultDisplayBounds.set(getDisplayBounds());
+ mLastVisualDisplayBounds.set(getDisplayBounds());
mEnterExitAnimationDurationMs = context.getResources().getInteger(
com.android.systemui.R.integer.config_one_handed_translate_animation_duration);
- mDefaultDisplayBounds = new Rect(0, 0, getDisplayBounds().x, getDisplayBounds().y);
- mLastVisualDisplayBounds = new Rect(mDefaultDisplayBounds);
- mSurfaceTransactionHelper = surfaceTransactionHelper;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
}
@@ -171,36 +164,59 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
@NonNull SurfaceControl leash) {
Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null");
Objects.requireNonNull(leash, "leash must not be null");
- mDisplayAreaInfo = displayAreaInfo;
- mLeash = leash;
- }
- @Override
- public void onDisplayAreaInfoChanged(@NonNull DisplayAreaInfo displayAreaInfo) {
- Objects.requireNonNull(displayAreaInfo, "displayArea must not be null");
- // Stop one handed without animation and reset cropped size immediately
- if (displayAreaInfo.configuration.orientation
- != mDisplayAreaInfo.configuration.orientation) {
- final Rect newBounds = displayAreaInfo.configuration.windowConfiguration.getBounds();
- resetImmediately(newBounds);
+ if (displayAreaInfo.featureId != FEATURE_ONE_HANDED) {
+ Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo);
+ return;
}
- mDisplayAreaInfo = displayAreaInfo;
+ // mDefaultDisplayBounds may out of date after removeDisplayChangingController()
+ mDefaultDisplayBounds.set(getDisplayBounds());
+ mDisplayAreaMap.put(displayAreaInfo, leash);
}
@Override
public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
Objects.requireNonNull(displayAreaInfo,
"Requires valid displayArea, and displayArea must not be null");
- if (displayAreaInfo.token.asBinder() != mDisplayAreaInfo.token.asBinder()) {
+
+ if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
return;
}
- mDisplayAreaInfo = displayAreaInfo;
+ mDisplayAreaMap.remove(displayAreaInfo);
+ }
+
+ @Override
+ public void unregisterOrganizer() {
+ super.unregisterOrganizer();
+ resetWindowsOffset();
- // Stop one handed immediately
- if (isInOneHanded()) {
- final Rect newBounds = mDisplayAreaInfo.configuration.windowConfiguration.getBounds();
- resetImmediately(newBounds);
+ // Ensure all cached instance are cleared after resetWindowsOffset
+ mUpdateHandler.post(() -> {
+ if (mDisplayAreaMap != null && !mDisplayAreaMap.isEmpty()) {
+ mDisplayAreaMap.clear();
+ }
+ });
+ }
+
+ /**
+ * Handler for display rotation changes by below policy which
+ * handles 90 degree display rotation changes {@link Surface.Rotation}
+ *
+ */
+ public void onRotateDisplay(int fromRotation, int toRotation) {
+ // Stop one handed without animation and reset cropped size immediately
+ final Rect newBounds = new Rect(mDefaultDisplayBounds);
+ final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
+
+ if (isOrientationDiff) {
+ newBounds.set(newBounds.left, newBounds.top, newBounds.bottom, newBounds.right);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = newBounds;
+ args.argi1 = 0 /* xOffset */;
+ args.argi2 = 0 /* yOffset */;
+ args.argi3 = TRANSITION_DIRECTION_EXIT;
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
}
}
@@ -217,55 +233,43 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
}
- /**
- * Immediately resize/reset leash from previous cropped boundary to default.
- * (i.e Screen rotation need to reset crop and position before applying new bounds)
- */
- public void resetImmediately(Rect newDisplayBounds) {
- updateDisplayBounds(newDisplayBounds);
- if (mDisplayAreaInfo != null && mLeash != null) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = newDisplayBounds;
- args.argi1 = 0 /* xOffset */;
- args.argi2 = 0 /* yOffset */;
- args.argi3 = TRANSITION_DIRECTION_EXIT;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
- }
- }
-
- private void offsetWindows(Rect currentbounds, int xOffset, int yOffset, int direction,
- int durationMs) {
+ private void offsetWindows(Rect fromBounds, Rect toBounds, int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleOffset() instead of this "
+ "directly");
}
- if (mDisplayAreaInfo == null || mLeash == null) {
- Log.w(TAG, "mToken is not set");
- return;
- }
+ mDisplayAreaMap.forEach(
+ (key, leash) -> animateWindows(leash, fromBounds, toBounds, direction,
+ durationMs));
+ }
- Rect toBounds = new Rect(mDefaultDisplayBounds.left,
- mDefaultDisplayBounds.top + yOffset,
- mDefaultDisplayBounds.right,
- mDefaultDisplayBounds.bottom + yOffset);
- animateWindows(currentbounds, toBounds, direction, durationMs);
+ private void resetWindowsOffset() {
+ mUpdateHandler.post(() -> {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ mDisplayAreaMap.forEach(
+ (key, leash) -> {
+ final OneHandedAnimationController.OneHandedTransitionAnimator animator =
+ mAnimationController.getAnimatorMap().remove(leash);
+ if (animator != null && animator.isRunning()) {
+ animator.cancel();
+ }
+ tx.setPosition(leash, 0, 0)
+ .setWindowCrop(leash, -1/* reset */, -1/* reset */);
+ });
+ tx.apply();
+ });
}
- private void animateWindows(Rect fromBounds, Rect toBounds,
+ private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds,
@OneHandedAnimationController.TransitionDirection int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleOffset() instead of "
+ "this directly");
}
- // Could happen when exit one handed
- if (mDisplayAreaInfo == null || mLeash == null) {
- Log.w(TAG, "Abort animation, invalid leash");
- return;
- }
-
mUpdateHandler.post(() -> {
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimator(mLeash, fromBounds, toBounds);
+ mAnimationController.getAnimator(leash, fromBounds, toBounds);
if (animator != null) {
animator.setTransitionDirection(direction)
.setOneHandedAnimationCallback(mOneHandedAnimationCallback)
@@ -275,7 +279,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
});
}
- private void finishOffset(Rect currentBounds, int offset,
+ private void finishOffset(int offset,
@OneHandedAnimationController.TransitionDirection int direction) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException(
@@ -284,18 +288,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
// Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence,
// the flag *MUST* be updated before dispatch mTransitionCallbacks
mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER);
- mLastVisualDisplayBounds.set(currentBounds);
- if (direction == TRANSITION_DIRECTION_TRIGGER) {
- mLastVisualDisplayBounds.offsetTo(0, offset);
- for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
- final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
- callback.onStartFinished(mLastVisualDisplayBounds);
- }
- } else {
- mLastVisualDisplayBounds.offsetTo(0, 0);
- for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
- final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
- callback.onStopFinished(mLastVisualDisplayBounds);
+ mLastVisualDisplayBounds.offsetTo(0,
+ direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0);
+ for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
+ if (direction == TRANSITION_DIRECTION_TRIGGER) {
+ callback.onStartFinished(getLastVisualDisplayBounds());
+ } else {
+ callback.onStopFinished(getLastVisualDisplayBounds());
}
}
}
@@ -319,12 +319,12 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
}
@Nullable
- private Point getDisplayBounds() {
+ private Rect getDisplayBounds() {
Point realSize = new Point(0, 0);
if (mDisplayController != null && mDisplayController.getDisplay(DEFAULT_DISPLAY) != null) {
mDisplayController.getDisplay(DEFAULT_DISPLAY).getRealSize(realSize);
}
- return realSize;
+ return new Rect(0, 0, realSize.x, realSize.y);
}
@VisibleForTesting
@@ -349,24 +349,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
return args;
}
- private void updateDisplayBounds(Rect newDisplayBounds) {
- if (newDisplayBounds == null) {
- mDefaultDisplayBounds.set(0, 0, getDisplayBounds().x, getDisplayBounds().y);
- } else {
- mDefaultDisplayBounds.set(newDisplayBounds);
- }
- }
-
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
pw.print(innerPrefix + "mIsInOneHanded=");
pw.println(mIsInOneHanded);
- pw.print(innerPrefix + "mDisplayAreaInfo=");
- pw.println(mDisplayAreaInfo);
- pw.print(innerPrefix + "mLeash=");
- pw.println(mLeash);
+ pw.print(innerPrefix + "mDisplayAreaMap=");
+ pw.println(mDisplayAreaMap);
pw.print(innerPrefix + "mDefaultDisplayBounds=");
pw.println(mDefaultDisplayBounds);
pw.print(innerPrefix + "mLastVisualDisplayBounds=");
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
new file mode 100644
index 000000000000..71c5f8020330
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.onehanded;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.hardware.input.InputManager;
+import android.os.Looper;
+import android.util.Log;
+import android.view.Display;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.InputMonitor;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.ViewConfiguration;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.NavigationModeController;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * The class manage swipe up and down gesture for 3-Button mode navigation,
+ * others(e.g, 2-button, full gesture mode) are handled by Launcher quick steps.
+ */
+@Singleton
+public class OneHandedGestureHandler implements OneHandedTransitionCallback,
+ NavigationModeController.ModeChangedListener,
+ DisplayChangeController.OnDisplayChangingListener {
+ private static final String TAG = "OneHandedGestureHandler";
+ private static final boolean DEBUG_GESTURE = false;
+
+ private static final int ANGLE_MAX = 150;
+ private static final int ANGLE_MIN = 30;
+ private final float mDragDistThreshold;
+ private final float mSquaredSlop;
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final PointF mStartDragPos = new PointF();
+ private boolean mPassedSlop;
+
+ private boolean mAllowGesture;
+ private boolean mIsEnabled;
+ private int mNavGestureHeight;
+ private boolean mIsThreeButtonModeEnable;
+ private int mRotation = Surface.ROTATION_0;
+
+ @VisibleForTesting
+ InputMonitor mInputMonitor;
+ @VisibleForTesting
+ InputEventReceiver mInputEventReceiver;
+ private DisplayController mDisplayController;
+ @VisibleForTesting
+ @Nullable
+ OneHandedGestureEventCallback mGestureEventCallback;
+ private Rect mGestureRegion = new Rect();
+
+ /**
+ * Constructor of OneHandedGestureHandler, we only handle the gesture of
+ * {@link Display#DEFAULT_DISPLAY}
+ *
+ * @param context {@link Context}
+ * @param displayController {@link DisplayController}
+ * @param navigationModeController {@link NavigationModeController}
+ */
+ @Inject
+ public OneHandedGestureHandler(Context context, DisplayController displayController,
+ NavigationModeController navigationModeController) {
+ mDisplayController = displayController;
+ displayController.addDisplayChangingController(this);
+ final int NavBarMode = navigationModeController.addListener(this);
+ mIsThreeButtonModeEnable = (NavBarMode == NAV_BAR_MODE_3BUTTON);
+ mNavGestureHeight = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_gesture_height);
+ mDragDistThreshold = context.getResources().getDimensionPixelSize(
+ R.dimen.gestures_onehanded_drag_threshold);
+ final float slop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mSquaredSlop = slop * slop;
+ updateIsEnabled();
+ }
+
+ /**
+ * Notified by {@link OneHandedManager}, when user update settings of Enabled or Disabled
+ *
+ * @param isEnabled is one handed settings enabled or not
+ */
+ public void onOneHandedEnabled(boolean isEnabled) {
+ if (DEBUG_GESTURE) {
+ Log.d(TAG, "onOneHandedEnabled, isEnabled = " + isEnabled);
+ }
+ mIsEnabled = isEnabled;
+ updateIsEnabled();
+ }
+
+ /**
+ * Register {@link OneHandedGestureEventCallback} to receive onStart(), onStop() callback
+ */
+ public void setGestureEventListener(OneHandedGestureEventCallback callback) {
+ mGestureEventCallback = callback;
+ }
+
+ private void onMotionEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mAllowGesture = isWithinTouchRegion(ev.getX(), ev.getY())
+ && mRotation == Surface.ROTATION_0;
+ if (mAllowGesture) {
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ }
+ if (DEBUG_GESTURE) {
+ Log.d(TAG, "ACTION_DOWN, mDownPos=" + mDownPos + ", mAllowGesture="
+ + mAllowGesture);
+ }
+ } else if (mAllowGesture) {
+ switch (action) {
+ case MotionEvent.ACTION_MOVE:
+ mLastPos.set(ev.getX(), ev.getY());
+ if (!mPassedSlop) {
+ if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
+ > mSquaredSlop) {
+ mStartDragPos.set(mLastPos.x, mLastPos.y);
+ if (isValidStartAngle(
+ mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)
+ || isValidExitAngle(
+ mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)) {
+ mPassedSlop = true;
+ mInputMonitor.pilferPointers();
+ }
+ }
+ } else {
+ float distance = (float) Math.hypot(mLastPos.x - mDownPos.x,
+ mLastPos.y - mDownPos.y);
+ if (distance > mDragDistThreshold && mPassedSlop) {
+ mGestureEventCallback.onStop();
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mLastPos.y >= mDownPos.y && mPassedSlop) {
+ mGestureEventCallback.onStart();
+ }
+ mPassedSlop = false;
+ mAllowGesture = false;
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ mPassedSlop = false;
+ mAllowGesture = false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private void disposeInputChannel() {
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
+ }
+
+ if (mInputMonitor != null) {
+ mInputMonitor.dispose();
+ mInputMonitor = null;
+ }
+ }
+
+ private boolean isWithinTouchRegion(float x, float y) {
+ if (DEBUG_GESTURE) {
+ Log.d(TAG, "isWithinTouchRegion(), mGestureRegion=" + mGestureRegion + ", downX=" + x
+ + ", downY=" + y);
+ }
+ return mGestureRegion.contains(Math.round(x), Math.round(y));
+ }
+
+ private void updateIsEnabled() {
+ disposeInputChannel();
+
+ if (mIsEnabled && mIsThreeButtonModeEnable) {
+ final Point displaySize = new Point();
+ if (mDisplayController != null) {
+ final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
+ if (display != null) {
+ display.getRealSize(displaySize);
+ }
+ }
+ // Register input event receiver to monitor the touch region of NavBar gesture height
+ mGestureRegion.set(0, displaySize.y - mNavGestureHeight, displaySize.x,
+ displaySize.y);
+ mInputMonitor = InputManager.getInstance().monitorGestureInput(
+ "onehanded-gesture-offset", DEFAULT_DISPLAY);
+ mInputEventReceiver = new SysUiInputEventReceiver(
+ mInputMonitor.getInputChannel(), Looper.getMainLooper());
+ }
+ }
+
+ private void onInputEvent(InputEvent ev) {
+ if (ev instanceof MotionEvent) {
+ onMotionEvent((MotionEvent) ev);
+ }
+ }
+
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ if (DEBUG_GESTURE) {
+ Log.d(TAG, "onNavigationModeChanged, mode =" + mode);
+ }
+ mIsThreeButtonModeEnable = (mode == NAV_BAR_MODE_3BUTTON);
+ updateIsEnabled();
+ }
+
+ @Override
+ public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
+ WindowContainerTransaction t) {
+ mRotation = toRotation;
+ }
+
+ private class SysUiInputEventReceiver extends InputEventReceiver {
+ SysUiInputEventReceiver(InputChannel channel, Looper looper) {
+ super(channel, looper);
+ }
+
+ public void onInputEvent(InputEvent event) {
+ OneHandedGestureHandler.this.onInputEvent(event);
+ finishInputEvent(event, true);
+ }
+ }
+
+ private boolean isValidStartAngle(float deltaX, float deltaY) {
+ final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
+ return angle > -(ANGLE_MAX) && angle < -(ANGLE_MIN);
+ }
+
+ private boolean isValidExitAngle(float deltaX, float deltaY) {
+ final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
+ return angle > ANGLE_MIN && angle < ANGLE_MAX;
+ }
+
+ private float squaredHypot(float x, float y) {
+ return x * x + y * y;
+ }
+
+ /**
+ * The touch(gesture) events to notify {@link OneHandedManager} start or stop one handed
+ */
+ public interface OneHandedGestureEventCallback {
+ /**
+ * Handle the start event event, and return whether the event was consumed.
+ */
+ boolean onStart();
+
+ /**
+ * Handle the exit event event, and return whether the event was consumed.
+ */
+ boolean onStop();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index bae25757b4e0..70a81aaed249 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -27,12 +27,14 @@ import android.graphics.Rect;
import androidx.annotation.NonNull;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -50,13 +52,18 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
private boolean mIsOneHandedEnabled;
private boolean mTaskChangeToExit;
private float mOffSetFraction;
- private DisplayController mDisplayController;
+
+ private final DisplayController mDisplayController;
+ private final OneHandedGestureHandler mGestureHandler;
+ private final OneHandedTimeoutHandler mTimeoutHandler;
+ private final OneHandedTouchHandler mTouchHandler;
+ private final SysUiState mSysUiFlagContainer;
+
+ private Context mContext;
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
- private OneHandedTimeoutHandler mTimeoutHandler;
- private OneHandedTouchHandler mTouchHandler;
+ private OneHandedGestureHandler.OneHandedGestureEventCallback mGestureEventCallback;
private OneHandedTouchHandler.OneHandedTouchEventCallback mTouchEventCallback;
private OneHandedTransitionCallback mTransitionCallback;
- private SysUiState mSysUiFlagContainer;
/**
* Handler for system task stack changes, exit when user lunch new task or bring task to front
@@ -82,6 +89,17 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
};
/**
+ * Handle rotation based on OnDisplayChangingListener callback
+ */
+ @VisibleForTesting
+ private final DisplayChangeController.OnDisplayChangingListener mRotationController =
+ (display, fromRotation, toRotation, wct) -> {
+ if (mDisplayAreaOrganizer != null) {
+ mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation);
+ }
+ };
+
+ /**
* Constructor of OneHandedManager
*/
@Inject
@@ -89,10 +107,12 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
DisplayController displayController,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
OneHandedTouchHandler touchHandler,
+ OneHandedGestureHandler gestureHandler,
SysUiState sysUiState) {
-
+ mContext = context;
mDisplayAreaOrganizer = displayAreaOrganizer;
mDisplayController = displayController;
+ mDisplayController.addDisplayChangingController(mRotationController);
mSysUiFlagContainer = sysUiState;
mOffSetFraction =
context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1);
@@ -100,6 +120,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
context.getContentResolver());
mTimeoutHandler = OneHandedTimeoutHandler.get();
mTouchHandler = touchHandler;
+ mGestureHandler = gestureHandler;
updateOneHandedEnabled();
setupGestures();
}
@@ -170,6 +191,29 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
};
mTouchHandler.registerTouchEventListener(mTouchEventCallback);
+ mGestureEventCallback = new OneHandedGestureHandler.OneHandedGestureEventCallback() {
+ @Override
+ public boolean onStart() {
+ boolean result = false;
+ if (!mDisplayAreaOrganizer.isInOneHanded()) {
+ startOneHanded();
+ result = true;
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onStop() {
+ boolean result = false;
+ if (mDisplayAreaOrganizer.isInOneHanded()) {
+ stopOneHanded();
+ result = true;
+ }
+ return result;
+ }
+ };
+ mGestureHandler.setGestureEventListener(mGestureEventCallback);
+
mTransitionCallback = new OneHandedTransitionCallback() {
@Override
public void onStartFinished(Rect bounds) {
@@ -185,6 +229,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
};
mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallback);
mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
+ mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
}
/**
@@ -215,6 +260,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
}
mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
+ mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
index 0f4e6be76721..9239435f1622 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
@@ -29,7 +29,9 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.provider.Settings;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -56,6 +58,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
private static final String TAG = "OneHandedUI";
private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
"com.android.internal.systemui.onehanded.gestural";
+ private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
private final OneHandedManagerImpl mOneHandedManager;
private final CommandQueue mCommandQueue;
@@ -136,10 +139,18 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
ScreenLifecycle screenLifecycle) {
super(context);
+ if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
+ mCommandQueue = null;
+ mOneHandedManager = null;
+ mOverlayManager = null;
+ mSettingUtil = null;
+ mTimeoutHandler = null;
+ mScreenLifecycle = null;
+ return;
+ }
+
mCommandQueue = commandQueue;
- /* TODO(b/154290458) define a boolean system properties "support_one_handed_mode"
- boolean supportOneHanded = SystemProperties.getBoolean("support_one_handed_mode");
- if (!supportOneHanded) return; */
mOneHandedManager = oneHandedManager;
mSettingUtil = settingsUtil;
mTimeoutHandler = OneHandedTimeoutHandler.get();
@@ -150,9 +161,9 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
@Override
public void start() {
- /* TODO(b/154290458) define a boolean system properties "support_one_handed_mode"
- boolean supportOneHanded = SystemProperties.getBoolean("support_one_handed_mode");
- if (!supportOneHanded) return; */
+ if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ return;
+ }
mCommandQueue.addCallback(this);
setupKeyguardUpdateMonitor();
setupScreenObserver();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index 2980f11b3cbc..72019315139b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -18,16 +18,15 @@ package com.android.systemui.pip;
import android.animation.AnimationHandler;
import android.animation.Animator;
+import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
-import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceControl;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.Interpolators;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -56,13 +55,15 @@ public class PipAnimationController {
public static final int TRANSITION_DIRECTION_TO_PIP = 2;
public static final int TRANSITION_DIRECTION_TO_FULLSCREEN = 3;
public static final int TRANSITION_DIRECTION_TO_SPLIT_SCREEN = 4;
+ public static final int TRANSITION_DIRECTION_REMOVE_STACK = 5;
@IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = {
TRANSITION_DIRECTION_NONE,
TRANSITION_DIRECTION_SAME,
TRANSITION_DIRECTION_TO_PIP,
TRANSITION_DIRECTION_TO_FULLSCREEN,
- TRANSITION_DIRECTION_TO_SPLIT_SCREEN
+ TRANSITION_DIRECTION_TO_SPLIT_SCREEN,
+ TRANSITION_DIRECTION_REMOVE_STACK
})
@Retention(RetentionPolicy.SOURCE)
public @interface TransitionDirection {}
@@ -76,7 +77,6 @@ public class PipAnimationController {
|| direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN;
}
- private final Interpolator mFastOutSlowInInterpolator;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private PipTransitionAnimator mCurrentAnimator;
@@ -89,9 +89,7 @@ public class PipAnimationController {
});
@Inject
- PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
+ PipAnimationController(PipSurfaceTransactionHelper helper) {
mSurfaceTransactionHelper = helper;
}
@@ -113,10 +111,11 @@ public class PipAnimationController {
}
@SuppressWarnings("unchecked")
- PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds) {
+ PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds,
+ Rect sourceHintRect) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
+ PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
// If we are still animating the fade into pip, then just move the surface and ensure
@@ -131,7 +130,7 @@ public class PipAnimationController {
} else {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
+ PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
}
return mCurrentAnimator;
}
@@ -142,7 +141,7 @@ public class PipAnimationController {
private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
- animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.setFloatValues(FRACTION_START, FRACTION_END);
animator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
return animator;
@@ -340,7 +339,12 @@ public class PipAnimationController {
@Override
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
+ if (getTransitionDirection() == TRANSITION_DIRECTION_REMOVE_STACK) {
+ // while removing the pip stack, no extra work needs to be done here.
+ return;
+ }
getSurfaceTransactionHelper()
+ .resetScale(tx, leash, getDestinationBounds())
.crop(tx, leash, getDestinationBounds())
.round(tx, leash, shouldApplyCornerRadius());
tx.show(leash);
@@ -356,35 +360,46 @@ public class PipAnimationController {
}
static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
- Rect startValue, Rect endValue) {
+ Rect startValue, Rect endValue, Rect sourceHintRect) {
+ // Just for simplicity we'll interpolate between the source rect hint insets and empty
+ // insets to calculate the window crop
+ final Rect initialStartValue = new Rect(startValue);
+ final Rect sourceHintRectInsets = sourceHintRect != null
+ ? new Rect(sourceHintRect.left - startValue.left,
+ sourceHintRect.top - startValue.top,
+ startValue.right - sourceHintRect.right,
+ startValue.bottom - sourceHintRect.bottom)
+ : null;
+ final Rect sourceInsets = new Rect(0, 0, 0, 0);
+
// construct new Rect instances in case they are recycled
return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
endValue, new Rect(startValue), new Rect(endValue)) {
- private final Rect mTmpRect = new Rect();
-
- private int getCastedFractionValue(float start, float end, float fraction) {
- return (int) (start * (1 - fraction) + end * fraction + .5f);
- }
+ private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
+ private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
@Override
void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction) {
final Rect start = getStartValue();
final Rect end = getEndValue();
- mTmpRect.set(
- getCastedFractionValue(start.left, end.left, fraction),
- getCastedFractionValue(start.top, end.top, fraction),
- getCastedFractionValue(start.right, end.right, fraction),
- getCastedFractionValue(start.bottom, end.bottom, fraction));
- setCurrentValue(mTmpRect);
+ Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
+ setCurrentValue(bounds);
if (inScaleTransition()) {
if (isOutPipDirection(getTransitionDirection())) {
- getSurfaceTransactionHelper().scale(tx, leash, end, mTmpRect);
+ getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
} else {
- getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect);
+ getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
}
} else {
- getSurfaceTransactionHelper().crop(tx, leash, mTmpRect);
+ if (sourceHintRectInsets != null) {
+ Rect insets = mInsetsEvaluator.evaluate(fraction, sourceInsets,
+ sourceHintRectInsets);
+ getSurfaceTransactionHelper().scaleAndCrop(tx, leash, initialStartValue,
+ bounds, insets);
+ } else {
+ getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
+ }
}
tx.apply();
}
@@ -400,11 +415,11 @@ public class PipAnimationController {
@Override
void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
- if (!inScaleTransition()) return;
// NOTE: intentionally does not apply the transaction here.
// this end transaction should get executed synchronously with the final
// WindowContainerTransaction in task organizer
- getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds())
+ getSurfaceTransactionHelper()
+ .resetScale(tx, leash, getDestinationBounds())
.crop(tx, leash, getDestinationBounds());
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 0d3a16ec1028..df3aeadaacd6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -34,12 +34,13 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.util.TypedValue;
+import android.view.Display;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.window.WindowContainerTransaction;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayLayout;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import java.io.PrintWriter;
@@ -60,7 +61,7 @@ public class PipBoundsHandler {
private final PipSnapAlgorithm mSnapAlgorithm;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final DisplayController mDisplayController;
- private final DisplayLayout mDisplayLayout;
+ private DisplayLayout mDisplayLayout;
private ComponentName mLastPipComponentName;
private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
@@ -177,7 +178,7 @@ public class PipBoundsHandler {
}
if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
transformBoundsToAspectRatio(normalBounds, mAspectRatio,
- false /* useCurrentMinEdgeSize */);
+ false /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
}
displayInfo.copyFrom(mDisplayInfo);
}
@@ -278,7 +279,9 @@ public class PipBoundsHandler {
destinationBounds = new Rect(bounds);
}
if (isValidPictureInPictureAspectRatio(aspectRatio)) {
- transformBoundsToAspectRatio(destinationBounds, aspectRatio, useCurrentMinEdgeSize);
+ boolean useCurrentSize = bounds == null && mReentrySize != null;
+ transformBoundsToAspectRatio(destinationBounds, aspectRatio, useCurrentMinEdgeSize,
+ useCurrentSize);
}
mAspectRatio = aspectRatio;
return destinationBounds;
@@ -288,6 +291,28 @@ public class PipBoundsHandler {
return mDefaultAspectRatio;
}
+ public void onOverlayChanged(Context context, Display display) {
+ mDisplayLayout = new DisplayLayout(context, display);
+ }
+
+ /**
+ * Updatest the display info and display layout on rotation change. This is needed even when we
+ * aren't in PIP because the rotation layout is used to calculate the proper insets for the
+ * next enter animation into PIP.
+ */
+ public void onDisplayRotationChangedNotInPip(int toRotation) {
+ // Update the display layout, note that we have to do this on every rotation even if we
+ // aren't in PIP since we need to update the display layout to get the right resources
+ mDisplayLayout.rotateTo(mContext.getResources(), toRotation);
+
+ // Populate the new {@link #mDisplayInfo}.
+ // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
+ // therefore, the width/height may require a swap first.
+ // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
+ mDisplayInfo.rotation = toRotation;
+ updateDisplayInfoIfNeeded();
+ }
+
/**
* Updates the display info, calculating and returning the new stack and movement bounds in the
* new orientation of the device if necessary.
@@ -366,7 +391,8 @@ public class PipBoundsHandler {
* @param stackBounds
*/
public void transformBoundsToAspectRatio(Rect stackBounds) {
- transformBoundsToAspectRatio(stackBounds, mAspectRatio, true);
+ transformBoundsToAspectRatio(stackBounds, mAspectRatio, true /* useCurrentMinEdgeSize */,
+ true /* useCurrentSize */);
}
/**
@@ -374,18 +400,16 @@ public class PipBoundsHandler {
* specified aspect ratio.
*/
private void transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
- boolean useCurrentMinEdgeSize) {
+ boolean useCurrentMinEdgeSize, boolean useCurrentSize) {
// Save the snap fraction and adjust the size based on the new aspect ratio.
final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
getMovementBounds(stackBounds));
- final int minEdgeSize;
+ final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
final Size size;
- if (useCurrentMinEdgeSize) {
- minEdgeSize = mCurrentMinSize;
+ if (useCurrentMinEdgeSize || useCurrentSize) {
size = mSnapAlgorithm.getSizeForAspectRatio(
new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
} else {
- minEdgeSize = mDefaultMinSize;
size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
index fc41d2ea8862..2c7ec48e4ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
@@ -23,8 +23,8 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.view.SurfaceControl;
-import com.android.systemui.R;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.R;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -44,6 +44,7 @@ public class PipSurfaceTransactionHelper implements ConfigurationController.Conf
private final float[] mTmpFloat9 = new float[9];
private final RectF mTmpSourceRectF = new RectF();
private final RectF mTmpDestinationRectF = new RectF();
+ private final Rect mTmpDestinationRect = new Rect();
@Inject
public PipSurfaceTransactionHelper(Context context, ConfigurationController configController) {
@@ -90,7 +91,30 @@ public class PipSurfaceTransactionHelper implements ConfigurationController.Conf
mTmpDestinationRectF.set(destinationBounds);
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
- .setPosition(leash, destinationBounds.left, destinationBounds.top);
+ .setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top);
+ return this;
+ }
+
+ /**
+ * Operates the scale (setMatrix) on a given transaction and leash
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect sourceBounds, Rect destinationBounds, Rect insets) {
+ mTmpSourceRectF.set(sourceBounds);
+ mTmpDestinationRect.set(sourceBounds);
+ mTmpDestinationRect.inset(insets);
+ // Scale by the shortest edge and offset such that the top/left of the scaled inset source
+ // rect aligns with the top/left of the destination bounds
+ final float scale = sourceBounds.width() <= sourceBounds.height()
+ ? (float) destinationBounds.width() / sourceBounds.width()
+ : (float) destinationBounds.height() / sourceBounds.height();
+ final float left = destinationBounds.left - insets.left * scale;
+ final float top = destinationBounds.top - insets.top * scale;
+ mTmpTransform.setScale(scale, scale);
+ tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+ .setWindowCrop(leash, mTmpDestinationRect)
+ .setPosition(leash, left, top);
return this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index e2feb71735ff..312d6d62128f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
+import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
@@ -55,10 +56,10 @@ import android.window.WindowContainerTransactionCallback;
import android.window.WindowOrganizer;
import com.android.internal.os.SomeArgs;
-import com.android.systemui.R;
import com.android.systemui.pip.phone.PipUpdateThread;
import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -143,8 +144,10 @@ public class PipTaskOrganizer extends TaskOrganizer implements
case MSG_RESIZE_ANIMATE: {
Rect currentBounds = (Rect) args.arg2;
Rect toBounds = (Rect) args.arg3;
+ Rect sourceHintRect = (Rect) args.arg4;
int duration = args.argi2;
- animateResizePip(currentBounds, toBounds, args.argi1 /* direction */, duration);
+ animateResizePip(currentBounds, toBounds, sourceHintRect,
+ args.argi1 /* direction */, duration);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
@@ -175,6 +178,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements
Rect startBounds = (Rect) args.arg2;
Rect toBounds = (Rect) args.arg3;
userResizePip(startBounds, toBounds);
+ if (updateBoundsCallback != null) {
+ updateBoundsCallback.accept(toBounds);
+ }
break;
}
}
@@ -224,10 +230,23 @@ public class PipTaskOrganizer extends TaskOrganizer implements
return new Rect(mLastReportedBounds);
}
+ public Rect getCurrentOrAnimatingBounds() {
+ PipAnimationController.PipTransitionAnimator animator =
+ mPipAnimationController.getCurrentAnimator();
+ if (animator != null && animator.isRunning()) {
+ return new Rect(animator.getDestinationBounds());
+ }
+ return getLastReportedBounds();
+ }
+
public boolean isInPip() {
return mInPip;
}
+ public boolean isDeferringEnterPipAnimation() {
+ return mInPip && mShouldDeferEnteringPip;
+ }
+
/**
* Registers {@link PipTransitionCallback} to receive transition callbacks.
*/
@@ -294,7 +313,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
t.apply();
scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
- direction, animationDurationMs, null /* updateBoundsCallback */);
+ null /* sourceHintRect */, direction, animationDurationMs,
+ null /* updateBoundsCallback */);
mInPip = false;
}
});
@@ -321,23 +341,32 @@ public class PipTaskOrganizer extends TaskOrganizer implements
+ " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken);
return;
}
- getUpdateHandler().post(() -> {
- try {
- // Reset the task bounds first to ensure the activity configuration is reset as well
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(mToken, null);
- WindowOrganizer.applyTransaction(wct);
-
- ActivityTaskManager.getService().removeStacksInWindowingModes(
- new int[]{ WINDOWING_MODE_PINNED });
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to remove PiP", e);
- }
- });
+
+ // removePipImmediately is expected when the following animation finishes.
+ mUpdateHandler.post(() -> mPipAnimationController
+ .getAnimator(mLeash, mLastReportedBounds, 1f, 0f)
+ .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(mEnterExitAnimationDuration)
+ .start());
mInitialState.remove(mToken.asBinder());
mExitingPip = true;
}
+ private void removePipImmediately() {
+ try {
+ // Reset the task bounds first to ensure the activity configuration is reset as well
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mToken, null);
+ WindowOrganizer.applyTransaction(wct);
+
+ ActivityTaskManager.getService().removeStacksInWindowingModes(
+ new int[]{ WINDOWING_MODE_PINNED });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to remove PiP", e);
+ }
+ }
+
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
Objects.requireNonNull(info, "Requires RunningTaskInfo");
@@ -367,7 +396,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
- scheduleAnimateResizePip(currentBounds, destinationBounds,
+ final Rect sourceHintRect = getValidSourceHintRect(info, currentBounds);
+ scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect,
TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
null /* updateBoundsCallback */);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
@@ -378,6 +408,21 @@ public class PipTaskOrganizer extends TaskOrganizer implements
}
}
+ /**
+ * Returns the source hint rect if it is valid (if provided and is contained by the current
+ * task bounds).
+ */
+ private Rect getValidSourceHintRect(ActivityManager.RunningTaskInfo info, Rect sourceBounds) {
+ final Rect sourceHintRect = info.pictureInPictureParams != null
+ && info.pictureInPictureParams.hasSourceBoundsHint()
+ ? info.pictureInPictureParams.getSourceRectHint()
+ : null;
+ if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) {
+ return sourceHintRect;
+ }
+ return null;
+ }
+
private void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) {
// If we are fading the PIP in, then we should move the pip to the final location as
// soon as possible, but set the alpha immediately since the transaction can take a
@@ -406,7 +451,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
private void sendOnPipTransitionStarted(
@PipAnimationController.TransitionDirection int direction) {
- mMainHandler.post(() -> {
+ runOnMainHandler(() -> {
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
callback.onPipTransitionStarted(mTaskInfo.baseActivity, direction);
@@ -416,7 +461,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
private void sendOnPipTransitionFinished(
@PipAnimationController.TransitionDirection int direction) {
- mMainHandler.post(() -> {
+ runOnMainHandler(() -> {
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction);
@@ -426,7 +471,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
private void sendOnPipTransitionCancelled(
@PipAnimationController.TransitionDirection int direction) {
- mMainHandler.post(() -> {
+ runOnMainHandler(() -> {
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction);
@@ -434,6 +479,14 @@ public class PipTaskOrganizer extends TaskOrganizer implements
});
}
+ private void runOnMainHandler(Runnable r) {
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ r.run();
+ } else {
+ mMainHandler.post(r);
+ }
+ }
+
/**
* Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
* Meanwhile this callback is invoked whenever the task is removed. For instance:
@@ -505,15 +558,33 @@ public class PipTaskOrganizer extends TaskOrganizer implements
*/
@SuppressWarnings("unchecked")
public void onMovementBoundsChanged(Rect destinationBoundsOut, boolean fromRotation,
- boolean fromImeAdjustment, boolean fromShelfAdjustment) {
+ boolean fromImeAdjustment, boolean fromShelfAdjustment,
+ WindowContainerTransaction wct) {
final PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getCurrentAnimator();
if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
if (mInPip && fromRotation) {
- // this could happen if rotation finishes before the animation
+ // If we are rotating while there is a current animation, immediately cancel the
+ // animation (remove the listeners so we don't trigger the normal finish resize
+ // call that should only happen on the update thread)
+ int direction = TRANSITION_DIRECTION_NONE;
+ if (animator != null) {
+ direction = animator.getTransitionDirection();
+ animator.removeAllUpdateListeners();
+ animator.removeAllListeners();
+ animator.cancel();
+ // Do notify the listeners that this was canceled
+ sendOnPipTransitionCancelled(direction);
+ sendOnPipTransitionFinished(direction);
+ }
mLastReportedBounds.set(destinationBoundsOut);
- scheduleFinishResizePip(mLastReportedBounds);
+
+ // Create a reset surface transaction for the new bounds and update the window
+ // container transaction
+ final SurfaceControl.Transaction tx = createFinishResizeSurfaceTransaction(
+ destinationBoundsOut);
+ prepareFinishResizeTransaction(destinationBoundsOut, direction, tx, wct);
} else {
// There could be an animation on-going. If there is one on-going, last-reported
// bounds isn't yet updated. We'll use the animator's bounds instead.
@@ -572,15 +643,17 @@ public class PipTaskOrganizer extends TaskOrganizer implements
Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
return;
}
- scheduleAnimateResizePip(mLastReportedBounds, toBounds,
+ scheduleAnimateResizePip(mLastReportedBounds, toBounds, null /* sourceHintRect */,
TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
}
private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
- @PipAnimationController.TransitionDirection int direction, int durationMs,
- Consumer<Rect> updateBoundsCallback) {
+ Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction,
+ int durationMs, Consumer<Rect> updateBoundsCallback) {
if (!mInPip) {
- // can be initiated in other component, ignore if we are no longer in PIP
+ // TODO: tend to use shouldBlockResizeRequest here as well but need to consider
+ // the fact that when in exitPip, scheduleAnimateResizePip is executed in the window
+ // container transaction callback and we want to set the mExitingPip immediately.
return;
}
@@ -588,6 +661,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
args.arg1 = updateBoundsCallback;
args.arg2 = currentBounds;
args.arg3 = destinationBounds;
+ args.arg4 = sourceHintRect;
args.argi1 = direction;
args.argi2 = durationMs;
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
@@ -622,7 +696,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
* {@link #scheduleResizePip}.
*/
public void scheduleFinishResizePip(Rect destinationBounds) {
- scheduleFinishResizePip(destinationBounds, null);
+ scheduleFinishResizePip(destinationBounds, null /* updateBoundsCallback */);
}
/**
@@ -630,37 +704,41 @@ public class PipTaskOrganizer extends TaskOrganizer implements
*/
public void scheduleFinishResizePip(Rect destinationBounds,
Consumer<Rect> updateBoundsCallback) {
- final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- mSurfaceTransactionHelper
- .crop(tx, mLeash, destinationBounds)
- .resetScale(tx, mLeash, destinationBounds)
- .round(tx, mLeash, mInPip);
- scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE,
- updateBoundsCallback);
+ scheduleFinishResizePip(destinationBounds, TRANSITION_DIRECTION_NONE, updateBoundsCallback);
}
- private void scheduleFinishResizePip(SurfaceControl.Transaction tx,
- Rect destinationBounds, @PipAnimationController.TransitionDirection int direction,
+ private void scheduleFinishResizePip(Rect destinationBounds,
+ @PipAnimationController.TransitionDirection int direction,
Consumer<Rect> updateBoundsCallback) {
- if (!mInPip) {
- // can be initiated in other component, ignore if we are no longer in PIP
+ if (shouldBlockResizeRequest()) {
return;
}
+
SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback;
- args.arg2 = tx;
+ args.arg2 = createFinishResizeSurfaceTransaction(
+ destinationBounds);
args.arg3 = destinationBounds;
args.argi1 = direction;
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args));
}
+ private SurfaceControl.Transaction createFinishResizeSurfaceTransaction(
+ Rect destinationBounds) {
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper
+ .crop(tx, mLeash, destinationBounds)
+ .resetScale(tx, mLeash, destinationBounds)
+ .round(tx, mLeash, mInPip);
+ return tx;
+ }
+
/**
* Offset the PiP window by a given offset on Y-axis, triggered also from screen rotation.
*/
public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
Consumer<Rect> updateBoundsCallback) {
- if (!mInPip) {
- // can be initiated in other component, ignore if we are no longer in PIP
+ if (shouldBlockResizeRequest()) {
return;
}
if (mShouldDeferEnteringPip) {
@@ -687,7 +765,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements
}
final Rect destinationBounds = new Rect(originalBounds);
destinationBounds.offset(xOffset, yOffset);
- animateResizePip(originalBounds, destinationBounds, TRANSITION_DIRECTION_SAME, durationMs);
+ animateResizePip(originalBounds, destinationBounds, null /* sourceHintRect */,
+ TRANSITION_DIRECTION_SAME, durationMs);
}
private void resizePip(Rect destinationBounds) {
@@ -737,11 +816,22 @@ public class PipTaskOrganizer extends TaskOrganizer implements
+ "directly");
}
mLastReportedBounds.set(destinationBounds);
- if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
+ if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
+ removePipImmediately();
+ return;
+ } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
return;
}
- final WindowContainerTransaction wct = new WindowContainerTransaction();
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareFinishResizeTransaction(destinationBounds, direction, tx, wct);
+ applyFinishBoundsResize(wct, direction);
+ }
+
+ private void prepareFinishResizeTransaction(Rect destinationBounds,
+ @PipAnimationController.TransitionDirection int direction,
+ SurfaceControl.Transaction tx,
+ WindowContainerTransaction wct) {
final Rect taskBounds;
if (isInPipDirection(direction)) {
// If we are animating from fullscreen using a bounds animation, then reset the
@@ -762,7 +852,6 @@ public class PipTaskOrganizer extends TaskOrganizer implements
wct.setBounds(mToken, taskBounds);
wct.setBoundsChangeTransaction(mToken, tx);
- applyFinishBoundsResize(wct, direction);
}
/**
@@ -786,7 +875,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements
return WINDOWING_MODE_UNDEFINED;
}
- private void animateResizePip(Rect currentBounds, Rect destinationBounds,
+
+ private void animateResizePip(Rect currentBounds, Rect destinationBounds, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
@@ -798,7 +888,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
return;
}
mPipAnimationController
- .getAnimator(mLeash, currentBounds, destinationBounds)
+ .getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
@@ -819,12 +909,22 @@ public class PipTaskOrganizer extends TaskOrganizer implements
}
private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
- return params == null
+ return params == null || !params.hasSetAspectRatio()
? mPipBoundsHandler.getDefaultAspectRatio()
: params.getAspectRatio();
}
/**
+ * Resize request can be initiated in other component, ignore if we are no longer in PIP
+ * or we're exiting from it.
+ *
+ * @return {@code true} if the resize request should be blocked/ignored.
+ */
+ private boolean shouldBlockResizeRequest() {
+ return !mInPip || mExitingPip;
+ }
+
+ /**
* Sync with {@link #mSplitDivider} on destination bounds if PiP is going to split screen.
*
* @param destinationBoundsOut contain the updated destination bounds if applicable
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java
index 8b3f4cb196bf..c715398d52da 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java
@@ -27,9 +27,9 @@ import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
-import com.android.systemui.R;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.wm.shell.R;
import java.util.ArrayList;
import java.util.List;
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 7a18ec361860..582cd046f9e0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityManager;
+import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -52,10 +53,11 @@ 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;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
import java.io.PrintWriter;
@@ -76,7 +78,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final Rect mTmpInsetBounds = new Rect();
private final Rect mTmpNormalBounds = new Rect();
- private final Rect mReentryBounds = new Rect();
+ protected final Rect mReentryBounds = new Rect();
private PipBoundsHandler mPipBoundsHandler;
private InputConsumerController mInputConsumerController;
@@ -94,9 +96,18 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
*/
private final DisplayChangeController.OnDisplayChangingListener mRotationController = (
int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> {
+ if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) {
+ // Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update
+ // the display layout in the bounds handler in this case.
+ mPipBoundsHandler.onDisplayRotationChangedNotInPip(toRotation);
+ return;
+ }
+ // If there is an animation running (ie. from a shelf offset), then ensure that we calculate
+ // the bounds for the next orientation using the destination bounds of the animation
+ // TODO: Techincally this should account for movement animation bounds as well
+ Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds();
final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mTmpNormalBounds,
- mPipTaskOrganizer.getLastReportedBounds(), mTmpInsetBounds, displayId, fromRotation,
- toRotation, t);
+ currentBounds, mTmpInsetBounds, displayId, fromRotation, toRotation, t);
if (changed) {
// If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the
// movement bounds
@@ -116,7 +127,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
}
updateMovementBounds(mTmpNormalBounds, true /* fromRotation */,
- false /* fromImeAdjustment */, false /* fromShelfAdjustment */);
+ false /* fromImeAdjustment */, false /* fromShelfAdjustment */, t);
}
};
@@ -166,7 +177,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
- if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
+ if (task.configuration.windowConfiguration.getWindowingMode()
!= WINDOWING_MODE_PINNED) {
return;
}
@@ -194,11 +205,12 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
@Override
public void onMovementBoundsChanged(boolean fromImeAdjustment) {
mHandler.post(() -> updateMovementBounds(null /* toBounds */,
- false /* fromRotation */, fromImeAdjustment, false /* fromShelfAdjustment */));
+ false /* fromRotation */, fromImeAdjustment, false /* fromShelfAdjustment */,
+ null /* windowContainerTransaction */));
}
@Override
- public void onActionsChanged(ParceledListSlice actions) {
+ public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
mHandler.post(() -> mMenuController.setAppActions(actions));
}
@@ -223,6 +235,19 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
}
}
+ public ConfigurationController.ConfigurationListener mOverlayChangedListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onOverlayChanged() {
+ mHandler.post(() -> {
+ mPipBoundsHandler.onOverlayChanged(mContext, mContext.getDisplay());
+ updateMovementBounds(null /* toBounds */,
+ false /* fromRotation */, false /* fromImeAdjustment */,
+ false /* fromShelfAdjustment */, null /* windowContainerTransaction */);
+ });
+ }
+ };
+
@Inject
public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
DisplayController displayController,
@@ -231,7 +256,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
PipBoundsHandler pipBoundsHandler,
PipSnapAlgorithm pipSnapAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
- SysUiState sysUiState) {
+ SysUiState sysUiState,
+ ConfigurationController configController) {
mContext = context;
mActivityManager = ActivityManager.getService();
@@ -264,6 +290,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
context.getDisplay().getDisplayInfo(displayInfo);
mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+ configController.addCallback(mOverlayChangedListener);
+
try {
mPipTaskOrganizer.registerOrganizer(WINDOWING_MODE_PINNED);
ActivityManager.StackInfo stackInfo = ActivityTaskManager.getService().getStackInfo(
@@ -327,7 +355,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight);
updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(),
false /* fromRotation */, false /* fromImeAdjustment */,
- true /* fromShelfAdjustment */);
+ true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
}
});
}
@@ -345,17 +373,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
@Override
public void onPipTransitionStarted(ComponentName activity, int direction) {
if (isOutPipDirection(direction)) {
- // On phones, the expansion animation that happens on pip tap before restoring
- // to fullscreen makes it so that the bounds received here are the expanded
- // bounds. We want to restore to the unexpanded bounds when re-entering pip,
- // so we save the bounds before expansion (normal) instead of the current
- // bounds.
- mReentryBounds.set(mTouchHandler.getNormalBounds());
- // Apply the snap fraction of the current bounds to the normal bounds.
- final Rect bounds = mPipTaskOrganizer.getLastReportedBounds();
- float snapFraction = mPipBoundsHandler.getSnapFraction(bounds);
- mPipBoundsHandler.applySnapFraction(mReentryBounds, snapFraction);
- // Save reentry bounds (normal non-expand bounds with current position applied).
+ // Exiting PIP, save the reentry bounds to restore to when re-entering.
+ updateReentryBounds();
mPipBoundsHandler.onSaveReentryBounds(activity, mReentryBounds);
}
// Disable touches while the animation is running
@@ -369,6 +388,23 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
}
}
+ /**
+ * Update the bounds used to save the re-entry size and snap fraction when exiting PIP.
+ */
+ public void updateReentryBounds() {
+ // On phones, the expansion animation that happens on pip tap before restoring
+ // to fullscreen makes it so that the last reported bounds are the expanded
+ // bounds. We want to restore to the unexpanded bounds when re-entering pip,
+ // so we use the bounds before expansion (normal) instead of the reported
+ // bounds.
+ Rect reentryBounds = mTouchHandler.getNormalBounds();
+ // Apply the snap fraction of the current bounds to the normal bounds.
+ final Rect bounds = mPipTaskOrganizer.getLastReportedBounds();
+ float snapFraction = mPipBoundsHandler.getSnapFraction(bounds);
+ mPipBoundsHandler.applySnapFraction(reentryBounds, snapFraction);
+ mReentryBounds.set(reentryBounds);
+ }
+
@Override
public void onPipTransitionFinished(ComponentName activity, int direction) {
onPipTransitionFinishedOrCanceled(direction);
@@ -387,15 +423,16 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
}
private void updateMovementBounds(@Nullable Rect toBounds, boolean fromRotation,
- boolean fromImeAdjustment, boolean fromShelfAdjustment) {
+ boolean fromImeAdjustment, boolean fromShelfAdjustment,
+ WindowContainerTransaction wct) {
// Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler before
// passing to mTouchHandler/mPipTaskOrganizer
final Rect outBounds = new Rect(toBounds);
mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
outBounds, mTmpDisplayInfo);
// mTouchHandler would rely on the bounds populated from mPipTaskOrganizer
- mPipTaskOrganizer.onMovementBoundsChanged(outBounds, fromRotation,
- fromImeAdjustment, fromShelfAdjustment);
+ mPipTaskOrganizer.onMovementBoundsChanged(outBounds, fromRotation, fromImeAdjustment,
+ fromShelfAdjustment, wct);
mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
outBounds, fromImeAdjustment, fromShelfAdjustment,
mTmpDisplayInfo.rotation);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index 849a62add80f..361aafacdf76 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -194,13 +194,13 @@ public class PipMediaController {
private void createMediaActions() {
String pauseDescription = mContext.getString(R.string.pip_pause);
mPauseAction = new RemoteAction(Icon.createWithResource(mContext,
- R.drawable.ic_pause_white), pauseDescription, pauseDescription,
+ R.drawable.pip_ic_pause_white), pauseDescription, pauseDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PAUSE),
FLAG_UPDATE_CURRENT));
String playDescription = mContext.getString(R.string.pip_play);
mPlayAction = new RemoteAction(Icon.createWithResource(mContext,
- R.drawable.ic_play_arrow_white), playDescription, playDescription,
+ R.drawable.pip_ic_play_arrow_white), playDescription, playDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PLAY),
FLAG_UPDATE_CURRENT));
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 204354645e10..d6f3e163ad70 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -76,12 +76,15 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import com.android.systemui.Interpolators;
-import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
+import com.android.wm.shell.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Translucent activity that gets started on top of a task in PIP to allow the user to control it.
*/
@@ -100,10 +103,13 @@ public class PipMenuActivity extends Activity {
public static final int MESSAGE_POINTER_EVENT = 7;
public static final int MESSAGE_MENU_EXPANDED = 8;
public static final int MESSAGE_FADE_OUT_MENU = 9;
+ public static final int MESSAGE_UPDATE_MENU_LAYOUT = 10;
private static final int INITIAL_DISMISS_DELAY = 3500;
private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
private static final long MENU_FADE_DURATION = 125;
+ private static final long MENU_SLOW_FADE_DURATION = 175;
+ private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
@@ -127,8 +133,12 @@ public class PipMenuActivity extends Activity {
private View mSettingsButton;
private View mDismissButton;
private View mResizeHandle;
+ private View mTopEndContainer;
private int mBetweenActionPaddingLand;
+ @Inject
+ PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
+
private AnimatorSet mMenuContainerAnimator;
private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
@@ -162,9 +172,10 @@ public class PipMenuActivity extends Activity {
break;
case MESSAGE_UPDATE_ACTIONS: {
final Bundle data = (Bundle) msg.obj;
- final ParceledListSlice actions = data.getParcelable(EXTRA_ACTIONS);
+ final ParceledListSlice<RemoteAction> actions = data.getParcelable(
+ EXTRA_ACTIONS);
setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null
- ? actions.getList() : Collections.EMPTY_LIST);
+ ? actions.getList() : Collections.emptyList());
break;
}
case MESSAGE_UPDATE_DISMISS_FRACTION: {
@@ -182,6 +193,7 @@ public class PipMenuActivity extends Activity {
break;
}
case MESSAGE_MENU_EXPANDED : {
+ mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY);
mMenuContainerAnimator.start();
break;
}
@@ -189,6 +201,11 @@ public class PipMenuActivity extends Activity {
fadeOutMenu();
break;
}
+ case MESSAGE_UPDATE_MENU_LAYOUT: {
+ final Rect bounds = (Rect) msg.obj;
+ mPipMenuIconsAlgorithm.onBoundsChanged(bounds);
+ break;
+ }
}
}
};
@@ -204,6 +221,9 @@ public class PipMenuActivity extends Activity {
getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
super.onCreate(savedInstanceState);
+
+ SystemUIFactory.getInstance().getRootComponent().inject(this);
+
setContentView(R.layout.pip_menu_activity);
mAccessibilityManager = getSystemService(AccessibilityManager.class);
@@ -213,6 +233,7 @@ public class PipMenuActivity extends Activity {
mViewRoot.setBackground(mBackgroundDrawable);
mMenuContainer = findViewById(R.id.menu_container);
mMenuContainer.setAlpha(0);
+ mTopEndContainer = findViewById(R.id.top_end_container);
mSettingsButton = findViewById(R.id.settings);
mSettingsButton.setAlpha(0);
mSettingsButton.setOnClickListener((v) -> {
@@ -234,6 +255,8 @@ public class PipMenuActivity extends Activity {
mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
R.dimen.pip_between_action_padding_land);
+ mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
+ mResizeHandle, mSettingsButton, mDismissButton);
updateFromIntent(getIntent());
setTitle(R.string.pip_menu_title);
setDisablePreviewScreenshots(true);
@@ -400,7 +423,9 @@ public class PipMenuActivity extends Activity {
mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim);
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
- mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
+ mMenuContainerAnimator.setDuration(menuState == MENU_STATE_CLOSE
+ ? MENU_FADE_DURATION
+ : MENU_SLOW_FADE_DURATION);
if (allowMenuTimeout) {
mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -496,7 +521,7 @@ public class PipMenuActivity extends Activity {
}
notifyActivityCallback(mMessenger);
- ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
+ ParceledListSlice<RemoteAction> actions = intent.getParcelableExtra(EXTRA_ACTIONS);
if (actions != null) {
mActions.clear();
mActions.addAll(actions.getList());
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 31d292fa3fd7..267c5eacd139 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -115,8 +115,8 @@ public class PipMenuActivityController {
private InputConsumerController mInputConsumerController;
private ArrayList<Listener> mListeners = new ArrayList<>();
- private ParceledListSlice mAppActions;
- private ParceledListSlice mMediaActions;
+ private ParceledListSlice<RemoteAction> mAppActions;
+ private ParceledListSlice<RemoteAction> mMediaActions;
private int mMenuState;
// The dismiss fraction update is sent frequently, so use a temporary bundle for the message
@@ -429,7 +429,7 @@ public class PipMenuActivityController {
/**
* Sets the menu actions to the actions provided by the current PiP activity.
*/
- public void setAppActions(ParceledListSlice appActions) {
+ public void setAppActions(ParceledListSlice<RemoteAction> appActions) {
mAppActions = appActions;
updateMenuActions();
}
@@ -437,7 +437,7 @@ public class PipMenuActivityController {
/**
* @return the best set of actions to show in the PiP menu.
*/
- private ParceledListSlice resolveMenuActions() {
+ private ParceledListSlice<RemoteAction> resolveMenuActions() {
if (isValidActions(mAppActions)) {
return mAppActions;
}
@@ -515,7 +515,7 @@ public class PipMenuActivityController {
/**
* Returns whether the set of actions are valid.
*/
- private boolean isValidActions(ParceledListSlice actions) {
+ private static boolean isValidActions(ParceledListSlice<?> actions) {
return actions != null && actions.getList().size() > 0;
}
@@ -574,6 +574,22 @@ public class PipMenuActivityController {
}
}
+ /**
+ * Tell the PIP Menu to recalculate its layout given its current position on the display.
+ */
+ public void updateMenuLayout(Rect bounds) {
+ if (mToActivityMessenger != null) {
+ Message m = Message.obtain();
+ m.what = PipMenuActivity.MESSAGE_UPDATE_MENU_LAYOUT;
+ m.obj = bounds;
+ try {
+ mToActivityMessenger.send(m);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not dispatch touch event", e);
+ }
+ }
+ }
+
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuIconsAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuIconsAlgorithm.java
new file mode 100644
index 000000000000..69a04d8d3e22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuIconsAlgorithm.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip.phone;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import javax.inject.Inject;
+
+/**
+ * Helper class to calculate and place the menu icons on the PIP Menu.
+ */
+public class PipMenuIconsAlgorithm {
+
+ private static final String TAG = "PipMenuIconsAlgorithm";
+
+ private boolean mFinishedLayout = false;
+ protected ViewGroup mViewRoot;
+ protected ViewGroup mTopEndContainer;
+ protected View mDragHandle;
+ protected View mSettingsButton;
+ protected View mDismissButton;
+
+ @Inject
+ public PipMenuIconsAlgorithm(Context context) {
+ }
+
+ /**
+ * Bind the necessary views.
+ */
+ public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle,
+ View settingsButton, View dismissButton) {
+ mViewRoot = viewRoot;
+ mTopEndContainer = topEndContainer;
+ mDragHandle = dragHandle;
+ mSettingsButton = settingsButton;
+ mDismissButton = dismissButton;
+ }
+
+
+ /**
+ * Updates the position of the drag handle based on where the PIP window is on the screen.
+ */
+ public void onBoundsChanged(Rect bounds) {
+ if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
+ || mSettingsButton == null || mDismissButton == null) {
+ Log.e(TAG, "One if the required views is null.");
+ }
+
+ //We only need to calculate the layout once since it does not change.
+ if (!mFinishedLayout) {
+ mTopEndContainer.removeView(mSettingsButton);
+ mViewRoot.addView(mSettingsButton);
+
+ setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
+ setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
+ mFinishedLayout = true;
+ }
+ }
+
+ /**
+ * Set the gravity on the given view.
+ */
+ protected static void setLayoutGravity(View v, int gravity) {
+ if (v.getLayoutParams() instanceof FrameLayout.LayoutParams) {
+ FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v.getLayoutParams();
+ params.gravity = gravity;
+ v.setLayoutParams(params);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 26805050e841..ca3ef2465498 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -26,9 +26,10 @@ import android.os.Debug;
import android.util.Log;
import android.view.Choreographer;
+import androidx.dynamicanimation.animation.AnimationHandler;
+import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler;
import androidx.dynamicanimation.animation.SpringForce;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.util.FloatingContentCoordinator;
@@ -74,9 +75,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** The region that all of PIP must stay within. */
private final Rect mFloatingAllowedArea = new Rect();
- private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider =
- new SfVsyncFrameCallbackProvider();
-
/**
* Temporary bounds used when PIP is being dragged or animated. These bounds are applied to PIP
* using {@link PipTaskOrganizer#scheduleUserResizePip}, so that we can animate shrinking into
@@ -94,8 +92,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** Coordinator instance for resolving conflicts with other floating content. */
private FloatingContentCoordinator mFloatingContentCoordinator;
- /** Callback that re-sizes PIP to the animated bounds. */
- private final Choreographer.FrameCallback mResizePipVsyncCallback;
+ private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+ ThreadLocal.withInitial(() -> {
+ FrameCallbackScheduler scheduler = runnable ->
+ Choreographer.getSfInstance().postFrameCallback(t -> runnable.run());
+ AnimationHandler handler = new AnimationHandler(scheduler);
+ return handler;
+ });
/**
* PhysicsAnimator instance for animating {@link #mTemporaryBounds} using physics animations.
@@ -124,7 +127,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
new PhysicsAnimator.SpringConfig(
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
- private final Consumer<Rect> mUpdateBoundsCallback = mBounds::set;
+ private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
+ mMenuController.updateMenuLayout(newBounds);
+ mBounds.set(newBounds);
+ };
/**
* Whether we're springing to the touch event location (vs. moving it to that position
@@ -171,16 +177,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mSnapAlgorithm = snapAlgorithm;
mFloatingContentCoordinator = floatingContentCoordinator;
mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
+ mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
+ mSfAnimationHandlerThreadLocal.get());
- mResizePipVsyncCallback = l -> {
+ mResizePipUpdateListener = (target, values) -> {
if (!mTemporaryBounds.isEmpty()) {
mPipTaskOrganizer.scheduleUserResizePip(
mBounds, mTemporaryBounds, null);
}
};
-
- mResizePipUpdateListener = (target, values) ->
- mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback);
}
@NonNull
@@ -246,7 +251,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mBounds.set(toBounds);
} else {
mTemporaryBounds.set(toBounds);
- mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds, null);
+ mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds,
+ (Rect newBounds) -> {
+ mMenuController.updateMenuLayout(newBounds);
+ });
}
} else {
// If PIP is 'catching up' after being stuck in the dismiss target, update the animation
@@ -365,6 +373,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
void flingToSnapTarget(
float velocityX, float velocityY,
@Nullable Runnable updateAction, @Nullable Runnable endAction) {
+ // If we're flinging to a snap target now, we're not springing to catch up to the touch
+ // location now.
+ mSpringingToTouch = false;
+
mTemporaryBoundsPhysicsAnimator
.spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig)
.spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig)
@@ -529,8 +541,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
&& !mSpringingToTouch
&& !mMagnetizedPip.getObjectStuckToTarget()) {
mBounds.set(mTemporaryBounds);
- mPipTaskOrganizer.scheduleFinishResizePip(mBounds);
-
+ if (!mDismissalPending) {
+ // do not schedule resize if PiP is dismissing, which may cause app re-open to
+ // mBounds instead of it's normal bounds.
+ mPipTaskOrganizer.scheduleFinishResizePip(mBounds);
+ }
mTemporaryBounds.setEmpty();
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index a4edacecfd91..d884fa956edc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -49,14 +49,15 @@ import android.view.MotionEvent;
import android.view.ViewConfiguration;
import com.android.internal.policy.TaskResizingAlgorithm;
-import com.android.systemui.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.wm.shell.R;
+import java.io.PrintWriter;
import java.util.concurrent.Executor;
-import java.util.function.Supplier;
+import java.util.function.Function;
/**
* Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
@@ -94,7 +95,7 @@ public class PipResizeGestureHandler {
private final Rect mTmpBottomLeftCorner = new Rect();
private final Rect mTmpBottomRightCorner = new Rect();
private final Rect mDisplayBounds = new Rect();
- private final Supplier<Rect> mMovementBoundsSupplier;
+ private final Function<Rect, Rect> mMovementBoundsSupplier;
private final Runnable mUpdateMovementBoundsRunnable;
private int mDelta;
@@ -113,7 +114,7 @@ public class PipResizeGestureHandler {
public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
- PipTaskOrganizer pipTaskOrganizer, Supplier<Rect> movementBoundsSupplier,
+ PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
Runnable updateMovementBoundsRunnable, SysUiState sysUiState) {
mContext = context;
mDisplayId = context.getDisplayId();
@@ -244,10 +245,15 @@ public class PipResizeGestureHandler {
return mTmpRegion.contains(x, y);
}
+ public boolean willStartResizeGesture(MotionEvent ev) {
+ return mEnableUserResize && isInValidSysUiState()
+ && isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY());
+ }
+
private void setCtrlType(int x, int y) {
final Rect currentPipBounds = mMotionHelper.getBounds();
- Rect movementBounds = mMovementBoundsSupplier.get();
+ Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
mDisplayBounds.set(movementBounds.left,
movementBounds.top,
movementBounds.right + currentPipBounds.width(),
@@ -353,6 +359,16 @@ public class PipResizeGestureHandler {
mMinSize.set(minX, minY);
}
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + "mAllowGesture=" + mAllowGesture);
+ pw.println(innerPrefix + "mIsAttached=" + mIsAttached);
+ pw.println(innerPrefix + "mIsEnabled=" + mIsEnabled);
+ pw.println(innerPrefix + "mEnableUserResize=" + mEnableUserResize);
+ pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
+ }
+
class SysUiInputEventReceiver extends BatchedInputEventReceiver {
SysUiInputEventReceiver(InputChannel channel, Looper looper) {
super(channel, looper, Choreographer.getSfInstance());
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 a4d7bad2891f..5434b62e19b8 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -171,7 +171,6 @@ public class PipTouchHandler {
private float mSavedSnapFraction = -1f;
private boolean mSendingHoverAccessibilityEvents;
private boolean mMovementWithinDismiss;
- private boolean mHideMenuAfterShown = false;
private PipAccessibilityInteractionConnection mConnection;
// Touch state
@@ -242,7 +241,8 @@ public class PipTouchHandler {
this::updateMovementBounds, sysUiState);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
- true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()));
+ true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
+ menuController::hideMenu);
Resources res = context.getResources();
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
@@ -272,7 +272,9 @@ public class PipTouchHandler {
mMagnetizedPip.setAnimateStuckToTarget(
(target, velX, velY, flung, after) -> {
- mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
+ if (mEnableDismissDragToEdge) {
+ mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
+ }
return Unit.INSTANCE;
});
mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
@@ -280,7 +282,9 @@ public class PipTouchHandler {
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
// Show the dismiss target, in case the initial touch event occurred within the
// magnetic field radius.
- showDismissTargetMaybe();
+ if (mEnableDismissDragToEdge) {
+ showDismissTargetMaybe();
+ }
}
@Override
@@ -303,8 +307,11 @@ public class PipTouchHandler {
hideDismissTarget();
});
- MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
- PipUtils.getTopPipActivity(mContext, mActivityManager));
+ Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext,
+ mActivityManager);
+ if (topPipActivity.first != null) {
+ MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, topPipActivity);
+ }
}
});
@@ -341,7 +348,7 @@ public class PipTouchHandler {
}
private boolean shouldShowResizeHandle() {
- return !mPipBoundsHandler.hasSaveReentryBounds();
+ return false;
}
public void setTouchGesture(PipTouchGesture gesture) {
@@ -480,6 +487,8 @@ public class PipTouchHandler {
mSnapAlgorithm.getMovementBounds(curBounds, insetBounds,
toMovementBounds, mIsImeShowing ? mImeHeight : 0);
final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
+ // This is to handle landscape fullscreen IMEs, don't apply the extra offset in this
+ // case
final int toBottom = toMovementBounds.bottom < toMovementBounds.top
? toMovementBounds.bottom
: toMovementBounds.bottom - extraOffset;
@@ -490,10 +499,16 @@ public class PipTouchHandler {
mSavedSnapFraction);
}
- if ((Math.min(prevBottom, toBottom) - mBottomOffsetBufferPx) <= curBounds.top
- && curBounds.top <= (Math.max(prevBottom, toBottom)
- + mBottomOffsetBufferPx)) {
- mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top);
+ if (prevBottom < toBottom) {
+ // The movement bounds are expanding
+ if (curBounds.top > prevBottom - mBottomOffsetBufferPx) {
+ mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top);
+ }
+ } else if (prevBottom > toBottom) {
+ // The movement bounds are shrinking
+ if (curBounds.top > toBottom - mBottomOffsetBufferPx) {
+ mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top);
+ }
}
}
}
@@ -633,12 +648,12 @@ public class PipTouchHandler {
}
MotionEvent ev = (MotionEvent) inputEvent;
- if (!mTouchState.isDragging()
- && !mMagnetizedPip.getObjectStuckToTarget()
- && !mMotionHelper.isAnimating()
- && mPipResizeGestureHandler.isWithinTouchRegion(
- (int) ev.getRawX(), (int) ev.getRawY())) {
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN
+ && mPipResizeGestureHandler.willStartResizeGesture(ev)) {
+ // Initialize the touch state for the gesture, but immediately reset to invalidate the
+ // gesture
mTouchState.onTouchEvent(ev);
+ mTouchState.reset();
return true;
}
@@ -697,6 +712,7 @@ public class PipTouchHandler {
// on and changing MotionEvents into HoverEvents.
// Let's not enable menu show/hide for a11y services.
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+ mTouchState.removeHoverExitTimeoutCallback();
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
false /* allowMenuTimeout */, false /* willResizeMenu */,
shouldShowResizeHandle());
@@ -713,8 +729,7 @@ public class PipTouchHandler {
// on and changing MotionEvents into HoverEvents.
// Let's not enable menu show/hide for a11y services.
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
- mHideMenuAfterShown = true;
- mMenuController.hideMenu();
+ mTouchState.scheduleHoverExitTimeoutCallback();
}
if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) {
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
@@ -800,9 +815,6 @@ public class PipTouchHandler {
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
mMovementBounds, mExpandedMovementBounds, callback);
}
- if (mHideMenuAfterShown) {
- mMenuController.hideMenu();
- }
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
// Try and restore the PiP to the closest edge, using the saved snap fraction
// if possible
@@ -840,7 +852,6 @@ public class PipTouchHandler {
}
}
mMenuState = menuState;
- mHideMenuAfterShown = false;
updateMovementBounds();
// If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
// as well, or it can't handle a11y focus and pip menu can't perform any action.
@@ -971,6 +982,8 @@ public class PipTouchHandler {
}
mShouldHideMenuAfterFling = mMenuState == MENU_STATE_NONE;
+ // Reset the touch state on up before the fling settles
+ mTouchState.reset();
mMotionHelper.flingToSnapTarget(vel.x, vel.y,
PipTouchHandler.this::updateDismissFraction /* updateAction */,
this::flingEndAction /* endAction */);
@@ -997,7 +1010,6 @@ public class PipTouchHandler {
}
private void flingEndAction() {
- mTouchState.setAllowTouches(true);
if (mShouldHideMenuAfterFling) {
// If the menu is not visible, then we can still be showing the activity for the
// dismiss overlay, so just finish it after the animation completes
@@ -1020,8 +1032,11 @@ public class PipTouchHandler {
isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0);
}
- private Rect getMovementBounds() {
- return mMovementBounds;
+ private Rect getMovementBounds(Rect curBounds) {
+ Rect movementBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(curBounds, mInsetBounds,
+ movementBounds, mIsImeShowing ? mImeHeight : 0);
+ return movementBounds;
}
/**
@@ -1053,6 +1068,9 @@ public class PipTouchHandler {
pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets);
mTouchState.dump(pw, innerPrefix);
mMotionHelper.dump(pw, innerPrefix);
+ if (mPipResizeGestureHandler != null) {
+ mPipResizeGestureHandler.dump(pw, innerPrefix);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index 132c04d381dd..ecd1128a5680 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -36,10 +36,12 @@ public class PipTouchState {
@VisibleForTesting
static final long DOUBLE_TAP_TIMEOUT = 200;
+ static final long HOVER_EXIT_TIMEOUT = 50;
private final Handler mHandler;
private final ViewConfiguration mViewConfig;
private final Runnable mDoubleTapTimeoutCallback;
+ private final Runnable mHoverExitTimeoutCallback;
private VelocityTracker mVelocityTracker;
private long mDownTouchTime = 0;
@@ -64,10 +66,11 @@ public class PipTouchState {
private int mActivePointerId;
public PipTouchState(ViewConfiguration viewConfig, Handler handler,
- Runnable doubleTapTimeoutCallback) {
+ Runnable doubleTapTimeoutCallback, Runnable hoverExitTimeoutCallback) {
mViewConfig = viewConfig;
mHandler = handler;
mDoubleTapTimeoutCallback = doubleTapTimeoutCallback;
+ mHoverExitTimeoutCallback = hoverExitTimeoutCallback;
}
/**
@@ -197,6 +200,10 @@ public class PipTouchState {
recycleVelocityTracker();
break;
}
+ case MotionEvent.ACTION_BUTTON_PRESS: {
+ removeHoverExitTimeoutCallback();
+ break;
+ }
}
}
@@ -326,6 +333,15 @@ public class PipTouchState {
mHandler.removeCallbacks(mDoubleTapTimeoutCallback);
}
+ void scheduleHoverExitTimeoutCallback() {
+ mHandler.removeCallbacks(mHoverExitTimeoutCallback);
+ mHandler.postDelayed(mHoverExitTimeoutCallback, HOVER_EXIT_TIMEOUT);
+ }
+
+ void removeHoverExitTimeoutCallback() {
+ mHandler.removeCallbacks(mHoverExitTimeoutCallback);
+ }
+
void addMovementToVelocityTracker(MotionEvent event) {
if (mVelocityTracker == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java
index b21cd95626a7..db9bedd2e620 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java
@@ -28,7 +28,7 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
/**
* A view containing PIP controls including fullscreen, close, and media controls.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
index 8efeef1ffa0a..125444d2dfb5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
@@ -22,7 +22,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
index 1fe531b372c6..05bb882ea52e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
@@ -26,8 +26,8 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.wm.shell.R;
import java.util.ArrayList;
import java.util.List;
@@ -216,10 +216,10 @@ public class PipControlsViewController {
} else {
mPlayPauseButtonView.setVisibility(View.VISIBLE);
if (state == PipManager.PLAYBACK_STATE_PLAYING) {
- mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white);
+ mPlayPauseButtonView.setImageResource(R.drawable.pip_ic_pause_white);
mPlayPauseButtonView.setText(R.string.pip_pause);
} else {
- mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white);
+ mPlayPauseButtonView.setImageResource(R.drawable.pip_ic_play_arrow_white);
mPlayPauseButtonView.setText(R.string.pip_play);
}
}
@@ -243,7 +243,7 @@ public class PipControlsViewController {
/**
* Updates the set of activity-defined actions.
*/
- public void setActions(List<RemoteAction> actions) {
+ public void setActions(List<? extends RemoteAction> actions) {
mCustomActions.clear();
mCustomActions.addAll(actions);
updateUserActions();
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 628223630af7..2138f092b790 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -24,6 +24,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
+import android.app.RemoteAction;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -42,7 +43,6 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import android.view.DisplayInfo;
import com.android.systemui.Dependency;
@@ -73,10 +73,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
private static final String TAG = "PipManager";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String SETTINGS_PACKAGE_AND_CLASS_DELIMITER = "/";
-
- private static List<Pair<String, String>> sSettingsPackageAndClassNamePairList;
-
/**
* State when there's no PIP.
*/
@@ -122,10 +118,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
private final Handler mHandler = new Handler();
private List<Listener> mListeners = new ArrayList<>();
private List<MediaListener> mMediaListeners = new ArrayList<>();
- private Rect mCurrentPipBounds;
private Rect mPipBounds;
private Rect mDefaultPipBounds = new Rect();
- private Rect mSettingsPipBounds;
private Rect mMenuModePipBounds;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private boolean mInitialized;
@@ -135,7 +129,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
private MediaController mPipMediaController;
private String[] mLastPackagesResourceGranted;
private PipNotification mPipNotification;
- private ParceledListSlice mCustomActions;
+ private ParceledListSlice<RemoteAction> mCustomActions;
private int mResizeAnimationDuration;
// Used to calculate the movement bounds
@@ -220,7 +214,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
}
@Override
- public void onActionsChanged(ParceledListSlice actions) {
+ public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
mCustomActions = actions;
mHandler.post(() -> {
for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -260,37 +254,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
broadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter,
null /* handler */, UserHandle.ALL);
- if (sSettingsPackageAndClassNamePairList == null) {
- String[] settings = mContext.getResources().getStringArray(
- R.array.tv_pip_settings_class_name);
- sSettingsPackageAndClassNamePairList = new ArrayList<>();
- if (settings != null) {
- for (int i = 0; i < settings.length; i++) {
- Pair<String, String> entry = null;
- String[] packageAndClassName =
- settings[i].split(SETTINGS_PACKAGE_AND_CLASS_DELIMITER);
- switch (packageAndClassName.length) {
- case 1:
- entry = Pair.<String, String>create(packageAndClassName[0], null);
- break;
- case 2:
- if (packageAndClassName[1] != null) {
- entry = Pair.<String, String>create(packageAndClassName[0],
- packageAndClassName[1].startsWith(".")
- ? packageAndClassName[0] + packageAndClassName[1]
- : packageAndClassName[1]);
- }
- break;
- }
- if (entry != null) {
- sSettingsPackageAndClassNamePairList.add(entry);
- } else {
- Log.w(TAG, "Ignoring malformed settings name " + settings[i]);
- }
- }
- }
- }
-
// Initialize the last orientation and apply the current configuration
Configuration initialConfig = mContext.getResources().getConfiguration();
mLastOrientation = initialConfig.orientation;
@@ -318,15 +281,13 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
}
Resources res = mContext.getResources();
- mSettingsPipBounds = Rect.unflattenFromString(res.getString(
- R.string.pip_settings_bounds));
mMenuModePipBounds = Rect.unflattenFromString(res.getString(
R.string.pip_menu_bounds));
// Reset the PIP bounds and apply. PIP bounds can be changed by two reasons.
// 1. Configuration changed due to the language change (RTL <-> RTL)
// 2. SystemUI restarts after the crash
- mPipBounds = isSettingsShown() ? mSettingsPipBounds : mDefaultPipBounds;
+ mPipBounds = mDefaultPipBounds;
resizePinnedStack(getPinnedStackInfo() == null ? STATE_NO_PIP : STATE_PIP);
}
@@ -447,9 +408,10 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
return;
}
mState = state;
+ final Rect newBounds;
switch (mState) {
case STATE_NO_PIP:
- mCurrentPipBounds = null;
+ newBounds = null;
// If the state was already STATE_NO_PIP, then do not resize the stack below as it
// will not exist
if (wasStateNoPip) {
@@ -457,16 +419,15 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
}
break;
case STATE_PIP_MENU:
- mCurrentPipBounds = mMenuModePipBounds;
+ newBounds = mMenuModePipBounds;
break;
case STATE_PIP: // fallthrough
default:
- mCurrentPipBounds = mPipBounds;
+ newBounds = mPipBounds;
break;
}
- if (mCurrentPipBounds != null) {
- mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, mResizeAnimationDuration,
- null);
+ if (newBounds != null) {
+ mPipTaskOrganizer.scheduleAnimateResizePip(newBounds, mResizeAnimationDuration, null);
} else {
mPipTaskOrganizer.exitPip(mResizeAnimationDuration);
}
@@ -627,30 +588,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
return PLAYBACK_STATE_UNAVAILABLE;
}
- private boolean isSettingsShown() {
- List<RunningTaskInfo> runningTasks;
- try {
- runningTasks = mActivityTaskManager.getTasks(1);
- if (runningTasks.isEmpty()) {
- return false;
- }
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to detect top activity", e);
- return false;
- }
- ComponentName topActivity = runningTasks.get(0).topActivity;
- for (Pair<String, String> componentName : sSettingsPackageAndClassNamePairList) {
- String packageName = componentName.first;
- if (topActivity.getPackageName().equals(packageName)) {
- String className = componentName.second;
- if (className == null || topActivity.getClassName().equals(className)) {
- return true;
- }
- }
- }
- return false;
- }
-
private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
@@ -679,9 +616,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
}
}
if (getState() == STATE_PIP) {
- Rect bounds = isSettingsShown() ? mSettingsPipBounds : mDefaultPipBounds;
- if (mPipBounds != bounds) {
- mPipBounds = bounds;
+ if (mPipBounds != mDefaultPipBounds) {
+ mPipBounds = mDefaultPipBounds;
resizePinnedStack(STATE_PIP);
}
}
@@ -703,7 +639,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
stackInfo.taskNames[stackInfo.taskNames.length - 1]);
// Set state to STATE_PIP so we show it when the pinned stack animation ends.
mState = STATE_PIP;
- mCurrentPipBounds = mPipBounds;
mMediaSessionManager.addOnActiveSessionsChangedListener(
mActiveMediaSessionListener, null);
updateMediaController(mMediaSessionManager.getActiveSessions(null));
@@ -716,7 +651,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
@Override
public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
boolean clearedTask, boolean wasVisible) {
- if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
+ if (task.configuration.windowConfiguration.getWindowingMode()
!= WINDOWING_MODE_PINNED) {
return;
}
@@ -764,7 +699,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
/** Invoked when the PIP menu gets shown. */
void onShowPipMenu();
/** Invoked when the PIP menu actions change. */
- void onPipMenuActionsChanged(ParceledListSlice actions);
+ void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions);
/** Invoked when the PIPed activity is about to return back to the fullscreen. */
void onMoveToFullscreen();
/** Invoked when we are above to start resizing the Pip. */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index 158be45e0adb..214088c99eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -19,13 +19,14 @@ package com.android.systemui.pip.tv;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.app.Activity;
+import android.app.RemoteAction;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import android.util.Log;
-import com.android.systemui.R;
import com.android.systemui.pip.tv.dagger.TvPipComponent;
+import com.android.wm.shell.R;
import java.util.Collections;
@@ -149,12 +150,12 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
}
@Override
- public void onPipMenuActionsChanged(ParceledListSlice actions) {
+ public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) {
if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()");
boolean hasCustomActions = actions != null && !actions.getList().isEmpty();
mPipControlsViewController.setActions(
- hasCustomActions ? actions.getList() : Collections.EMPTY_LIST);
+ hasCustomActions ? actions.getList() : Collections.emptyList());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index 30ec29683942..651a4f367f21 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -19,6 +19,7 @@ package com.android.systemui.pip.tv;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.RemoteAction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -35,9 +36,9 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.util.NotificationChannels;
+import com.android.wm.shell.R;
/**
* A notification that informs users that PIP is running and also provides PIP controls.
@@ -89,7 +90,7 @@ public class PipNotification {
}
@Override
- public void onPipMenuActionsChanged(ParceledListSlice actions) {
+ public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) {
// no-op.
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 10b04c0129e4..6abbbbeaa397 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -24,6 +24,7 @@ import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioAttributes;
@@ -376,13 +377,15 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
return;
}
mHighTempWarning = true;
+ final String message = mContext.getString(R.string.high_temp_notif_message);
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.ALERTS)
.setSmallIcon(R.drawable.ic_device_thermostat_24)
.setWhen(0)
.setShowWhen(false)
.setContentTitle(mContext.getString(R.string.high_temp_title))
- .setContentText(mContext.getString(R.string.high_temp_notif_message))
+ .setContentText(message)
+ .setStyle(new Notification.BigTextStyle().bigText(message))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setContentIntent(pendingBroadcast(ACTION_CLICKED_TEMP_WARNING))
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
@@ -402,6 +405,23 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
d.setPositiveButton(com.android.internal.R.string.ok, null);
d.setShowForAllUsers(true);
d.setOnDismissListener(dialog -> mHighTempDialog = null);
+ final String url = mContext.getString(R.string.high_temp_dialog_help_url);
+ if (!url.isEmpty()) {
+ d.setNeutralButton(R.string.high_temp_dialog_help_text,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Intent helpIntent =
+ new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse(url))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Dependency.get(ActivityStarter.class).startActivity(helpIntent,
+ true /* dismissShade */, resultCode -> {
+ mHighTempDialog = null;
+ });
+ }
+ });
+ }
d.show();
mHighTempDialog = d;
}
@@ -420,19 +440,38 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
d.setPositiveButton(com.android.internal.R.string.ok, null);
d.setShowForAllUsers(true);
d.setOnDismissListener(dialog -> mThermalShutdownDialog = null);
+ final String url = mContext.getString(R.string.thermal_shutdown_dialog_help_url);
+ if (!url.isEmpty()) {
+ d.setNeutralButton(R.string.thermal_shutdown_dialog_help_text,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Intent helpIntent =
+ new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse(url))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Dependency.get(ActivityStarter.class).startActivity(helpIntent,
+ true /* dismissShade */, resultCode -> {
+ mThermalShutdownDialog = null;
+ });
+ }
+ });
+ }
d.show();
mThermalShutdownDialog = d;
}
@Override
public void showThermalShutdownWarning() {
+ final String message = mContext.getString(R.string.thermal_shutdown_message);
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.ALERTS)
.setSmallIcon(R.drawable.ic_device_thermostat_24)
.setWhen(0)
.setShowWhen(false)
.setContentTitle(mContext.getString(R.string.thermal_shutdown_title))
- .setContentText(mContext.getString(R.string.thermal_shutdown_message))
+ .setContentText(message)
+ .setStyle(new Notification.BigTextStyle().bigText(message))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setContentIntent(pendingBroadcast(ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING))
.setDeleteIntent(
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 078c540939aa..66804bef8f53 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -225,6 +225,8 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
@VisibleForTesting
final class Receiver extends BroadcastReceiver {
+ private boolean mHasReceivedBattery = false;
+
public void init() {
// Register for Intent broadcasts for...
IntentFilter filter = new IntentFilter();
@@ -234,6 +236,17 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
+ // Force get initial values. Relying on Sticky behavior until API for getting info.
+ if (!mHasReceivedBattery) {
+ // Get initial state
+ Intent intent = mContext.registerReceiver(
+ null,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+ );
+ if (intent != null) {
+ onReceive(mContext, intent);
+ }
+ }
}
@Override
@@ -246,6 +259,7 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
}
});
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+ mHasReceivedBattery = true;
final int oldBatteryLevel = mBatteryLevel;
mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
final int oldBatteryStatus = mBatteryStatus;
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
new file mode 100644
index 000000000000..870e714ee24c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.systemui.R
+
+class OngoingPrivacyChip @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttrs: Int = 0,
+ defStyleRes: Int = 0
+) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
+
+ private val iconMarginExpanded = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_chip_icon_margin_expanded)
+ private val iconMarginCollapsed = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_chip_icon_margin_collapsed)
+ private val iconSize =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
+ private val iconColor = context.resources.getColor(
+ R.color.status_bar_clock_color, context.theme)
+ private val sidePadding =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
+ private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
+ private lateinit var iconsContainer: LinearLayout
+ private lateinit var back: FrameLayout
+ var expanded = false
+ set(value) {
+ if (value != field) {
+ field = value
+ updateView(PrivacyChipBuilder(context, privacyList))
+ }
+ }
+
+ var privacyList = emptyList<PrivacyItem>()
+ set(value) {
+ field = value
+ updateView(PrivacyChipBuilder(context, field))
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ back = requireViewById(R.id.background)
+ iconsContainer = requireViewById(R.id.icons_container)
+ }
+
+ // Should only be called if the builder icons or app changed
+ private fun updateView(builder: PrivacyChipBuilder) {
+ back.background = if (expanded) backgroundDrawable else null
+ val padding = if (expanded) sidePadding else 0
+ back.setPaddingRelative(padding, 0, padding, 0)
+ fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) {
+ iconsContainer.removeAllViews()
+ chipBuilder.generateIcons().forEachIndexed { i, it ->
+ it.mutate()
+ it.setTint(iconColor)
+ val image = ImageView(context).apply {
+ setImageDrawable(it)
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
+ iconsContainer.addView(image, iconSize, iconSize)
+ if (i != 0) {
+ val lp = image.layoutParams as MarginLayoutParams
+ lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed
+ image.layoutParams = lp
+ }
+ }
+ }
+
+ if (!privacyList.isEmpty()) {
+ generateContentDescription(builder)
+ setIcons(builder, iconsContainer)
+ val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams
+ lp.gravity = Gravity.CENTER_VERTICAL or
+ (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END)
+ iconsContainer.layoutParams = lp
+ } else {
+ iconsContainer.removeAllViews()
+ }
+ requestLayout()
+ }
+
+ private fun generateContentDescription(builder: PrivacyChipBuilder) {
+ val typesText = builder.joinTypes()
+ contentDescription = context.getString(
+ R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
new file mode 100644
index 000000000000..1d2e74703b42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import com.android.systemui.R
+
+class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
+
+ val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
+ val types: List<PrivacyType>
+ private val separator = context.getString(R.string.ongoing_privacy_dialog_separator)
+ private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator)
+
+ init {
+ appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
+ .toList()
+ .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
+ { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+ types = itemsList.map { it.privacyType }.distinct().sorted()
+ }
+
+ fun generateIcons() = types.map { it.getIcon(context) }
+
+ private fun <T> List<T>.joinWithAnd(): StringBuilder {
+ return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply {
+ append(lastSeparator)
+ append(this@joinWithAnd.last())
+ }
+ }
+
+ fun joinTypes(): String {
+ return when (types.size) {
+ 0 -> ""
+ 1 -> types[0].getName(context)
+ else -> types.map { it.getName(context) }.joinWithAnd().toString()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
new file mode 100644
index 000000000000..1f24fde1377e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class PrivacyChipEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Privacy chip is viewed by the user. Logged at most once per time QS is visible")
+ ONGOING_INDICATORS_CHIP_VIEW(601),
+
+ @UiEvent(doc = "Privacy chip is clicked")
+ ONGOING_INDICATORS_CHIP_CLICK(602);
+
+ override fun getId() = _id
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
new file mode 100644
index 000000000000..3da1363f2a56
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import com.android.systemui.R
+
+typealias Privacy = PrivacyType
+
+enum class PrivacyType(val nameId: Int, val iconId: Int) {
+ // This is uses the icons used by the corresponding permission groups in the AndroidManifest
+ TYPE_CAMERA(R.string.privacy_type_camera,
+ com.android.internal.R.drawable.perm_group_camera),
+ TYPE_MICROPHONE(R.string.privacy_type_microphone,
+ com.android.internal.R.drawable.perm_group_microphone),
+ TYPE_LOCATION(R.string.privacy_type_location,
+ com.android.internal.R.drawable.perm_group_location);
+
+ fun getName(context: Context) = context.resources.getString(nameId)
+
+ fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
+}
+
+data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication)
+
+data class PrivacyApplication(val packageName: String, val uid: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
new file mode 100644
index 000000000000..d5a14f7bef2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.DeviceConfig
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.Dumpable
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.concurrency.DelayableExecutor
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class PrivacyItemController @Inject constructor(
+ context: Context,
+ private val appOpsController: AppOpsController,
+ @Main uiExecutor: DelayableExecutor,
+ @Background private val bgExecutor: Executor,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val deviceConfigProxy: DeviceConfigProxy,
+ private val userManager: UserManager,
+ dumpManager: DumpManager
+) : Dumpable {
+
+ @VisibleForTesting
+ internal companion object {
+ val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_RECORD_AUDIO,
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_FINE_LOCATION)
+ val intentFilter = IntentFilter().apply {
+ addAction(Intent.ACTION_USER_SWITCHED)
+ addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+ }
+ const val TAG = "PrivacyItemController"
+ }
+
+ @VisibleForTesting
+ internal var privacyList = emptyList<PrivacyItem>()
+ @Synchronized get() = field.toList() // Returns a shallow copy of the list
+ @Synchronized set
+
+ private fun isPermissionsHubEnabled(): Boolean {
+ return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+ }
+
+ private var currentUserIds = emptyList<Int>()
+ private var listening = false
+ private val callbacks = mutableListOf<WeakReference<Callback>>()
+ private val internalUiExecutor = MyExecutor(uiExecutor)
+
+ private val notifyChanges = Runnable {
+ val list = privacyList
+ callbacks.forEach { it.get()?.onPrivacyItemsChanged(list) }
+ }
+
+ private val updateListAndNotifyChanges = Runnable {
+ updatePrivacyList()
+ uiExecutor.execute(notifyChanges)
+ }
+
+ var indicatorsAvailable = isPermissionsHubEnabled()
+ private set
+ @VisibleForTesting
+ internal val devicePropertiesChangedListener =
+ object : DeviceConfig.OnPropertiesChangedListener {
+ override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+ if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
+ properties.getKeyset().contains(
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
+ val flag = properties.getBoolean(
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+ if (indicatorsAvailable != flag) {
+ // This is happening already in the UI executor, so we can iterate in the
+ indicatorsAvailable = flag
+ callbacks.forEach { it.get()?.onFlagChanged(flag) }
+ }
+
+ internalUiExecutor.updateListeningState()
+ }
+ }
+ }
+
+ private val cb = object : AppOpsController.Callback {
+ override fun onActiveStateChanged(
+ code: Int,
+ uid: Int,
+ packageName: String,
+ active: Boolean
+ ) {
+ val userId = UserHandle.getUserId(uid)
+ if (userId in currentUserIds) {
+ update(false)
+ }
+ }
+ }
+
+ @VisibleForTesting
+ internal var userSwitcherReceiver = Receiver()
+ set(value) {
+ unregisterReceiver()
+ field = value
+ if (listening) registerReceiver()
+ }
+
+ init {
+ deviceConfigProxy.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ uiExecutor,
+ devicePropertiesChangedListener)
+ dumpManager.registerDumpable(TAG, this)
+ }
+
+ private fun unregisterReceiver() {
+ broadcastDispatcher.unregisterReceiver(userSwitcherReceiver)
+ }
+
+ private fun registerReceiver() {
+ broadcastDispatcher.registerReceiver(userSwitcherReceiver, intentFilter,
+ null /* handler */, UserHandle.ALL)
+ }
+
+ private fun update(updateUsers: Boolean) {
+ bgExecutor.execute {
+ if (updateUsers) {
+ val currentUser = ActivityManager.getCurrentUser()
+ currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ }
+ updateListAndNotifyChanges.run()
+ }
+ }
+
+ /**
+ * Updates listening status based on whether there are callbacks and the indicators are enabled
+ *
+ * This is only called from private (add/remove)Callback and from the config listener, all in
+ * main thread.
+ */
+ private fun setListeningState() {
+ val listen = !callbacks.isEmpty() and indicatorsAvailable
+ if (listening == listen) return
+ listening = listen
+ if (listening) {
+ appOpsController.addCallback(OPS, cb)
+ registerReceiver()
+ update(true)
+ } else {
+ appOpsController.removeCallback(OPS, cb)
+ unregisterReceiver()
+ // Make sure that we remove all indicators and notify listeners if we are not
+ // listening anymore due to indicators being disabled
+ update(false)
+ }
+ }
+
+ private fun addCallback(callback: WeakReference<Callback>) {
+ callbacks.add(callback)
+ if (callbacks.isNotEmpty() && !listening) {
+ internalUiExecutor.updateListeningState()
+ }
+ // Notify this callback if we didn't set to listening
+ else if (listening) {
+ internalUiExecutor.execute(NotifyChangesToCallback(callback.get(), privacyList))
+ }
+ }
+
+ private fun removeCallback(callback: WeakReference<Callback>) {
+ // Removes also if the callback is null
+ callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
+ if (callbacks.isEmpty()) {
+ internalUiExecutor.updateListeningState()
+ }
+ }
+
+ fun addCallback(callback: Callback) {
+ addCallback(WeakReference(callback))
+ }
+
+ fun removeCallback(callback: Callback) {
+ removeCallback(WeakReference(callback))
+ }
+
+ private fun updatePrivacyList() {
+ if (!listening) {
+ privacyList = emptyList()
+ return
+ }
+ val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
+ .mapNotNull { toPrivacyItem(it) }.distinct()
+ privacyList = list
+ }
+
+ private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
+ val type: PrivacyType = when (appOpItem.code) {
+ AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
+ AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
+ AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
+ AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
+ else -> return null
+ }
+ val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
+ return PrivacyItem(type, app)
+ }
+
+ interface Callback {
+ fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
+ @JvmDefault
+ fun onFlagChanged(flag: Boolean) {}
+ }
+
+ internal inner class Receiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intentFilter.hasAction(intent.action)) {
+ update(true)
+ }
+ }
+ }
+
+ private class NotifyChangesToCallback(
+ private val callback: Callback?,
+ private val list: List<PrivacyItem>
+ ) : Runnable {
+ override fun run() {
+ callback?.onPrivacyItemsChanged(list)
+ }
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("PrivacyItemController state:")
+ pw.println(" Listening: $listening")
+ pw.println(" Current user ids: $currentUserIds")
+ pw.println(" Privacy Items:")
+ privacyList.forEach {
+ pw.print(" ")
+ pw.println(it.toString())
+ }
+ pw.println(" Callbacks:")
+ callbacks.forEach {
+ it.get()?.let {
+ pw.print(" ")
+ pw.println(it.toString())
+ }
+ }
+ }
+
+ private inner class MyExecutor(
+ private val delegate: DelayableExecutor
+ ) : Executor {
+
+ private var listeningCanceller: Runnable? = null
+
+ override fun execute(command: Runnable) {
+ delegate.execute(command)
+ }
+
+ fun updateListeningState() {
+ listeningCanceller?.run()
+ listeningCanceller = delegate.executeDelayed({ setListeningState() }, 0L)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 2c76d70fb3cc..f8655cc44d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -10,10 +10,18 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import java.util.ArrayList;
+/**
+ * Page indicator for using with pageable layouts
+ *
+ * Supports {@code android.R.attr.tint}. If missing, it will use the current accent color.
+ */
public class PageIndicator extends ViewGroup {
private static final String TAG = "PageIndicator";
@@ -31,12 +39,22 @@ public class PageIndicator extends ViewGroup {
private final int mPageIndicatorWidth;
private final int mPageIndicatorHeight;
private final int mPageDotWidth;
+ private @NonNull ColorStateList mTint;
private int mPosition = -1;
private boolean mAnimating;
public PageIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ TypedArray array = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.tint});
+ if (array.hasValue(0)) {
+ mTint = array.getColorStateList(0);
+ } else {
+ mTint = Utils.getColorAccent(context);
+ }
+ array.recycle();
+
mPageIndicatorWidth =
(int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_width);
mPageIndicatorHeight =
@@ -45,15 +63,6 @@ public class PageIndicator extends ViewGroup {
}
public void setNumPages(int numPages) {
- TypedArray array = getContext().obtainStyledAttributes(
- new int[]{android.R.attr.colorControlActivated});
- int color = array.getColor(0, 0);
- array.recycle();
- setNumPages(numPages, color);
- }
-
- /** Overload of setNumPages that allows the indicator color to be specified.*/
- public void setNumPages(int numPages, int color) {
setVisibility(numPages > 1 ? View.VISIBLE : View.GONE);
if (numPages == getChildCount()) {
return;
@@ -67,13 +76,41 @@ public class PageIndicator extends ViewGroup {
while (numPages > getChildCount()) {
ImageView v = new ImageView(mContext);
v.setImageResource(R.drawable.minor_a_b);
- v.setImageTintList(ColorStateList.valueOf(color));
+ v.setImageTintList(mTint);
addView(v, new LayoutParams(mPageIndicatorWidth, mPageIndicatorHeight));
}
// Refresh state.
setIndex(mPosition >> 1);
}
+ /**
+ * @return the current tint list for this view.
+ */
+ @NonNull
+ public ColorStateList getTintList() {
+ return mTint;
+ }
+
+ /**
+ * Set the color for this view.
+ * <br>
+ * Calling this will change the color of the current view and any new dots that are added to it.
+ * @param color the new color
+ */
+ public void setTintList(@NonNull ColorStateList color) {
+ if (color.equals(mTint)) {
+ return;
+ }
+ mTint = color;
+ final int N = getChildCount();
+ for (int i = 0; i < N; i++) {
+ View v = getChildAt(i);
+ if (v instanceof ImageView) {
+ ((ImageView) v).setImageTintList(mTint);
+ }
+ }
+ }
+
public void setLocation(float location) {
int index = (int) location;
setContentDescription(getContext().getString(R.string.accessibility_quick_settings_page,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 3eed8ad89075..560998b5d1d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -508,7 +508,6 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
: position == 0);
}
-
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index e66b33c660d6..9dcc924f161e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -64,6 +64,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
private TouchAnimator mTranslationYAnimator;
private TouchAnimator mNonfirstPageAnimator;
private TouchAnimator mNonfirstPageDelayedAnimator;
+ // This animates fading of SecurityFooter and media divider
+ private TouchAnimator mAllPagesDelayedAnimator;
private TouchAnimator mBrightnessAnimator;
private boolean mNeedsAnimatorUpdate = false;
@@ -296,19 +298,24 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
Builder builder = new Builder()
.setStartDelay(EXPANDED_TILE_DELAY)
.addFloat(tileLayout, "alpha", 0, 1);
+ mFirstPageDelayedAnimator = builder.build();
+
+ // Fade in the security footer and the divider as we reach the final position
+ builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
if (mQsPanel.getSecurityFooter() != null) {
builder.addFloat(mQsPanel.getSecurityFooter().getView(), "alpha", 0, 1);
}
if (mQsPanel.getDivider() != null) {
builder.addFloat(mQsPanel.getDivider(), "alpha", 0, 1);
}
- mFirstPageDelayedAnimator = builder.build();
+ mAllPagesDelayedAnimator = builder.build();
if (mQsPanel.getSecurityFooter() != null) {
mAllViews.add(mQsPanel.getSecurityFooter().getView());
}
if (mQsPanel.getDivider() != null) {
mAllViews.add(mQsPanel.getDivider());
}
+
float px = 0;
float py = 1;
if (tiles.size() <= 3) {
@@ -388,6 +395,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
mNonfirstPageAnimator.setPosition(position);
mNonfirstPageDelayedAnimator.setPosition(position);
}
+ if (mAllowFancy) {
+ mAllPagesDelayedAnimator.setPosition(position);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 6b12e478f627..eba4465018ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -96,6 +96,11 @@ public class QSContainerImpl extends FrameLayout {
mAnimateBottomOnNextLayout = true;
}
});
+ mQSPanel.setMediaVisibilityChangedListener((visible) -> {
+ if (mQSPanel.isShown()) {
+ mAnimateBottomOnNextLayout = true;
+ }
+ });
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index c454048d0649..daf8ca324c74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -104,4 +104,13 @@ public class QSDetailClipper {
public void showBackground() {
mBackground.showSecondLayer();
}
+
+ /**
+ * Cancels the animator if it's running.
+ */
+ public void cancelAnimator() {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index c4bb4e86e41e..6e4ab9a3323a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -78,6 +78,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private SettingsButton mSettingsButton;
protected View mSettingsContainer;
private PageIndicator mPageIndicator;
+ private TextView mBuildText;
+ private boolean mShouldShowBuildText;
private boolean mQsDisabled;
private QSPanel mQsPanel;
@@ -147,6 +149,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
mActionsContainer = findViewById(R.id.qs_footer_actions_container);
mEditContainer = findViewById(R.id.qs_footer_actions_edit_container);
+ mBuildText = findViewById(R.id.build);
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
@@ -162,16 +165,19 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
}
private void setBuildText() {
- TextView v = findViewById(R.id.build);
- if (v == null) return;
+ if (mBuildText == null) return;
if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
- v.setText(mContext.getString(
+ mBuildText.setText(mContext.getString(
com.android.internal.R.string.bugreport_status,
Build.VERSION.RELEASE_OR_CODENAME,
Build.ID));
- v.setVisibility(View.VISIBLE);
+ // Set as selected for marquee before its made visible, then it won't be announced when
+ // it's made visible.
+ mBuildText.setSelected(true);
+ mShouldShowBuildText = true;
} else {
- v.setVisibility(View.GONE);
+ mShouldShowBuildText = false;
+ mBuildText.setSelected(false);
}
}
@@ -321,6 +327,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.INVISIBLE);
mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
+
+ mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.GONE);
}
private boolean showUserSwitcher() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 1e8c4d86da36..290ab8594fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -20,7 +20,6 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.external.TileServices;
-import com.android.systemui.qs.logging.QSLogger;
import java.util.Collection;
@@ -31,7 +30,6 @@ public interface QSHost {
void openPanels();
Context getContext();
Context getUserContext();
- QSLogger getQSLogger();
UiEventLogger getUiEventLogger();
Collection<QSTile> getTiles();
void addCallback(Callback callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index c8a34f010ae4..ae925d1d6ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -25,6 +25,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.PointF;
import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Handler;
@@ -60,6 +61,7 @@ import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.animation.DisappearParameters;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -246,11 +248,40 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
protected void initMediaHostState() {
mMediaHost.setExpansion(1.0f);
mMediaHost.setShowsOnlyActiveMedia(false);
- // Reveal player with some parallax (1.0f would also work)
- mMediaHost.setGonePivot(0.0f, 0.8f);
+ updateMediaDisappearParameters();
mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
}
+ /**
+ * Update the way the media disappears based on if we're using the horizontal layout
+ */
+ private void updateMediaDisappearParameters() {
+ if (!mUsingMediaPlayer) {
+ return;
+ }
+ DisappearParameters parameters = mMediaHost.getDisappearParameters();
+ if (mUsingHorizontalLayout) {
+ // Only height remaining
+ parameters.getDisappearSize().set(0.0f, 0.4f);
+ // Disappearing on the right side on the bottom
+ parameters.getGonePivot().set(1.0f, 1.0f);
+ // translating a bit horizontal
+ parameters.getContentTranslationFraction().set(0.25f, 1.0f);
+ parameters.setDisappearEnd(0.6f);
+ } else {
+ // Only width remaining
+ parameters.getDisappearSize().set(1.0f, 0.0f);
+ // Disappearing on the bottom
+ parameters.getGonePivot().set(0.0f, 1.0f);
+ // translating a bit vertical
+ parameters.getContentTranslationFraction().set(0.0f, 1.05f);
+ parameters.setDisappearEnd(0.95f);
+ }
+ parameters.setFadeStartPosition(0.95f);
+ parameters.setDisappearStart(0.0f);
+ mMediaHost.setDisappearParameters(parameters);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mTileLayout instanceof PagedTileLayout) {
@@ -542,6 +573,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
updateTileLayoutMargins();
updateFooterMargin();
updateDividerMargin();
+ updateMediaDisappearParameters();
updateMediaHostContentMargins();
updateHorizontalLinearLayoutMargins();
updatePadding();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 51eca67e02f9..afc5be4e6c2f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -265,9 +265,13 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic
private void createDialog() {
final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
+ boolean isProfileOwnerOfOrganizationOwnedDevice =
+ mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
final CharSequence deviceOwnerOrganization =
mSecurityController.getDeviceOwnerOrganizationName();
+ final CharSequence workProfileOrganizationName =
+ mSecurityController.getWorkProfileOrganizationName();
final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
@@ -284,7 +288,8 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic
// device management section
CharSequence managementMessage = getManagementMessage(isDeviceManaged,
- deviceOwnerOrganization);
+ deviceOwnerOrganization, isProfileOwnerOfOrganizationOwnedDevice,
+ workProfileOrganizationName);
if (managementMessage == null) {
dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.GONE);
} else {
@@ -292,7 +297,11 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic
TextView deviceManagementWarning =
(TextView) dialogView.findViewById(R.id.device_management_warning);
deviceManagementWarning.setText(managementMessage);
- mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
+ // Don't show the policies button for profile owner of org owned device, because there
+ // is no policies settings screen for it
+ if (!isProfileOwnerOfOrganizationOwnedDevice) {
+ mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
+ }
}
// ca certificate section
@@ -382,11 +391,18 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic
}
protected CharSequence getManagementMessage(boolean isDeviceManaged,
- CharSequence organizationName) {
- if (!isDeviceManaged) return null;
- if (organizationName != null)
+ CharSequence organizationName, boolean isProfileOwnerOfOrganizationOwnedDevice,
+ CharSequence workProfileOrganizationName) {
+ if (!isDeviceManaged && !isProfileOwnerOfOrganizationOwnedDevice) {
+ return null;
+ }
+ if (isDeviceManaged && organizationName != null) {
return mContext.getString(
R.string.monitoring_description_named_management, organizationName);
+ } else if (isProfileOwnerOfOrganizationOwnedDevice && workProfileOrganizationName != null) {
+ return mContext.getString(
+ R.string.monitoring_description_named_management, workProfileOrganizationName);
+ }
return mContext.getString(R.string.monitoring_description_management);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 1f3967c03a41..3b16a4ec1c38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -179,10 +179,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
onTuningChanged(TILES_SETTING, value);
}
- public QSLogger getQSLogger() {
- return mQSLogger;
- }
-
@Override
public UiEventLogger getUiEventLogger() {
return mUiEventLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index b5afe771926c..2dc82dd853d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -31,6 +31,7 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.media.AudioManager;
import android.os.Handler;
+import android.os.Looper;
import android.provider.AlarmClock;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
@@ -44,8 +45,11 @@ import android.view.DisplayCutout;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
+import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.Space;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -54,6 +58,7 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
+import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
@@ -62,6 +67,10 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.privacy.OngoingPrivacyChip;
+import com.android.systemui.privacy.PrivacyChipEvent;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.qs.carrier.QSCarrierGroup;
import com.android.systemui.statusbar.CommandQueue;
@@ -100,7 +109,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
- private final Handler mHandler = new Handler();
private final NextAlarmController mAlarmController;
private final ZenModeController mZenController;
private final StatusBarIconController mStatusBarIconController;
@@ -139,13 +147,19 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private View mRingerContainer;
private Clock mClockView;
private DateView mDateView;
+ private OngoingPrivacyChip mPrivacyChip;
+ private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
private RingerModeTracker mRingerModeTracker;
+ private boolean mPermissionsHubEnabled;
+ private PrivacyItemController mPrivacyItemController;
+ private final UiEventLogger mUiEventLogger;
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
private boolean mHasTopCutout = false;
+ private int mStatusBarPaddingTop = 0;
private int mRoundedCornerPadding = 0;
private int mContentMarginStart;
private int mContentMarginEnd;
@@ -154,22 +168,43 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private int mCutOutPaddingRight;
private float mExpandedHeaderAlpha = 1.0f;
private float mKeyguardExpansionFraction;
+ private boolean mPrivacyChipLogged = false;
+
+ private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
+ @Override
+ public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+ mPrivacyChip.setPrivacyList(privacyItems);
+ setChipVisibility(!privacyItems.isEmpty());
+ }
+
+ @Override
+ public void onFlagChanged(boolean flag) {
+ if (mPermissionsHubEnabled != flag) {
+ StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
+ iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+ setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+ }
+ }
+ };
@Inject
public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
NextAlarmController nextAlarmController, ZenModeController zenModeController,
StatusBarIconController statusBarIconController,
- ActivityStarter activityStarter,
- CommandQueue commandQueue, RingerModeTracker ringerModeTracker) {
+ ActivityStarter activityStarter, PrivacyItemController privacyItemController,
+ CommandQueue commandQueue, RingerModeTracker ringerModeTracker,
+ UiEventLogger uiEventLogger) {
super(context, attrs);
mAlarmController = nextAlarmController;
mZenController = zenModeController;
mStatusBarIconController = statusBarIconController;
mActivityStarter = activityStarter;
+ mPrivacyItemController = privacyItemController;
mDualToneHandler = new DualToneHandler(
new ContextThemeWrapper(context, R.style.QSHeaderTheme));
mCommandQueue = commandQueue;
mRingerModeTracker = ringerModeTracker;
+ mUiEventLogger = uiEventLogger;
}
@Override
@@ -196,8 +231,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mRingerModeTextView = findViewById(R.id.ringer_mode_text);
mRingerContainer = findViewById(R.id.ringer_container);
mRingerContainer.setOnClickListener(this::onClick);
+ mPrivacyChip = findViewById(R.id.privacy_chip);
+ mPrivacyChip.setOnClickListener(this::onClick);
mCarrierGroup = findViewById(R.id.carrier_group);
+
updateResources();
Rect tintArea = new Rect(0, 0, 0, 0);
@@ -217,6 +255,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mClockView = findViewById(R.id.clock);
mClockView.setOnClickListener(this);
mDateView = findViewById(R.id.date);
+ mSpace = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -227,6 +266,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
mRingerModeTextView.setSelected(true);
mNextAlarmTextView.setSelected(true);
+
+ mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
}
public QuickQSPanel getHeaderQsPanel() {
@@ -239,6 +280,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements
com.android.internal.R.string.status_bar_camera));
ignored.add(mContext.getResources().getString(
com.android.internal.R.string.status_bar_microphone));
+ if (mPermissionsHubEnabled) {
+ ignored.add(mContext.getResources().getString(
+ com.android.internal.R.string.status_bar_location));
+ }
return ignored;
}
@@ -254,6 +299,20 @@ public class QuickStatusBarHeader extends RelativeLayout implements
}
}
+ private void setChipVisibility(boolean chipVisible) {
+ if (chipVisible && mPermissionsHubEnabled) {
+ mPrivacyChip.setVisibility(View.VISIBLE);
+ // Makes sure that the chip is logged as viewed at most once each time QS is opened
+ // mListening makes sure that the callback didn't return after the user closed QS
+ if (!mPrivacyChipLogged && mListening) {
+ mPrivacyChipLogged = true;
+ mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
+ }
+ } else {
+ mPrivacyChip.setVisibility(View.GONE);
+ }
+ }
+
private boolean updateRingerStatus() {
boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
CharSequence originalRingerText = mRingerModeTextView.getText();
@@ -339,6 +398,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mRoundedCornerPadding = resources.getDimensionPixelSize(
R.dimen.rounded_corner_content_padding);
+ mStatusBarPaddingTop = resources.getDimensionPixelSize(R.dimen.status_bar_padding_top);
// Update height for a few views, especially due to landscape mode restricting space.
mHeaderTextContainerView.getLayoutParams().height =
@@ -360,6 +420,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
updateStatusIconAlphaAnimator();
updateHeaderTextContainerAlphaAnimator();
+ updatePrivacyChipAlphaAnimator();
}
private void updateStatusIconAlphaAnimator() {
@@ -374,6 +435,12 @@ public class QuickStatusBarHeader extends RelativeLayout implements
.build();
}
+ private void updatePrivacyChipAlphaAnimator() {
+ mPrivacyChipAlphaAnimator = new TouchAnimator.Builder()
+ .addFloat(mPrivacyChip, "alpha", 1, 0, 1)
+ .build();
+ }
+
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
@@ -412,6 +479,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mHeaderTextContainerView.setVisibility(INVISIBLE);
}
}
+ if (mPrivacyChipAlphaAnimator != null) {
+ mPrivacyChip.setExpanded(expansionFraction > 0.5);
+ mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
+ }
if (expansionFraction < 1 && expansionFraction > 0.99) {
if (mHeaderQsPanel.switchTileLayout()) {
updateResources();
@@ -450,6 +521,31 @@ public class QuickStatusBarHeader extends RelativeLayout implements
Pair<Integer, Integer> padding =
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, -1);
+ if (padding == null) {
+ mSystemIconsView.setPaddingRelative(
+ getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
+ getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
+ } else {
+ mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
+
+ }
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
+ boolean cornerCutout = cornerCutoutPadding != null
+ && (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
+ if (cutout != null) {
+ Rect topCutout = cutout.getBoundingRectTop();
+ if (topCutout.isEmpty() || cornerCutout) {
+ mHasTopCutout = false;
+ lp.width = 0;
+ mSpace.setVisibility(View.GONE);
+ } else {
+ mHasTopCutout = true;
+ lp.width = topCutout.width();
+ mSpace.setVisibility(View.VISIBLE);
+ }
+ }
+ mSpace.setLayoutParams(lp);
+ setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
mCutOutPaddingLeft = padding.first;
mCutOutPaddingRight = padding.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
@@ -460,6 +556,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private void updateClockPadding() {
int clockPaddingLeft = 0;
int clockPaddingRight = 0;
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+ int leftMargin = lp.leftMargin;
+ int rightMargin = lp.rightMargin;
+
// The clock might collide with cutouts, let's shift it out of the way.
// We only do that if the inset is bigger than our own padding, since it's nicer to
// align with
@@ -467,16 +568,19 @@ public class QuickStatusBarHeader extends RelativeLayout implements
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding);
int contentMarginLeft = isLayoutRtl() ? mContentMarginEnd : mContentMarginStart;
- clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft, 0);
+ clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
}
if (mCutOutPaddingRight > 0) {
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding);
int contentMarginRight = isLayoutRtl() ? mContentMarginStart : mContentMarginEnd;
- clockPaddingRight = Math.max(cutoutPadding - contentMarginRight, 0);
+ clockPaddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
}
- mSystemIconsView.setPadding(clockPaddingLeft, mWaterfallTopInset, clockPaddingRight, 0);
+ mSystemIconsView.setPadding(clockPaddingLeft,
+ mWaterfallTopInset + mStatusBarPaddingTop,
+ clockPaddingRight,
+ 0);
}
@Override
@@ -502,10 +606,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mZenController.addCallback(this);
mAlarmController.addCallback(this);
mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
+ // Get the most up to date info
+ mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+ mPrivacyItemController.addCallback(mPICCallback);
} else {
mZenController.removeCallback(this);
mAlarmController.removeCallback(this);
mLifecycle.setCurrentState(Lifecycle.State.CREATED);
+ mPrivacyItemController.removeCallback(mPICCallback);
+ mPrivacyChipLogged = false;
}
}
@@ -523,6 +632,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
}
+ } else if (v == mPrivacyChip) {
+ // If the privacy chip is visible, it means there were some indicators
+ Handler mUiHandler = new Handler(Looper.getMainLooper());
+ mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
+ mUiHandler.post(() -> {
+ mActivityStarter.postStartActivityDismissingKeyguard(
+ new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
+ mHost.collapsePanels();
+ });
} else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
Settings.ACTION_SOUND_SETTINGS), 0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 3e2f9dec5807..e5ed88c10a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -208,6 +208,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
public void showImmediately() {
if (!isShown) {
setVisibility(VISIBLE);
+ mClipper.cancelAnimator();
mClipper.showBackground();
isShown = true;
setTileSpecs();
@@ -230,6 +231,10 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED);
isShown = false;
mToolbar.dismissPopupMenus();
+ mClipper.cancelAnimator();
+ // Make sure we're not opening (because we're closing). Nobody can think we are
+ // customizing after the next two lines.
+ mOpening = false;
setCustomizing(false);
save();
if (animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 30e0a766de37..19c7b6cefc5d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -29,7 +29,9 @@ import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.provider.Settings;
import android.service.quicksettings.IQSTileService;
@@ -42,16 +44,27 @@ import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import android.widget.Switch;
+import androidx.annotation.NonNull;
+
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.State;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import java.util.Objects;
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
public class CustomTile extends QSTileImpl<State> implements TileChangeListener {
public static final String PREFIX = "custom(";
@@ -79,8 +92,19 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
private boolean mIsTokenGranted;
private boolean mIsShowingDialog;
- private CustomTile(QSHost host, String action, Context userContext) {
- super(host);
+ private CustomTile(
+ QSHost host,
+ Looper backgroundLooper,
+ Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ String action,
+ Context userContext
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
mTile = new Tile();
@@ -397,7 +421,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
return ComponentName.unflattenFromString(action);
}
- public static CustomTile create(QSHost host, String spec, Context userContext) {
+ private static String getAction(String spec) {
if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
throw new IllegalArgumentException("Bad custom tile spec: " + spec);
}
@@ -405,6 +429,81 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
if (action.isEmpty()) {
throw new IllegalArgumentException("Empty custom tile spec action");
}
- return new CustomTile(host, action, userContext);
+ return action;
+ }
+
+ /**
+ * Create a {@link CustomTile} for a given spec and user.
+ *
+ * @param builder including injected common dependencies.
+ * @param spec as provided by {@link CustomTile#toSpec}
+ * @param userContext context for the user that is creating this tile.
+ * @return a new {@link CustomTile}
+ */
+ public static CustomTile create(Builder builder, String spec, Context userContext) {
+ return builder
+ .setSpec(spec)
+ .setUserContext(userContext)
+ .build();
+ }
+
+ public static class Builder {
+ final Lazy<QSHost> mQSHostLazy;
+ final Looper mBackgroundLooper;
+ final Handler mMainHandler;
+ final MetricsLogger mMetricsLogger;
+ final StatusBarStateController mStatusBarStateController;
+ final ActivityStarter mActivityStarter;
+ final QSLogger mQSLogger;
+
+ Context mUserContext;
+ String mSpec = "";
+
+ @Inject
+ public Builder(
+ Lazy<QSHost> hostLazy,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
+ mQSHostLazy = hostLazy;
+ mBackgroundLooper = backgroundLooper;
+ mMainHandler = mainHandler;
+ mMetricsLogger = metricsLogger;
+ mStatusBarStateController = statusBarStateController;
+ mActivityStarter = activityStarter;
+ mQSLogger = qsLogger;
+ }
+
+ Builder setSpec(@NonNull String spec) {
+ mSpec = spec;
+ return this;
+ }
+
+ Builder setUserContext(@NonNull Context userContext) {
+ mUserContext = userContext;
+ return this;
+ }
+
+ CustomTile build() {
+ if (mUserContext == null) {
+ throw new NullPointerException("UserContext cannot be null");
+ }
+ String action = getAction(mSpec);
+ return new CustomTile(
+ mQSHostLazy.get(),
+ mBackgroundLooper,
+ mMainHandler,
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ action,
+ mUserContext
+ );
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index c182a58a28c4..69a6fe1075c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -80,9 +80,12 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<ScreenRecordTile> mScreenRecordTileProvider;
private final Lazy<QSHost> mQsHostLazy;
+ private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@Inject
- public QSFactoryImpl(Lazy<QSHost> qsHostLazy,
+ public QSFactoryImpl(
+ Lazy<QSHost> qsHostLazy,
+ Provider<CustomTile.Builder> customTileBuilderProvider,
Provider<WifiTile> wifiTileProvider,
Provider<BluetoothTile> bluetoothTileProvider,
Provider<CellularTile> cellularTileProvider,
@@ -104,6 +107,8 @@ public class QSFactoryImpl implements QSFactory {
Provider<UiModeNightTile> uiModeNightTileProvider,
Provider<ScreenRecordTile> screenRecordTileProvider) {
mQsHostLazy = qsHostLazy;
+ mCustomTileBuilderProvider = customTileBuilderProvider;
+
mWifiTileProvider = wifiTileProvider;
mBluetoothTileProvider = bluetoothTileProvider;
mCellularTileProvider = cellularTileProvider;
@@ -179,8 +184,8 @@ public class QSFactoryImpl implements QSFactory {
// Custom tiles
if (tileSpec.startsWith(CustomTile.PREFIX)) {
- return CustomTile.create(mQsHostLazy.get(), tileSpec,
- mQsHostLazy.get().getUserContext());
+ return CustomTile.create(
+ mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
}
// Debug tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 795d0627c447..8c485a6a950f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -14,6 +14,7 @@
package com.android.systemui.qs.tileimpl;
+import static androidx.lifecycle.Lifecycle.State.DESTROYED;
import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static androidx.lifecycle.Lifecycle.State.STARTED;
@@ -54,7 +55,6 @@ import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
import com.android.systemui.plugins.ActivityStarter;
@@ -92,12 +92,12 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
protected final QSHost mHost;
protected final Context mContext;
// @NonFinalForTesting
- protected H mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
- protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
+ protected final H mHandler;
+ protected final Handler mUiHandler;
private final ArraySet<Object> mListeners = new ArraySet<>();
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
- private final StatusBarStateController
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+ private final MetricsLogger mMetricsLogger;
+ private final StatusBarStateController mStatusBarStateController;
+ protected final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
@@ -150,14 +150,29 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
*/
abstract public int getMetricsCategory();
- protected QSTileImpl(QSHost host) {
+ protected QSTileImpl(
+ QSHost host,
+ Looper backgroundLooper,
+ Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
mHost = host;
mContext = host.getContext();
mInstanceId = host.getNewInstanceId();
+ mUiEventLogger = host.getUiEventLogger();
+
+ mUiHandler = mainHandler;
+ mHandler = new H(backgroundLooper);
+ mQSLogger = qsLogger;
+ mMetricsLogger = metricsLogger;
+ mStatusBarStateController = statusBarStateController;
+ mActivityStarter = activityStarter;
+
mState = newTileState();
mTmpState = newTileState();
- mQSLogger = host.getQSLogger();
- mUiEventLogger = host.getUiEventLogger();
}
protected final void resetStates() {
@@ -357,8 +372,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
* {@link QSTileImpl#getLongClickIntent}
*/
protected void handleLongClick() {
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
- getLongClickIntent(), 0);
+ mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
}
/**
@@ -475,6 +489,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
}
mCallbacks.clear();
mHandler.removeCallbacksAndMessages(null);
+ // This will force it to be removed from all controllers that may have it registered.
+ mLifecycle.setCurrentState(DESTROYED);
}
protected void checkIfRestrictionEnforcedByAdminOnly(State state, String userRestriction) {
@@ -530,7 +546,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
private static final int REMOVE_CALLBACKS = 11;
private static final int REMOVE_CALLBACK = 12;
private static final int SET_LISTENING = 13;
- private static final int STALE = 14;
+ @VisibleForTesting
+ protected static final int STALE = 14;
@VisibleForTesting
protected H(Looper looper) {
@@ -555,8 +572,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
if (mState.disabledByPolicy) {
Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
mContext, mEnforcedAdmin);
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
- intent, 0);
+ mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
} else {
handleClick();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index b24fdbfc562f..b8485907c519 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -21,6 +21,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -33,33 +35,50 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import javax.inject.Inject;
+import dagger.Lazy;
+
/** Quick settings tile: Airplane mode **/
public class AirplaneModeTile extends QSTileImpl<BooleanState> {
private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_airplane);
private final GlobalSetting mSetting;
- private final ActivityStarter mActivityStarter;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final Lazy<ConnectivityManager> mLazyConnectivityManager;
private boolean mListening;
@Inject
- public AirplaneModeTile(QSHost host, ActivityStarter activityStarter,
- BroadcastDispatcher broadcastDispatcher) {
- super(host);
- mActivityStarter = activityStarter;
+ public AirplaneModeTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BroadcastDispatcher broadcastDispatcher,
+ Lazy<ConnectivityManager> lazyConnectivityManager
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mBroadcastDispatcher = broadcastDispatcher;
+ mLazyConnectivityManager = lazyConnectivityManager;
mSetting = new GlobalSetting(mContext, mHandler, Global.AIRPLANE_MODE_ON) {
@Override
protected void handleValueChanged(int value) {
+ // mHandler is the background handler so calling this is OK
handleRefreshState(value);
}
};
@@ -83,9 +102,7 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> {
}
private void setEnabled(boolean enabled) {
- final ConnectivityManager mgr =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- mgr.setAirplaneMode(enabled);
+ mLazyConnectivityManager.get().setAirplaneMode(enabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index f2495048bf26..eb794a8b4378 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -16,16 +16,24 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.widget.Switch;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -46,8 +54,18 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
private Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_battery_saver);
@Inject
- public BatterySaverTile(QSHost host, BatteryController batteryController) {
- super(host);
+ public BatterySaverTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BatteryController batteryController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mBatteryController = batteryController;
mBatteryController.observe(getLifecycle(), this);
int currentUser = host.getUserContext().getUserId();
@@ -55,6 +73,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
currentUser) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
+ // mHandler is the background handler so calling this is OK
handleRefreshState(null);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index c67ece060b02..1424244b6729 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -24,6 +24,8 @@ import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
@@ -37,12 +39,16 @@ import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -58,15 +64,21 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
private final BluetoothController mController;
private final BluetoothDetailAdapter mDetailAdapter;
- private final ActivityStarter mActivityStarter;
@Inject
- public BluetoothTile(QSHost host,
- BluetoothController bluetoothController,
- ActivityStarter activityStarter) {
- super(host);
+ public BluetoothTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BluetoothController bluetoothController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = bluetoothController;
- mActivityStarter = activityStarter;
mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
mController.observe(getLifecycle(), mCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4b53ae2c6379..56b939df383f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -22,6 +22,8 @@ import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.media.MediaRouter.RouteInfo;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.util.Log;
@@ -35,12 +37,16 @@ import com.android.internal.app.MediaRouteDialogPresenter;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.CastController;
@@ -64,20 +70,28 @@ public class CastTile extends QSTileImpl<BooleanState> {
private final KeyguardStateController mKeyguard;
private final NetworkController mNetworkController;
private final Callback mCallback = new Callback();
- private final ActivityStarter mActivityStarter;
private Dialog mDialog;
private boolean mWifiConnected;
@Inject
- public CastTile(QSHost host, CastController castController,
- KeyguardStateController keyguardStateController, NetworkController networkController,
- ActivityStarter activityStarter) {
- super(host);
+ public CastTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ CastController castController,
+ KeyguardStateController keyguardStateController,
+ NetworkController networkController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = castController;
mDetailAdapter = new CastDetailAdapter();
mKeyguard = keyguardStateController;
mNetworkController = networkController;
- mActivityStarter = activityStarter;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
mNetworkController.observe(this, mSignalCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index bc03ca617dea..b4f1fe72f944 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -23,6 +23,8 @@ import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.telephony.SubscriptionManager;
@@ -39,12 +41,16 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.SignalTileView;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -62,14 +68,21 @@ public class CellularTile extends QSTileImpl<SignalState> {
private final CellularDetailAdapter mDetailAdapter;
private final CellSignalCallback mSignalCallback = new CellSignalCallback();
- private final ActivityStarter mActivityStarter;
@Inject
- public CellularTile(QSHost host, NetworkController networkController,
- ActivityStarter activityStarter) {
- super(host);
+ public CellularTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ NetworkController networkController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = networkController;
- mActivityStarter = activityStarter;
mDataController = mController.getMobileDataController();
mDetailAdapter = new CellularDetailAdapter();
mController.observe(getLifecycle(), mSignalCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 9c0030d528e7..347ef45824c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -17,17 +17,26 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.R.drawable;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import javax.inject.Inject;
@@ -35,19 +44,33 @@ import javax.inject.Inject;
/** Quick settings tile: Invert colors **/
public class ColorInversionTile extends QSTileImpl<BooleanState> {
+ private static final String EXTRA_FRAGMENT_ARGS_KEY = ":settings:fragment_args_key";
+ private static final String EXTRA_SHOW_FRAGMENT_ARGS_KEY = ":settings:show_fragment_args";
+ private static final String COLOR_INVERSION_PREFERENCE_KEY = "toggle_inversion_preference";
+
private final Icon mIcon = ResourceIcon.get(drawable.ic_invert_colors);
private final SecureSetting mSetting;
private boolean mListening;
@Inject
- public ColorInversionTile(QSHost host) {
- super(host);
-
- mSetting = new SecureSetting(mContext, mHandler,
+ public ColorInversionTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
+
+ mSetting = new SecureSetting(mContext, mainHandler,
Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
+ // mHandler is the background handler so calling this is OK
handleRefreshState(value);
}
};
@@ -78,7 +101,11 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> {
@Override
public Intent getLongClickIntent() {
- return new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
+ Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_FRAGMENT_ARGS_KEY, COLOR_INVERSION_PREFERENCE_KEY);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGS_KEY, bundle);
+ return intent;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 7ae8fbc928a6..85f12458c33a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -16,19 +16,26 @@ package com.android.systemui.qs.tiles;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.NetworkController;
import javax.inject.Inject;
@@ -38,9 +45,19 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
private final DataSaverController mDataSaverController;
@Inject
- public DataSaverTile(QSHost host, NetworkController networkController) {
- super(host);
- mDataSaverController = networkController.getDataSaverController();
+ public DataSaverTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ DataSaverController dataSaverController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
+ mDataSaverController = dataSaverController;
mDataSaverController.observe(getLifecycle(), this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 9f7b84aa62c1..ec8b1435e201 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -30,6 +30,8 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -53,11 +55,14 @@ import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysUIToast;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -79,7 +84,6 @@ public class DndTile extends QSTileImpl<BooleanState> {
private final ZenModeController mController;
private final DndDetailAdapter mDetailAdapter;
- private final ActivityStarter mActivityStarter;
private final SharedPreferences mSharedPreferences;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -88,12 +92,21 @@ public class DndTile extends QSTileImpl<BooleanState> {
private boolean mReceiverRegistered;
@Inject
- public DndTile(QSHost host, ZenModeController zenModeController,
- ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher,
- @Main SharedPreferences sharedPreferences) {
- super(host);
+ public DndTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ ZenModeController zenModeController,
+ BroadcastDispatcher broadcastDispatcher,
+ @Main SharedPreferences sharedPreferences
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = zenModeController;
- mActivityStarter = activityStarter;
mSharedPreferences = sharedPreferences;
mDetailAdapter = new DndDetailAdapter();
mBroadcastDispatcher = broadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 27ccd7cab226..cd45082458f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -18,14 +18,22 @@ package com.android.systemui.qs.tiles;
import android.app.ActivityManager;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.MediaStore;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
@@ -39,8 +47,18 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
private final FlashlightController mFlashlightController;
@Inject
- public FlashlightTile(QSHost host, FlashlightController flashlightController) {
- super(host);
+ public FlashlightTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ FlashlightController flashlightController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mFlashlightController = flashlightController;
mFlashlightController.observe(getLifecycle(), this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 1ab77f3f7524..a45d94ace425 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -18,16 +18,24 @@ package com.android.systemui.qs.tiles;
import android.annotation.Nullable;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.util.Log;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -45,9 +53,19 @@ public class HotspotTile extends QSTileImpl<BooleanState> {
private boolean mListening;
@Inject
- public HotspotTile(QSHost host, HotspotController hotspotController,
- DataSaverController dataSaverController) {
- super(host);
+ public HotspotTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ HotspotController hotspotController,
+ DataSaverController dataSaverController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mHotspotController = hotspotController;
mDataSaverController = dataSaverController;
mHotspotController.observe(this, mCallbacks);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 02f364bd3a7e..d502d06efceb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -17,16 +17,23 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -41,16 +48,24 @@ public class LocationTile extends QSTileImpl<BooleanState> {
private final LocationController mController;
private final KeyguardStateController mKeyguard;
- private final ActivityStarter mActivityStarter;
private final Callback mCallback = new Callback();
@Inject
- public LocationTile(QSHost host, LocationController locationController,
- KeyguardStateController keyguardStateController, ActivityStarter activityStarter) {
- super(host);
+ public LocationTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ LocationController locationController,
+ KeyguardStateController keyguardStateController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = locationController;
mKeyguard = keyguardStateController;
- mActivityStarter = activityStarter;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 7da913592286..fb281169b2f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -23,15 +23,23 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import javax.inject.Inject;
@@ -47,8 +55,18 @@ public class NfcTile extends QSTileImpl<BooleanState> {
private boolean mListening;
@Inject
- public NfcTile(QSHost host, BroadcastDispatcher broadcastDispatcher) {
- super(host);
+ public NfcTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BroadcastDispatcher broadcastDispatcher
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mBroadcastDispatcher = broadcastDispatcher;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 2f582727c766..3264429a1723 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -33,10 +33,17 @@ import android.widget.Switch;
import androidx.annotation.StringRes;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.NightDisplayListenerModule;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.LocationController;
@@ -62,15 +69,29 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements
private final ColorDisplayManager mManager;
private final LocationController mLocationController;
+ private final NightDisplayListenerModule.Builder mNightDisplayListenerBuilder;
private NightDisplayListener mListener;
private boolean mIsListening;
@Inject
- public NightDisplayTile(QSHost host, LocationController locationController) {
- super(host);
+ public NightDisplayTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ LocationController locationController,
+ ColorDisplayManager colorDisplayManager,
+ NightDisplayListenerModule.Builder nightDisplayListenerBuilder
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mLocationController = locationController;
- mManager = mContext.getSystemService(ColorDisplayManager.class);
- mListener = new NightDisplayListener(mContext, new Handler(Looper.myLooper()));
+ mManager = colorDisplayManager;
+ mNightDisplayListenerBuilder = nightDisplayListenerBuilder;
+ mListener = mNightDisplayListenerBuilder.setUser(host.getUserContext().getUserId()).build();
}
@Override
@@ -106,7 +127,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements
}
// Make a new controller for the new user.
- mListener = new NightDisplayListener(mContext, newUserId, new Handler(Looper.myLooper()));
+ mListener = mNightDisplayListenerBuilder.setUser(newUserId).build();
if (mIsListening) {
mListener.setCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 5dcb4e3b1fb9..4bf27e2aa4b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -19,14 +19,22 @@ package com.android.systemui.qs.tiles;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
@@ -40,8 +48,18 @@ public class RotationLockTile extends QSTileImpl<BooleanState> {
private final RotationLockController mController;
@Inject
- public RotationLockTile(QSHost host, RotationLockController rotationLockController) {
- super(host);
+ public RotationLockTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ RotationLockController rotationLockController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = rotationLockController;
mController.observe(this, mCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 32ef063a55be..d7a2975a4b04 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -17,18 +17,25 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Switch;
-import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import javax.inject.Inject;
@@ -39,19 +46,27 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
implements RecordingController.RecordingStateChangeCallback {
private static final String TAG = "ScreenRecordTile";
private RecordingController mController;
- private ActivityStarter mActivityStarter;
+ private KeyguardDismissUtil mKeyguardDismissUtil;
private long mMillisUntilFinished = 0;
private Callback mCallback = new Callback();
- private UiEventLogger mUiEventLogger;
@Inject
- public ScreenRecordTile(QSHost host, RecordingController controller,
- ActivityStarter activityStarter, UiEventLogger uiEventLogger) {
- super(host);
+ public ScreenRecordTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ RecordingController controller,
+ KeyguardDismissUtil keyguardDismissUtil
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = controller;
mController.observe(this, mCallback);
- mActivityStarter = activityStarter;
- mUiEventLogger = uiEventLogger;
+ mKeyguardDismissUtil = keyguardDismissUtil;
}
@Override
@@ -69,7 +84,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
} else if (mController.isRecording()) {
stopRecording();
} else {
- startCountdown();
+ mUiHandler.post(() -> showPrompt());
}
refreshState();
}
@@ -114,11 +129,15 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
return mContext.getString(R.string.quick_settings_screen_record_label);
}
- private void startCountdown() {
- // Close QS, otherwise the permission dialog appears beneath it
+ private void showPrompt() {
+ // Close QS, otherwise the dialog appears beneath it
getHost().collapsePanels();
Intent intent = mController.getPromptIntent();
- mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+ ActivityStarter.OnDismissAction dismissAction = () -> {
+ mContext.startActivity(intent);
+ return false;
+ };
+ mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false);
}
private void cancelCountdown() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 7b83c20d4b86..07b841ffb6f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -19,15 +19,23 @@ package com.android.systemui.qs.tiles;
import android.app.UiModeManager;
import android.content.Intent;
import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -55,9 +63,20 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
private final LocationController mLocationController;
@Inject
- public UiModeNightTile(QSHost host, ConfigurationController configurationController,
- BatteryController batteryController, LocationController locationController) {
- super(host);
+ public UiModeNightTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ ConfigurationController configurationController,
+ BatteryController batteryController,
+ LocationController locationController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mBatteryController = batteryController;
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mLocationController = locationController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index fba7a22351e2..201ed9c9ebec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -21,7 +21,6 @@ import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
@@ -33,6 +32,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.qs.PseudoGridView;
import com.android.systemui.qs.QSUserSwitcherEvent;
@@ -100,7 +100,9 @@ public class UserDetailView extends PseudoGridView {
if (item.picture == null) {
v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
} else {
- Drawable drawable = new BitmapDrawable(v.getResources(), item.picture);
+ int avatarSize =
+ (int) mContext.getResources().getDimension(R.dimen.qs_framed_avatar_size);
+ Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
drawable.setColorFilter(
item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
v.bind(name, drawable, item.info.id);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index aab30d499e08..26adfdcd8539 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -18,14 +18,22 @@ package com.android.systemui.qs.tiles;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.util.Pair;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -39,9 +47,19 @@ public class UserTile extends QSTileImpl<State> implements UserInfoController.On
private Pair<String, Drawable> mLastUpdate;
@Inject
- public UserTile(QSHost host, UserSwitcherController userSwitcherController,
- UserInfoController userInfoController) {
- super(host);
+ public UserTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ UserSwitcherController userSwitcherController,
+ UserInfoController userInfoController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mUserSwitcherController = userSwitcherController;
mUserInfoController = userInfoController;
mUserInfoController.observe(getLifecycle(), this);
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 1279d42eb64d..4d89dea7cb70 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -20,6 +20,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
@@ -32,15 +34,19 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.wifi.AccessPoint;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.AlphaControlledSignalTileView;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -63,17 +69,24 @@ public class WifiTile extends QSTileImpl<SignalState> {
private final QSTile.SignalState mStateBeforeClick = newTileState();
protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
- private final ActivityStarter mActivityStarter;
private boolean mExpectDisabled;
@Inject
- public WifiTile(QSHost host, NetworkController networkController,
- ActivityStarter activityStarter) {
- super(host);
+ public WifiTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ NetworkController networkController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = networkController;
mWifiController = mController.getAccessPointController();
mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
- mActivityStarter = activityStarter;
mController.observe(getLifecycle(), mSignalCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 318c0c4660cb..5235b6d5a0bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -17,14 +17,22 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -38,8 +46,18 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements
private final ManagedProfileController mProfileController;
@Inject
- public WorkModeTile(QSHost host, ManagedProfileController managedProfileController) {
- super(host);
+ public WorkModeTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ ManagedProfileController managedProfileController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mProfileController = managedProfileController;
mProfileController.observe(getLifecycle(), this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9115b4849355..d03082e6b442 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -809,7 +809,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void addCallback(OverviewProxyListener listener) {
- mConnectionCallbacks.add(listener);
+ if (!mConnectionCallbacks.contains(listener)) {
+ mConnectionCallbacks.add(listener);
+ }
listener.onConnectionChanged(mOverviewProxy != null);
listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index b253635e9bfa..82ac1f6f6a33 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -17,12 +17,17 @@
package com.android.systemui.screenrecord;
import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.os.CountDownTimer;
+import android.os.UserHandle;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.policy.CallbackController;
import java.util.ArrayList;
@@ -41,21 +46,30 @@ public class RecordingController
private static final String SYSUI_SCREENRECORD_LAUNCHER =
"com.android.systemui.screenrecord.ScreenRecordDialog";
- private final Context mContext;
private boolean mIsStarting;
private boolean mIsRecording;
private PendingIntent mStopIntent;
private CountDownTimer mCountDownTimer = null;
+ private BroadcastDispatcher mBroadcastDispatcher;
private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>();
+ @VisibleForTesting
+ protected final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mStopIntent != null) {
+ stopRecording();
+ }
+ }
+ };
+
/**
* Create a new RecordingController
- * @param context Context for the controller
*/
@Inject
- public RecordingController(Context context) {
- mContext = context;
+ public RecordingController(BroadcastDispatcher broadcastDispatcher) {
+ mBroadcastDispatcher = broadcastDispatcher;
}
/**
@@ -99,6 +113,9 @@ public class RecordingController
}
try {
startIntent.send();
+ IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+ mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userFilter, null,
+ UserHandle.ALL);
Log.d(TAG, "sent start intent");
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Pending intent was cancelled: " + e.getMessage());
@@ -146,11 +163,16 @@ public class RecordingController
*/
public void stopRecording() {
try {
- mStopIntent.send();
+ if (mStopIntent != null) {
+ mStopIntent.send();
+ } else {
+ Log.e(TAG, "Stop intent was null");
+ }
updateState(false);
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Error stopping: " + e.getMessage());
}
+ mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 87597263168a..469c4a7b4c57 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -32,6 +32,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
@@ -40,6 +41,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.LongRunning;
+import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import java.io.IOException;
import java.util.concurrent.Executor;
@@ -58,7 +61,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
private static final String TAG = "RecordingService";
private static final String CHANNEL_ID = "screen_record";
private static final String EXTRA_RESULT_CODE = "extra_resultCode";
- private static final String EXTRA_DATA = "extra_data";
private static final String EXTRA_PATH = "extra_path";
private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
@@ -71,7 +73,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE";
private final RecordingController mController;
-
+ private final KeyguardDismissUtil mKeyguardDismissUtil;
private ScreenRecordingAudioSource mAudioSource;
private boolean mShowTaps;
private boolean mOriginalShowTaps;
@@ -79,14 +81,18 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
private final Executor mLongExecutor;
private final UiEventLogger mUiEventLogger;
private final NotificationManager mNotificationManager;
+ private final CurrentUserContextTracker mUserContextTracker;
@Inject
public RecordingService(RecordingController controller, @LongRunning Executor executor,
- UiEventLogger uiEventLogger, NotificationManager notificationManager) {
+ UiEventLogger uiEventLogger, NotificationManager notificationManager,
+ CurrentUserContextTracker userContextTracker, KeyguardDismissUtil keyguardDismissUtil) {
mController = controller;
mLongExecutor = executor;
mUiEventLogger = uiEventLogger;
mNotificationManager = notificationManager;
+ mUserContextTracker = userContextTracker;
+ mKeyguardDismissUtil = keyguardDismissUtil;
}
/**
@@ -95,8 +101,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
* @param context Context from the requesting activity
* @param resultCode The result code from {@link android.app.Activity#onActivityResult(int, int,
* android.content.Intent)}
- * @param data The data from {@link android.app.Activity#onActivityResult(int, int,
- * android.content.Intent)}
* @param audioSource The ordinal value of the audio source
* {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource}
* @param showTaps True to make touches visible while recording
@@ -118,6 +122,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
String action = intent.getAction();
Log.d(TAG, "onStartCommand " + action);
+ int mCurrentUserId = mUserContextTracker.getCurrentUserContext().getUserId();
+ UserHandle currentUser = new UserHandle(mCurrentUserId);
switch (action) {
case ACTION_START:
mAudioSource = ScreenRecordingAudioSource
@@ -132,8 +138,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
setTapsVisible(mShowTaps);
mRecorder = new ScreenMediaRecorder(
- getApplicationContext(),
- getUserId(),
+ mUserContextTracker.getCurrentUserContext(),
+ mCurrentUserId,
mAudioSource,
this
);
@@ -148,7 +154,14 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
} else {
mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_END_QS_TILE);
}
- stopRecording();
+ // Check user ID - we may be getting a stop intent after user switch, in which case
+ // we want to post the notifications for that user, which is NOT current user
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId == -1) {
+ userId = mUserContextTracker.getCurrentUserContext().getUserId();
+ }
+ Log.d(TAG, "notifying for user " + userId);
+ stopRecording(userId);
mNotificationManager.cancel(NOTIFICATION_RECORDING_ID);
stopSelf();
break;
@@ -159,33 +172,36 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
Intent shareIntent = new Intent(Intent.ACTION_SEND)
.setType("video/mp4")
.putExtra(Intent.EXTRA_STREAM, shareUri);
- String shareLabel = getResources().getString(R.string.screenrecord_share_label);
+ mKeyguardDismissUtil.executeWhenUnlocked(() -> {
+ String shareLabel = getResources().getString(R.string.screenrecord_share_label);
+ startActivity(Intent.createChooser(shareIntent, shareLabel)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ // Remove notification
+ mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
+ return false;
+ }, false);
// Close quick shade
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
-
- // Remove notification
- mNotificationManager.cancel(NOTIFICATION_VIEW_ID);
-
- startActivity(Intent.createChooser(shareIntent, shareLabel)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
break;
case ACTION_DELETE:
- // Close quick shade
- sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
-
- ContentResolver resolver = getContentResolver();
- Uri uri = Uri.parse(intent.getStringExtra(EXTRA_PATH));
- resolver.delete(uri, null, null);
-
- Toast.makeText(
- this,
- R.string.screenrecord_delete_description,
- Toast.LENGTH_LONG).show();
-
- // Remove notification
- mNotificationManager.cancel(NOTIFICATION_VIEW_ID);
- Log.d(TAG, "Deleted recording " + uri);
+ mKeyguardDismissUtil.executeWhenUnlocked(() -> {
+ // Close quick shade
+ sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ ContentResolver resolver = getContentResolver();
+ Uri uri = Uri.parse(intent.getStringExtra(EXTRA_PATH));
+ resolver.delete(uri, null, null);
+
+ Toast.makeText(
+ this,
+ R.string.screenrecord_delete_description,
+ Toast.LENGTH_LONG).show();
+ Log.d(TAG, "Deleted recording " + uri);
+
+ // Remove notification
+ mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
+ return false;
+ }, false);
break;
}
return Service.START_STICKY;
@@ -215,11 +231,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
mController.updateState(true);
createRecordingNotification();
mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START);
- } catch (IOException | RemoteException e) {
+ } catch (IOException | RemoteException | IllegalStateException e) {
Toast.makeText(this,
R.string.screenrecord_start_error, Toast.LENGTH_LONG)
.show();
e.printStackTrace();
+ mController.updateState(false);
}
}
@@ -242,7 +259,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
? res.getString(R.string.screenrecord_ongoing_screen_only)
: res.getString(R.string.screenrecord_ongoing_screen_and_audio);
-
Intent stopIntent = getNotificationIntent(this);
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_screenrecord)
@@ -254,7 +270,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
.setOngoing(true)
.setContentIntent(
PendingIntent.getService(this, REQUEST_CODE, stopIntent,
- PendingIntent.FLAG_UPDATE_CURRENT))
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.addExtras(extras);
startForeground(NOTIFICATION_RECORDING_ID, builder.build());
}
@@ -265,11 +281,17 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE
? res.getString(R.string.screenrecord_ongoing_screen_only)
: res.getString(R.string.screenrecord_ongoing_screen_and_audio);
+
+ Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ res.getString(R.string.screenrecord_name));
+
Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID)
.setContentTitle(notificationTitle)
.setContentText(
getResources().getString(R.string.screenrecord_background_processing_label))
- .setSmallIcon(R.drawable.ic_screenrecord);
+ .setSmallIcon(R.drawable.ic_screenrecord)
+ .addExtras(extras);
return builder.build();
}
@@ -287,7 +309,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
this,
REQUEST_CODE,
getShareIntent(this, uri.toString()),
- PendingIntent.FLAG_UPDATE_CURRENT))
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.build();
Notification.Action deleteAction = new Notification.Action.Builder(
@@ -297,7 +319,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
this,
REQUEST_CODE,
getDeleteIntent(this, uri.toString()),
- PendingIntent.FLAG_UPDATE_CURRENT))
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.build();
Bundle extras = new Bundle();
@@ -328,34 +350,36 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
return builder.build();
}
- private void stopRecording() {
+ private void stopRecording(int userId) {
setTapsVisible(mOriginalShowTaps);
if (getRecorder() != null) {
getRecorder().end();
- saveRecording();
+ saveRecording(userId);
} else {
Log.e(TAG, "stopRecording called, but recorder was null");
}
mController.updateState(false);
}
- private void saveRecording() {
- mNotificationManager.notify(NOTIFICATION_PROCESSING_ID, createProcessingNotification());
+ private void saveRecording(int userId) {
+ UserHandle currentUser = new UserHandle(userId);
+ mNotificationManager.notifyAsUser(null, NOTIFICATION_PROCESSING_ID,
+ createProcessingNotification(), currentUser);
mLongExecutor.execute(() -> {
try {
Log.d(TAG, "saving recording");
Notification notification = createSaveNotification(getRecorder().save());
if (!mController.isRecording()) {
- Log.d(TAG, "showing saved notification");
- mNotificationManager.notify(NOTIFICATION_VIEW_ID, notification);
+ mNotificationManager.notifyAsUser(null, NOTIFICATION_VIEW_ID, notification,
+ currentUser);
}
} catch (IOException e) {
Log.e(TAG, "Error saving screen recording: " + e.getMessage());
Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
.show();
} finally {
- mNotificationManager.cancel(NOTIFICATION_PROCESSING_ID);
+ mNotificationManager.cancelAsUser(null, NOTIFICATION_PROCESSING_ID, currentUser);
}
});
}
@@ -371,7 +395,9 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
* @return
*/
public static Intent getStopIntent(Context context) {
- return new Intent(context, RecordingService.class).setAction(ACTION_STOP);
+ return new Intent(context, RecordingService.class)
+ .setAction(ACTION_STOP)
+ .putExtra(Intent.EXTRA_USER_HANDLE, context.getUserId());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
index edbc3cfdece5..df03c3e08f08 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
@@ -16,7 +16,6 @@
package com.android.systemui.screenrecord;
-import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioPlaybackCaptureConfiguration;
@@ -39,7 +38,6 @@ public class ScreenInternalAudioRecorder {
private static String TAG = "ScreenAudioRecorder";
private static final int TIMEOUT = 500;
private static final float MIC_VOLUME_SCALE = 1.4f;
- private final Context mContext;
private AudioRecord mAudioRecord;
private AudioRecord mAudioRecordMic;
private Config mConfig = new Config();
@@ -49,17 +47,14 @@ public class ScreenInternalAudioRecorder {
private long mPresentationTime;
private long mTotalBytes;
private MediaMuxer mMuxer;
- private String mOutFile;
private boolean mMic;
private int mTrackId = -1;
- public ScreenInternalAudioRecorder(String outFile, Context context,
- MediaProjection mp, boolean includeMicInput) throws IOException {
+ public ScreenInternalAudioRecorder(String outFile, MediaProjection mp, boolean includeMicInput)
+ throws IOException {
mMic = includeMicInput;
- mOutFile = outFile;
mMuxer = new MediaMuxer(outFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
- mContext = context;
mMediaProjection = mp;
Log.d(TAG, "creating audio file " + outFile);
setupSimple();
@@ -266,8 +261,9 @@ public class ScreenInternalAudioRecorder {
/**
* start recording
+ * @throws IllegalStateException if recording fails to initialize
*/
- public void start() {
+ public void start() throws IllegalStateException {
if (mThread != null) {
Log.e(TAG, "a recording is being done in parallel or stop is not called");
}
@@ -276,8 +272,7 @@ public class ScreenInternalAudioRecorder {
Log.d(TAG, "channel count " + mAudioRecord.getChannelCount());
mCodec.start();
if (mAudioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
- Log.e(TAG, "Error starting audio recording");
- return;
+ throw new IllegalStateException("Audio recording failed to start");
}
mThread.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 1c7d987afff2..1a9abb9cf27d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -166,7 +166,7 @@ public class ScreenMediaRecorder {
mAudioSource == MIC_AND_INTERNAL) {
mTempAudioFile = File.createTempFile("temp", ".aac",
mContext.getCacheDir());
- mAudio = new ScreenInternalAudioRecorder(mTempAudioFile.getAbsolutePath(), mContext,
+ mAudio = new ScreenInternalAudioRecorder(mTempAudioFile.getAbsolutePath(),
mMediaProjection, mAudioSource == MIC_AND_INTERNAL);
}
@@ -175,7 +175,7 @@ public class ScreenMediaRecorder {
/**
* Start screen recording
*/
- void start() throws IOException, RemoteException {
+ void start() throws IOException, RemoteException, IllegalStateException {
Log.d(TAG, "start recording");
prepare();
mMediaRecorder.start();
@@ -205,7 +205,7 @@ public class ScreenMediaRecorder {
}
}
- private void recordInternalAudio() {
+ private void recordInternalAudio() throws IllegalStateException {
if (mAudioSource == INTERNAL || mAudioSource == MIC_AND_INTERNAL) {
mAudio.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 8347def2d430..dc47ab4dff63 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -23,16 +23,19 @@ import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.NONE;
import android.app.Activity;
import android.app.PendingIntent;
+import android.content.Context;
import android.os.Bundle;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.Switch;
import com.android.systemui.R;
+import com.android.systemui.settings.CurrentUserContextTracker;
import java.util.ArrayList;
import java.util.List;
@@ -48,16 +51,17 @@ public class ScreenRecordDialog extends Activity {
private static final String TAG = "ScreenRecordDialog";
private final RecordingController mController;
+ private final CurrentUserContextTracker mCurrentUserContextTracker;
private Switch mTapsSwitch;
private Switch mAudioSwitch;
private Spinner mOptions;
private List<ScreenRecordingAudioSource> mModes;
- private int mSelected;
-
@Inject
- public ScreenRecordDialog(RecordingController controller) {
+ public ScreenRecordDialog(RecordingController controller,
+ CurrentUserContextTracker currentUserContextTracker) {
mController = controller;
+ mCurrentUserContextTracker = currentUserContextTracker;
}
@Override
@@ -68,6 +72,7 @@ public class ScreenRecordDialog extends Activity {
// Inflate the decor view, so the attributes below are not overwritten by the theme.
window.getDecorView();
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
window.setGravity(Gravity.TOP);
setTitle(R.string.screenrecord_name);
@@ -100,24 +105,24 @@ public class ScreenRecordDialog extends Activity {
mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
mAudioSwitch.setChecked(true);
});
-
}
private void requestScreenCapture() {
+ Context userContext = mCurrentUserContextTracker.getCurrentUserContext();
boolean showTaps = mTapsSwitch.isChecked();
ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
: NONE;
- PendingIntent startIntent = PendingIntent.getForegroundService(this,
+ PendingIntent startIntent = PendingIntent.getForegroundService(userContext,
RecordingService.REQUEST_CODE,
RecordingService.getStartIntent(
- ScreenRecordDialog.this, RESULT_OK,
+ userContext, RESULT_OK,
audioMode.ordinal(), showTaps),
- PendingIntent.FLAG_UPDATE_CURRENT);
- PendingIntent stopIntent = PendingIntent.getService(this,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ PendingIntent stopIntent = PendingIntent.getService(userContext,
RecordingService.REQUEST_CODE,
- RecordingService.getStopIntent(this),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ RecordingService.getStopIntent(userContext),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
new file mode 100644
index 000000000000..3fd7f94514f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_EDIT;
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.inject.Inject;
+
+/**
+ * Receiver to proxy the share or edit intent, used to clean up the notification and send
+ * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
+ */
+public class ActionProxyReceiver extends BroadcastReceiver {
+ private static final String TAG = "ActionProxyReceiver";
+
+ private static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
+ private final StatusBar mStatusBar;
+ private final ActivityManagerWrapper mActivityManagerWrapper;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+
+ @Inject
+ public ActionProxyReceiver(Optional<StatusBar> statusBar,
+ ActivityManagerWrapper activityManagerWrapper,
+ ScreenshotSmartActions screenshotSmartActions) {
+ mStatusBar = statusBar.orElse(null);
+ mActivityManagerWrapper = activityManagerWrapper;
+ mScreenshotSmartActions = screenshotSmartActions;
+ }
+
+ @Override
+ public void onReceive(Context context, final Intent intent) {
+ Runnable startActivityRunnable = () -> {
+ try {
+ mActivityManagerWrapper.closeSystemWindows(
+ SYSTEM_DIALOG_REASON_SCREENSHOT).get(
+ CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Unable to share screenshot", e);
+ return;
+ }
+
+ PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+ ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setDisallowEnterPictureInPictureWhileLaunching(
+ intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
+ try {
+ actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent canceled", e);
+ }
+
+ };
+
+ if (mStatusBar != null) {
+ mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
+ true /* dismissShade */, true /* afterKeyguardGone */,
+ true /* deferred */);
+ } else {
+ startActivityRunnable.run();
+ }
+
+ if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+ String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
+ ? ACTION_TYPE_EDIT
+ : ACTION_TYPE_SHARE;
+ mScreenshotSmartActions.notifyScreenshotAction(
+ context, intent.getStringExtra(EXTRA_ID), actionType, false);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
deleted file mode 100644
index 8c4865510ed1..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.AsyncTask;
-
-/**
- * An AsyncTask that deletes an image from the media store in the background.
- */
-class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
- private Context mContext;
-
- DeleteImageInBackgroundTask(Context context) {
- mContext = context;
- }
-
- @Override
- protected Void doInBackground(Uri... params) {
- if (params.length != 1) return null;
-
- Uri screenshotUri = params[0];
- ContentResolver resolver = mContext.getContentResolver();
- resolver.delete(screenshotUri, null, null);
- return null;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
new file mode 100644
index 000000000000..9028bb57c8e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import com.android.systemui.dagger.qualifiers.Background;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Removes the file at a provided URI.
+ */
+public class DeleteScreenshotReceiver extends BroadcastReceiver {
+
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+ private final Executor mBackgroundExecutor;
+
+ @Inject
+ public DeleteScreenshotReceiver(ScreenshotSmartActions screenshotSmartActions,
+ @Background Executor backgroundExecutor) {
+ mScreenshotSmartActions = screenshotSmartActions;
+ mBackgroundExecutor = backgroundExecutor;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
+ return;
+ }
+
+ // And delete the image from the media store
+ final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
+ mBackgroundExecutor.execute(() -> {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.delete(uri, null, null);
+ });
+ if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+ mScreenshotSmartActions.notifyScreenshotAction(
+ context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 9bbc4ddcc62c..c53523032353 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -18,10 +18,9 @@ package com.android.systemui.screenshot;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -29,13 +28,10 @@ import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.Notification;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -56,14 +52,11 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
-import android.util.Slog;
import android.view.Display;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -73,6 +66,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
@@ -87,22 +81,15 @@ import android.widget.Toast;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.shared.system.QuickStepContract;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
-import dagger.Lazy;
-
/**
* Class for handling device screen shots
*/
@@ -191,6 +178,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private final UiEventLogger mUiEventLogger;
private final Context mContext;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
private final Display mDisplay;
@@ -212,15 +200,19 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private Animator mScreenshotAnimation;
private Runnable mOnCompleteRunnable;
private Animator mDismissAnimation;
- private boolean mInDarkMode = false;
- private boolean mDirectionLTR = true;
- private boolean mOrientationPortrait = true;
+ private boolean mInDarkMode;
+ private boolean mDirectionLTR;
+ private boolean mOrientationPortrait;
private float mCornerSizeX;
private float mDismissDeltaY;
private MediaActionSound mCameraSound;
+ private int mNavMode;
+ private int mLeftInset;
+ private int mRightInset;
+
// standard material ease
private final Interpolator mFastOutSlowIn;
@@ -239,15 +231,14 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
};
- /**
- * @param context everything needs a context :(
- */
@Inject
public GlobalScreenshot(
Context context, @Main Resources resources,
+ ScreenshotSmartActions screenshotSmartActions,
ScreenshotNotificationsController screenshotNotificationsController,
UiEventLogger uiEventLogger) {
mContext = context;
+ mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
mUiEventLogger = uiEventLogger;
@@ -302,9 +293,116 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mDismissButton.getBoundsOnScreen(dismissRect);
touchRegion.op(dismissRect, Region.Op.UNION);
+ if (QuickStepContract.isGesturalMode(mNavMode)) {
+ // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE
+ Rect inset = new Rect(0, 0, mLeftInset, mDisplayMetrics.heightPixels);
+ touchRegion.op(inset, Region.Op.UNION);
+ inset.set(mDisplayMetrics.widthPixels - mRightInset, 0, mDisplayMetrics.widthPixels,
+ mDisplayMetrics.heightPixels);
+ touchRegion.op(inset, Region.Op.UNION);
+ }
+
inoutInfo.touchableRegion.set(touchRegion);
}
+ void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
+ mOnCompleteRunnable = onComplete;
+
+ mDisplay.getRealMetrics(mDisplayMetrics);
+ takeScreenshotInternal(
+ finisher,
+ new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+ }
+
+ void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
+ Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
+ Consumer<Uri> finisher, Runnable onComplete) {
+ // TODO: use task Id, userId, topComponent for smart handler
+
+ mOnCompleteRunnable = onComplete;
+ if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
+ saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
+ } else {
+ saveScreenshot(screenshot, finisher,
+ new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
+ true);
+ }
+ }
+
+ /**
+ * Displays a screenshot selector
+ */
+ @SuppressLint("ClickableViewAccessibility")
+ void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
+ dismissScreenshot("new screenshot requested", true);
+ mOnCompleteRunnable = onComplete;
+
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ mScreenshotSelectorView.setOnTouchListener((v, event) -> {
+ ScreenshotSelectorView view = (ScreenshotSelectorView) v;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ view.startSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ view.updateSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_UP:
+ view.setVisibility(View.GONE);
+ mWindowManager.removeView(mScreenshotLayout);
+ final Rect rect = view.getSelectionRect();
+ if (rect != null) {
+ if (rect.width() != 0 && rect.height() != 0) {
+ // Need mScreenshotLayout to handle it after the view disappears
+ mScreenshotLayout.post(() -> takeScreenshotInternal(finisher, rect));
+ }
+ }
+
+ view.stopSelection();
+ return true;
+ }
+
+ return false;
+ });
+ mScreenshotLayout.post(() -> {
+ mScreenshotSelectorView.setVisibility(View.VISIBLE);
+ mScreenshotSelectorView.requestFocus();
+ });
+ }
+
+ /**
+ * Cancels screenshot request
+ */
+ void stopScreenshot() {
+ // If the selector layer still presents on screen, we remove it and resets its state.
+ if (mScreenshotSelectorView.getSelectionRect() != null) {
+ mWindowManager.removeView(mScreenshotLayout);
+ mScreenshotSelectorView.stopSelection();
+ }
+ }
+
+ /**
+ * Clears current screenshot
+ */
+ void dismissScreenshot(String reason, boolean immediate) {
+ Log.v(TAG, "clearing screenshot: " + reason);
+ mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+ mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ if (!immediate) {
+ mDismissAnimation = createScreenshotDismissAnimation();
+ mDismissAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ clearScreenshot();
+ }
+ });
+ mDismissAnimation.start();
+ } else {
+ clearScreenshot();
+ }
+ }
+
private void onConfigChanged(Configuration newConfig) {
boolean needsUpdate = false;
// dark mode
@@ -357,6 +455,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
if (needsUpdate) {
reloadAssets();
}
+
+ mNavMode = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode);
}
/**
@@ -371,15 +472,31 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
// Inflate the screenshot layout
mScreenshotLayout = LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
- mScreenshotLayout.setOnKeyListener(new View.OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- dismissScreenshot("back pressed", true);
- return true;
- }
- return false;
+ // TODO(159460485): Remove this when focus is handled properly in the system
+ mScreenshotLayout.setOnTouchListener((v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
+ // Once the user touches outside, stop listening for input
+ setWindowFocusable(false);
+ }
+ return false;
+ });
+ mScreenshotLayout.setOnApplyWindowInsetsListener((v, insets) -> {
+ if (QuickStepContract.isGesturalMode(mNavMode)) {
+ Insets gestureInsets = insets.getInsets(
+ WindowInsets.Type.systemGestures());
+ mLeftInset = gestureInsets.left;
+ mRightInset = gestureInsets.right;
+ } else {
+ mLeftInset = mRightInset = 0;
+ }
+ return mScreenshotLayout.onApplyWindowInsets(insets);
+ });
+ mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ dismissScreenshot("back pressed", false);
+ return true;
}
+ return false;
});
// Get focus so that the key events go to the layout.
mScreenshotLayout.setFocusableInTouchMode(true);
@@ -433,45 +550,20 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
}
-
- /**
- * Creates a new worker thread and saves the screenshot to the media store.
- */
- private void saveScreenshotInWorkerThread(
- Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
- SaveImageInBackgroundData data = new SaveImageInBackgroundData();
- data.image = mScreenBitmap;
- data.finisher = finisher;
- data.mActionsReadyListener = actionsReadyListener;
-
- if (mSaveInBgTask != null) {
- // just log success/failure for the pre-existing screenshot
- mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
- @Override
- void onActionsReady(SavedImageData imageData) {
- logSuccessOnActionsReady(imageData);
- }
- });
- }
-
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
- mSaveInBgTask.execute();
- }
-
/**
* Takes a screenshot of the current display and shows an animation.
*/
- private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
+ private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
// copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop);
int rot = mDisplay.getRotation();
int width = crop.width();
int height = crop.height();
- takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
+ saveScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
Insets.NONE, true);
}
- private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
+ private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, boolean showFlash) {
dismissScreenshot("new screenshot requested", true);
@@ -501,87 +593,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
mDismissAnimation.cancel();
}
- // Start the post-screenshot animation
- startAnimation(finisher, screenRect, screenInsets, showFlash);
- }
-
- void takeScreenshot(Consumer<Uri> finisher, Runnable onComplete) {
- mOnCompleteRunnable = onComplete;
-
- mDisplay.getRealMetrics(mDisplayMetrics);
- takeScreenshot(
- finisher,
- new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
- }
-
- void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
- Consumer<Uri> finisher, Runnable onComplete) {
- // TODO: use task Id, userId, topComponent for smart handler
-
- mOnCompleteRunnable = onComplete;
- if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
- takeScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
- } else {
- takeScreenshot(screenshot, finisher,
- new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
- true);
- }
- }
-
- /**
- * Displays a screenshot selector
- */
- @SuppressLint("ClickableViewAccessibility")
- void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
- dismissScreenshot("new screenshot requested", true);
- mOnCompleteRunnable = onComplete;
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- ScreenshotSelectorView view = (ScreenshotSelectorView) v;
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- view.startSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_MOVE:
- view.updateSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_UP:
- view.setVisibility(View.GONE);
- mWindowManager.removeView(mScreenshotLayout);
- final Rect rect = view.getSelectionRect();
- if (rect != null) {
- if (rect.width() != 0 && rect.height() != 0) {
- // Need mScreenshotLayout to handle it after the view disappears
- mScreenshotLayout.post(() -> takeScreenshot(finisher, rect));
- }
- }
+ // The window is focusable by default
+ setWindowFocusable(true);
- view.stopSelection();
- return true;
- }
-
- return false;
- }
- });
- mScreenshotLayout.post(() -> {
- mScreenshotSelectorView.setVisibility(View.VISIBLE);
- mScreenshotSelectorView.requestFocus();
- });
- }
-
- /**
- * Cancels screenshot request
- */
- void stopScreenshot() {
- // If the selector layer still presents on screen, we remove it and resets its state.
- if (mScreenshotSelectorView.getSelectionRect() != null) {
- mWindowManager.removeView(mScreenshotLayout);
- mScreenshotSelectorView.stopSelection();
- }
+ // Start the post-screenshot animation
+ startAnimation(finisher, screenRect, screenInsets, showFlash);
}
/**
@@ -614,55 +631,70 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
});
}
- private boolean isUserSetupComplete() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+ /**
+ * Starts the animation after taking the screenshot
+ */
+ private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
+ boolean showFlash) {
+ mScreenshotHandler.post(() -> {
+ if (!mScreenshotLayout.isAttachedToWindow()) {
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ }
+ mScreenshotAnimatedView.setImageDrawable(
+ createScreenDrawable(mScreenBitmap, screenInsets));
+ setAnimatedViewSize(screenRect.width(), screenRect.height());
+ // Show when the animation starts
+ mScreenshotAnimatedView.setVisibility(View.GONE);
+
+ mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
+ // make static preview invisible (from gone) so we can query its location on screen
+ mScreenshotPreview.setVisibility(View.INVISIBLE);
+
+ mScreenshotHandler.post(() -> {
+ mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+
+ mScreenshotAnimation =
+ createScreenshotDropInAnimation(screenRect, showFlash);
+
+ saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
+ @Override
+ void onActionsReady(SavedImageData imageData) {
+ showUiOnActionsReady(imageData);
+ }
+ });
+
+ // Play the shutter sound to notify that we've taken a screenshot
+ mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+
+ mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mScreenshotPreview.buildLayer();
+ mScreenshotAnimation.start();
+ });
+ });
}
/**
- * Clears current screenshot
+ * Creates a new worker thread and saves the screenshot to the media store.
*/
- void dismissScreenshot(String reason, boolean immediate) {
- Log.v(TAG, "clearing screenshot: " + reason);
- mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
- mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- if (!immediate) {
- mDismissAnimation = createScreenshotDismissAnimation();
- mDismissAnimation.addListener(new AnimatorListenerAdapter() {
+ private void saveScreenshotInWorkerThread(
+ Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
+ SaveImageInBackgroundData data = new SaveImageInBackgroundData();
+ data.image = mScreenBitmap;
+ data.finisher = finisher;
+ data.mActionsReadyListener = actionsReadyListener;
+
+ if (mSaveInBgTask != null) {
+ // just log success/failure for the pre-existing screenshot
+ mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
@Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- clearScreenshot();
+ void onActionsReady(SavedImageData imageData) {
+ logSuccessOnActionsReady(imageData);
}
});
- mDismissAnimation.start();
- } else {
- clearScreenshot();
}
- }
- private void clearScreenshot() {
- if (mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.removeView(mScreenshotLayout);
- }
-
- // Clear any references to the bitmap
- mScreenshotPreview.setImageDrawable(null);
- mScreenshotAnimatedView.setImageDrawable(null);
- mScreenshotAnimatedView.setVisibility(View.GONE);
- mActionsContainerBackground.setVisibility(View.GONE);
- mActionsContainer.setVisibility(View.GONE);
- mBackgroundProtection.setAlpha(0f);
- mDismissButton.setVisibility(View.GONE);
- mScreenshotPreview.setVisibility(View.GONE);
- mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
- mScreenshotPreview.setContentDescription(
- mContext.getResources().getString(R.string.screenshot_preview_description));
- mScreenshotLayout.setAlpha(1);
- mDismissButton.setTranslationY(0);
- mActionsContainer.setTranslationY(0);
- mActionsContainerBackground.setTranslationY(0);
- mScreenshotPreview.setTranslationY(0);
+ mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
+ mSaveInBgTask.execute();
}
/**
@@ -712,56 +744,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
}
- /**
- * Starts the animation after taking the screenshot
- */
- private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
- boolean showFlash) {
-
- // If power save is on, show a toast so there is some visual indication that a
- // screenshot has been taken.
- PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isPowerSaveMode()) {
- Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
- }
-
- mScreenshotHandler.post(() -> {
- if (!mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- }
- mScreenshotAnimatedView.setImageDrawable(
- createScreenDrawable(mScreenBitmap, screenInsets));
- setAnimatedViewSize(screenRect.width(), screenRect.height());
- // Show when the animation starts
- mScreenshotAnimatedView.setVisibility(View.GONE);
-
- mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
- // make static preview invisible (from gone) so we can query its location on screen
- mScreenshotPreview.setVisibility(View.INVISIBLE);
-
- mScreenshotHandler.post(() -> {
- mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-
- mScreenshotAnimation =
- createScreenshotDropInAnimation(screenRect, showFlash);
-
- saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
- @Override
- void onActionsReady(SavedImageData imageData) {
- showUiOnActionsReady(imageData);
- }
- });
-
- // Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-
- mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mScreenshotPreview.buildLayer();
- mScreenshotAnimation.start();
- });
- });
- }
-
private AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
Rect previewBounds = new Rect();
mScreenshotPreview.getBoundsOnScreen(previewBounds);
@@ -1014,6 +996,31 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
return animSet;
}
+ private void clearScreenshot() {
+ if (mScreenshotLayout.isAttachedToWindow()) {
+ mWindowManager.removeView(mScreenshotLayout);
+ }
+
+ // Clear any references to the bitmap
+ mScreenshotPreview.setImageDrawable(null);
+ mScreenshotAnimatedView.setImageDrawable(null);
+ mScreenshotAnimatedView.setVisibility(View.GONE);
+ mActionsContainerBackground.setVisibility(View.GONE);
+ mActionsContainer.setVisibility(View.GONE);
+ mBackgroundProtection.setAlpha(0f);
+ mDismissButton.setVisibility(View.GONE);
+ mScreenshotPreview.setVisibility(View.GONE);
+ mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
+ mScreenshotPreview.setContentDescription(
+ mContext.getResources().getString(R.string.screenshot_preview_description));
+ mScreenshotPreview.setOnClickListener(null);
+ mScreenshotLayout.setAlpha(1);
+ mDismissButton.setTranslationY(0);
+ mActionsContainer.setTranslationY(0);
+ mActionsContainerBackground.setTranslationY(0);
+ mScreenshotPreview.setTranslationY(0);
+ }
+
private void setAnimatedViewSize(int width, int height) {
ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams();
layoutParams.width = width;
@@ -1021,6 +1028,27 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotAnimatedView.setLayoutParams(layoutParams);
}
+ /**
+ * Updates the window focusability. If the window is already showing, then it updates the
+ * window immediately, otherwise the layout params will be applied when the window is next
+ * shown.
+ */
+ private void setWindowFocusable(boolean focusable) {
+ if (focusable) {
+ mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
+ } else {
+ mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
+ }
+ if (mScreenshotLayout.isAttachedToWindow()) {
+ mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
+ }
+ }
+
+ private boolean isUserSetupComplete() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+ }
+
/** Does the aspect ratio of the bitmap with insets removed match the bounds. */
private boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets, Rect screenBounds) {
int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
@@ -1072,121 +1100,10 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
// Are any of the insets negative, meaning the bitmap is smaller than the bounds so need
// to fill in the background of the drawable.
- return new LayerDrawable(new Drawable[] {
+ return new LayerDrawable(new Drawable[]{
new ColorDrawable(Color.BLACK), insetDrawable});
} else {
return insetDrawable;
}
}
-
- /**
- * Receiver to proxy the share or edit intent, used to clean up the notification and send
- * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
- */
- public static class ActionProxyReceiver extends BroadcastReceiver {
- static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
- private final StatusBar mStatusBar;
-
- @Inject
- public ActionProxyReceiver(Optional<Lazy<StatusBar>> statusBarLazy) {
- Lazy<StatusBar> statusBar = statusBarLazy.orElse(null);
- mStatusBar = statusBar != null ? statusBar.get() : null;
- }
-
- @Override
- public void onReceive(Context context, final Intent intent) {
- Runnable startActivityRunnable = () -> {
- try {
- ActivityManagerWrapper.getInstance().closeSystemWindows(
- SYSTEM_DIALOG_REASON_SCREENSHOT).get(
- CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException | InterruptedException | ExecutionException e) {
- Slog.e(TAG, "Unable to share screenshot", e);
- return;
- }
-
- PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
- if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
- ScreenshotNotificationsController.cancelScreenshotNotification(context);
- }
- ActivityOptions opts = ActivityOptions.makeBasic();
- opts.setDisallowEnterPictureInPictureWhileLaunching(
- intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
- try {
- actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent canceled", e);
- }
-
- };
-
- if (mStatusBar != null) {
- mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
- true /* dismissShade */, true /* afterKeyguardGone */,
- true /* deferred */);
- } else {
- startActivityRunnable.run();
- }
-
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
- ? ACTION_TYPE_EDIT
- : ACTION_TYPE_SHARE;
- ScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), actionType, false);
- }
- }
- }
-
- /**
- * Removes the notification for a screenshot after a share target is chosen.
- */
- public static class TargetChosenReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Clear the notification only after the user has chosen a share action
- ScreenshotNotificationsController.cancelScreenshotNotification(context);
- }
- }
-
- /**
- * Removes the last screenshot.
- */
- public static class DeleteScreenshotReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
- return;
- }
-
- // Clear the notification when the image is deleted
- ScreenshotNotificationsController.cancelScreenshotNotification(context);
-
- // And delete the image from the media store
- final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
- new DeleteImageInBackgroundTask(context).execute(uri);
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- ScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
- }
- }
- }
-
- /**
- * Executes the smart action tapped by the user in the notification.
- */
- public static class SmartActionsReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
- Intent actionIntent = pendingIntent.getIntent();
- String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
- Slog.d(TAG, "Executing smart action [" + actionType + "]:" + actionIntent);
- ActivityOptions opts = ActivityOptions.makeBasic();
- context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
-
- ScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), actionType, true);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e3fbdbc7c30d..df1d78953f46 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -81,6 +81,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
private final Context mContext;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
private final GlobalScreenshot.SaveImageInBackgroundData mParams;
private final GlobalScreenshot.SavedImageData mImageData;
private final String mImageFileName;
@@ -90,8 +91,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private final boolean mSmartActionsEnabled;
private final Random mRandom = new Random();
- SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
+ SaveImageInBackgroundTask(Context context, ScreenshotSmartActions screenshotSmartActions,
+ GlobalScreenshot.SaveImageInBackgroundData data) {
mContext = context;
+ mScreenshotSmartActions = screenshotSmartActions;
mImageData = new GlobalScreenshot.SavedImageData();
// Prepare all the output metadata
@@ -141,7 +144,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
mScreenshotId, uri, image, mSmartActionsProvider,
mSmartActionsEnabled, getUserHandle(mContext));
@@ -199,7 +202,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
1000);
smartActions.addAll(buildSmartActions(
- ScreenshotSmartActions.getSmartActions(
+ mScreenshotSmartActions.getSmartActions(
mScreenshotId, smartActionsFuture, timeoutMs,
mSmartActionsProvider),
mContext));
@@ -274,19 +277,18 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// by setting the (otherwise unused) request code to the current user id.
int requestCode = context.getUserId();
- PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
- new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
Intent sharingChooserIntent =
- Intent.createChooser(sharingIntent, null, chooserAction.getIntentSender())
+ Intent.createChooser(sharingIntent, null)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, requestCode,
- sharingChooserIntent, 0, null, UserHandle.CURRENT);
+
+ // cancel current pending intent (if any) since clipData isn't used for matching
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
+ sharingChooserIntent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
// Create a share action for the notification
PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ new Intent(context, ActionProxyReceiver.class)
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
.putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
@@ -331,10 +333,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Create a edit action
PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ new Intent(context, ActionProxyReceiver.class)
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
- .putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
- editIntent.getComponent() != null)
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
@@ -356,7 +356,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Create a delete action for the notification
PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
- new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+ new Intent(context, DeleteScreenshotReceiver.class)
.putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
@@ -396,7 +396,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
String actionType = extras.getString(
ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
- Intent intent = new Intent(context, GlobalScreenshot.SmartActionsReceiver.class)
+ Intent intent = new Intent(context, SmartActionsReceiver.class)
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index 442b373b31be..633cdd6ca5ca 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -39,14 +39,21 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Collects the static functions for retrieving and acting on smart actions.
*/
+@Singleton
public class ScreenshotSmartActions {
private static final String TAG = "ScreenshotSmartActions";
+ @Inject
+ public ScreenshotSmartActions() {}
+
@VisibleForTesting
- static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
+ CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
String screenshotId, Uri screenshotUri, Bitmap image,
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
boolean smartActionsEnabled, UserHandle userHandle) {
@@ -86,7 +93,7 @@ public class ScreenshotSmartActions {
}
@VisibleForTesting
- static List<Notification.Action> getSmartActions(String screenshotId,
+ List<Notification.Action> getSmartActions(String screenshotId,
CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
long startTimeMs = SystemClock.uptimeMillis();
@@ -116,7 +123,7 @@ public class ScreenshotSmartActions {
}
}
- static void notifyScreenshotOp(String screenshotId,
+ void notifyScreenshotOp(String screenshotId,
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
@@ -127,7 +134,7 @@ public class ScreenshotSmartActions {
}
}
- static void notifyScreenshotAction(Context context, String screenshotId, String action,
+ void notifyScreenshotAction(Context context, String screenshotId, String action,
boolean isSmartAction) {
try {
ScreenshotNotificationSmartActionsProvider provider =
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
new file mode 100644
index 000000000000..217235b16ecf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.util.Slog;
+
+import javax.inject.Inject;
+
+
+/**
+ * Executes the smart action tapped by the user in the notification.
+ */
+public class SmartActionsReceiver extends BroadcastReceiver {
+ private static final String TAG = "SmartActionsReceiver";
+
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+
+ @Inject
+ SmartActionsReceiver(ScreenshotSmartActions screenshotSmartActions) {
+ mScreenshotSmartActions = screenshotSmartActions;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+ String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
+ Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
+ ActivityOptions opts = ActivityOptions.makeBasic();
+
+ try {
+ pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent canceled", e);
+ }
+
+ mScreenshotSmartActions.notifyScreenshotAction(
+ context, intent.getStringExtra(EXTRA_ID), actionType, true);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 9f8a9bb4a432..a043f0f1e50c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -61,7 +61,7 @@ public class TakeScreenshotService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) {
- mScreenshot.dismissScreenshot("close system dialogs", true);
+ mScreenshot.dismissScreenshot("close system dialogs", false);
}
}
};
@@ -102,7 +102,7 @@ public class TakeScreenshotService extends Service {
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
- mScreenshot.takeScreenshot(uriConsumer, onComplete);
+ mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 8a3819925f30..eb7231211ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -39,16 +39,16 @@ import android.window.WindowOrganizer;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.TransactionPool;
import com.android.systemui.recents.Recents;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.DisplayLayout;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -193,10 +193,13 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
@Override
public void onKeyguardShowingChanged() {
- if (!isDividerVisible() || mView == null) {
+ if (!isSplitActive() || mView == null) {
return;
}
mView.setHidden(mKeyguardStateController.isShowing());
+ if (!mKeyguardStateController.isShowing()) {
+ mImePositionProcessor.updateAdjustForIme();
+ }
}
@Override
@@ -285,8 +288,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
* while this only cares if some things are (eg. while entering/exiting as well).
*/
private boolean isSplitActive() {
- return mSplits.mPrimary.topActivityType != ACTIVITY_TYPE_UNDEFINED
- || mSplits.mSecondary.topActivityType != ACTIVITY_TYPE_UNDEFINED;
+ return mSplits.mPrimary != null && mSplits.mSecondary != null
+ && (mSplits.mPrimary.topActivityType != ACTIVITY_TYPE_UNDEFINED
+ || mSplits.mSecondary.topActivityType != ACTIVITY_TYPE_UNDEFINED);
}
private void addDivider(Configuration configuration) {
@@ -314,7 +318,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
}
private void update(Configuration configuration) {
- final boolean isDividerHidden = mView != null && mView.isHidden();
+ final boolean isDividerHidden = mView != null && mKeyguardStateController.isShowing();
removeDivider();
addDivider(configuration);
@@ -527,6 +531,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
updateVisibility(false /* visible */);
mMinimized = false;
removeDivider();
+ mImePositionProcessor.reset();
}
void ensureMinimizedSplit() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
index 47c8c0ad8a4e..84ec38744e98 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
@@ -33,8 +33,8 @@ import android.window.WindowOrganizer;
import androidx.annotation.Nullable;
-import com.android.systemui.TransactionPool;
-import com.android.systemui.wm.DisplayImeController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.TransactionPool;
class DividerImeController implements DisplayImeController.ImePositionProcessor {
private static final String TAG = "DividerImeController";
@@ -91,6 +91,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
private boolean mPaused = true;
private boolean mPausedTargetAdjusted = false;
+ private boolean mAdjustedWhileHidden = false;
DividerImeController(SplitScreenTaskOrganizer splits, TransactionPool pool, Handler handler) {
mSplits = splits;
@@ -116,6 +117,18 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
&& (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder());
}
+ void reset() {
+ mPaused = true;
+ mPausedTargetAdjusted = false;
+ mAdjustedWhileHidden = false;
+ mAnimation = null;
+ mAdjusted = mTargetAdjusted = false;
+ mImeWasShown = mTargetShown = false;
+ mTargetPrimaryDim = mTargetSecondaryDim = mLastPrimaryDim = mLastSecondaryDim = 0.f;
+ mSecondaryHasFocus = false;
+ mLastAdjustTop = -1;
+ }
+
private void updateDimTargets() {
final boolean splitIsVisible = !getView().isHidden();
mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible)
@@ -125,18 +138,20 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
}
@Override
- public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
- boolean imeShouldShow, SurfaceControl.Transaction t) {
+ @ImeAnimationFlags
+ public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
+ boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) {
mHiddenTop = hiddenTop;
mShownTop = shownTop;
mTargetShown = imeShouldShow;
if (!isDividerVisible()) {
- return;
+ return 0;
}
final boolean splitIsVisible = !getView().isHidden();
mSecondaryHasFocus = getSecondaryHasFocus(displayId);
final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
- && !getLayout().mDisplayLayout.isLandscape() && !mSplits.mDivider.isMinimized();
+ && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
+ && !mSplits.mDivider.isMinimized();
if (mLastAdjustTop < 0) {
mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
} else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) {
@@ -154,7 +169,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
if (mPaused) {
mPausedTargetAdjusted = targetAdjusted;
if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState());
- return;
+ return (targetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0;
}
mTargetAdjusted = targetAdjusted;
updateDimTargets();
@@ -170,11 +185,18 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
// If split is hidden, we don't want to trigger any relayouts that would cause the
// divider to show again.
updateImeAdjustState();
+ } else {
+ mAdjustedWhileHidden = true;
}
+ return (mTargetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0;
}
private void updateImeAdjustState() {
- if (mAdjusted != mTargetAdjusted) {
+ updateImeAdjustState(false /* force */);
+ }
+
+ private void updateImeAdjustState(boolean force) {
+ if (mAdjusted != mTargetAdjusted || force) {
// Reposition the server's secondary split position so that it evaluates
// insets properly.
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -231,6 +253,11 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
mSplits.mDivider.setAdjustedForIme(mTargetShown && !mPaused);
}
+ public void updateAdjustForIme() {
+ updateImeAdjustState(mAdjustedWhileHidden);
+ mAdjustedWhileHidden = false;
+ }
+
@Override
public void onImePositionChanged(int displayId, int imeTop,
SurfaceControl.Transaction t) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
index 3b7f3152ec76..c24431c22d62 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -19,13 +19,13 @@ package com.android.systemui.stackdivider;
import android.content.Context;
import android.os.Handler;
-import com.android.systemui.TransactionPool;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 6f554e698c58..b6c6afd523b3 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -675,6 +675,9 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
private void notifySplitScreenBoundsChanged() {
+ if (mSplitLayout.mPrimary == null || mSplitLayout.mSecondary == null) {
+ return;
+ }
mOtherTaskRect.set(mSplitLayout.mSecondary);
mTmpRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), mHandle.getBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 6ea3132ac942..d869333e11a7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -32,7 +32,7 @@ import android.os.Binder;
import android.view.View;
import android.view.WindowManager;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.SystemWindows;
/**
* Manages the window parameters of the docked stack divider.
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
index 69095f7538c5..a34e85517953 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
@@ -34,7 +34,7 @@ import android.window.WindowContainerTransaction;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.wm.DisplayLayout;
+import com.android.wm.shell.common.DisplayLayout;
/**
* Handles split-screen related internal display layout. In general, this represents the
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index 4a2cad705c17..7a313dc0622b 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -102,10 +102,14 @@ class SplitScreenTaskOrganizer extends TaskOrganizer {
// Initialize dim surfaces:
mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession)
.setParent(mPrimarySurface).setColorLayer()
- .setName("Primary Divider Dim").build();
+ .setName("Primary Divider Dim")
+ .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
+ .build();
mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession)
.setParent(mSecondarySurface).setColorLayer()
- .setName("Secondary Divider Dim").build();
+ .setName("Secondary Divider Dim")
+ .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
+ .build();
SurfaceControl.Transaction t = getTransaction();
t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
index 1ff404677ea6..6812f62422a7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
@@ -25,7 +25,7 @@ import android.window.WindowOrganizer;
import androidx.annotation.NonNull;
-import com.android.systemui.TransactionPool;
+import com.android.wm.shell.common.TransactionPool;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 410e3dd39a0b..2b3681281064 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -40,7 +40,7 @@ import android.window.WindowContainerTransaction;
import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.TransactionPool;
+import com.android.wm.shell.common.TransactionPool;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index b846aa08c33b..eca4c8082dfe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -316,7 +316,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
* of the timer and should be removed externally.
* @return true if the notification is sticky
*/
- protected boolean isSticky() {
+ public boolean isSticky() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 8c24c540e743..2638d28733e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -154,7 +154,7 @@ public class NavigationBarController implements Callbacks {
Dependency.get(IWindowManager.class));
navBar.setAutoHideController(autoHideController);
navBar.restoreAppearanceAndTransientState();
- mNavigationBars.append(displayId, navBar);
+ mNavigationBars.put(displayId, navBar);
if (result != null) {
navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 5628a24f40ef..739d30c2a707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -100,12 +100,7 @@ public class NotificationMediaManager implements Dumpable {
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_STOPPED);
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_PAUSED);
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_ERROR);
- }
- private static final HashSet<Integer> INACTIVE_MEDIA_STATES = new HashSet<>();
- static {
- INACTIVE_MEDIA_STATES.add(PlaybackState.STATE_NONE);
- INACTIVE_MEDIA_STATES.add(PlaybackState.STATE_STOPPED);
- INACTIVE_MEDIA_STATES.add(PlaybackState.STATE_ERROR);
+ PAUSED_MEDIA_STATES.add(PlaybackState.STATE_CONNECTING);
}
private final NotificationEntryManager mEntryManager;
@@ -262,15 +257,6 @@ public class NotificationMediaManager implements Dumpable {
return !PAUSED_MEDIA_STATES.contains(state);
}
- /**
- * Check if a state should be considered active (playing or paused)
- * @param state a PlaybackState
- * @return true if active
- */
- public static boolean isActiveState(int state) {
- return !INACTIVE_MEDIA_STATES.contains(state);
- }
-
public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 710ac9e03355..6b023c07b1a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -181,11 +181,11 @@ public class NotificationRemoteInputManager implements Dumpable {
return;
}
ViewParent parent = view.getParent();
- StatusBarNotification statusBarNotification = entry.getSbn();
- if (statusBarNotification == null) {
+ if (entry == null) {
Log.w(TAG, "Couldn't determine notification for click.");
return;
}
+ StatusBarNotification statusBarNotification = entry.getSbn();
String key = statusBarNotification.getKey();
int buttonIndex = -1;
// If this is a default template, determine the index of the button.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index d3819e5faa36..0445c9879ac5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -84,6 +84,7 @@ class NotificationShadeDepthController @Inject constructor(
private var isClosed: Boolean = true
private var isOpen: Boolean = false
private var isBlurred: Boolean = false
+ private var listeners = mutableListOf<DepthListener>()
private var prevTracking: Boolean = false
private var prevTimestamp: Long = -1
@@ -187,12 +188,15 @@ class NotificationShadeDepthController @Inject constructor(
}
blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
+ val zoomOut = blurUtils.ratioOfBlurRadius(blur)
try {
- wallpaperManager.setWallpaperZoomOut(root.windowToken,
- blurUtils.ratioOfBlurRadius(blur))
+ wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut)
} catch (e: IllegalArgumentException) {
Log.w(TAG, "Can't set zoom. Window is gone: ${root.windowToken}", e)
}
+ listeners.forEach {
+ it.onWallpaperZoomOutChanged(zoomOut)
+ }
notificationShadeWindowController.setBackgroundBlurRadius(blur)
}
@@ -271,6 +275,14 @@ class NotificationShadeDepthController @Inject constructor(
shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
}
+ fun addListener(listener: DepthListener) {
+ listeners.add(listener)
+ }
+
+ fun removeListener(listener: DepthListener) {
+ listeners.remove(listener)
+ }
+
/**
* Update blurs when pulling down the shade
*/
@@ -482,4 +494,14 @@ class NotificationShadeDepthController @Inject constructor(
springAnimation.setStartVelocity(velocity)
}
}
+
+ /**
+ * Invoked when changes are needed in z-space
+ */
+ interface DepthListener {
+ /**
+ * Current wallpaper zoom out, where 0 is the closest, and 1 the farthest
+ */
+ fun onWallpaperZoomOutChanged(zoomOut: Float)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 9abc66056452..aba9e1005559 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,18 +24,24 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
@@ -52,6 +58,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.Assert;
import com.android.systemui.util.leak.LeakDetector;
@@ -127,6 +134,8 @@ public class NotificationEntryManager implements
private final NotificationEntryManagerLogger mLogger;
+ private final IStatusBarService mStatusBarService;
+
// Lazily retrieved dependencies
private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
@@ -138,6 +147,8 @@ public class NotificationEntryManager implements
private final NotificationRankingManager mRankingManager;
private final FeatureFlags mFeatureFlags;
private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
+ private final HeadsUpManager mHeadsUpManager;
+ private final StatusBarStateController mStatusBarStateController;
private NotificationPresenter mPresenter;
private RankingMap mLatestRankingMap;
@@ -201,7 +212,10 @@ public class NotificationEntryManager implements
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController) {
+ ForegroundServiceDismissalFeatureController fgsFeatureController,
+ HeadsUpManager headsUpManager,
+ StatusBarStateController statusBarStateController
+ ) {
mLogger = logger;
mGroupManager = groupManager;
mRankingManager = rankingManager;
@@ -211,6 +225,11 @@ public class NotificationEntryManager implements
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
mLeakDetector = leakDetector;
mFgsFeatureController = fgsFeatureController;
+ mHeadsUpManager = headsUpManager;
+ mStatusBarStateController = statusBarStateController;
+
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
}
/** Once called, the NEM will start processing notification events from system server. */
@@ -496,6 +515,9 @@ public class NotificationEntryManager implements
removedByUser |= entryDismissed;
mLogger.logNotifRemoved(entry.getKey(), removedByUser);
+ if (removedByUser && visibility != null) {
+ sendNotificationRemovalToServer(entry.getKey(), entry.getSbn(), visibility);
+ }
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onEntryRemoved(entry, visibility, removedByUser, reason);
}
@@ -511,6 +533,36 @@ public class NotificationEntryManager implements
}
}
+ private void sendNotificationRemovalToServer(
+ String key,
+ StatusBarNotification notification,
+ NotificationVisibility nv) {
+ final String pkg = notification.getPackageName();
+ final String tag = notification.getTag();
+ final int id = notification.getId();
+ final int userId = notification.getUser().getIdentifier();
+ try {
+ int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+ if (mHeadsUpManager.isAlerting(key)) {
+ dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+ } else if (mStatusBarStateController.isDozing()) {
+ dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ }
+ int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+ mStatusBarService.onNotificationClear(
+ pkg,
+ tag,
+ id,
+ userId,
+ notification.getKey(),
+ dismissalSurface,
+ dismissalSentiment,
+ nv);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ }
+
/**
* Ensures that the group children are cancelled immediately when the group summary is cancelled
* instead of waiting for the notification manager to send all cancels. Otherwise this could
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 27476964b9af..f982cf05de97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -382,7 +382,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
private fun shouldAnimateVisibility() =
- dozeParameters.getAlwaysOn() && !dozeParameters.getDisplayNeedsBlanking()
+ dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
interface WakeUpListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index c1acfbadef45..285cf7abce20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -382,9 +382,8 @@ public class NotifCollection implements Dumpable {
final NotificationEntry entry = mNotificationSet.get(sbn.getKey());
if (entry == null) {
- crashIfNotInitializing(
- new IllegalStateException("No notification to remove with key "
- + sbn.getKey()));
+ // TODO (b/160008901): Throw an exception here
+ mLogger.logNoNotificationToRemoveWithKey(sbn.getKey());
return;
}
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 423f85f2ddd0..bd65ef06f3a9 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
@@ -282,7 +282,7 @@ public final class NotificationEntry extends ListEntry {
+ " doesn't match existing key " + mKey);
}
- mRanking = ranking;
+ mRanking = ranking.withAudiblyAlertedInfo(mRanking);
}
/*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 9d81d3563ebb..a86ab41141a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -842,7 +842,7 @@ public class ShadeListBuilder implements Dumpable {
}
private static final NotifSection sDefaultSection =
- new NotifSection("DefaultSection") {
+ new NotifSection("UnknownSection") {
@Override
public boolean isInSection(ListEntry entry) {
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
index 4b244bb18975..68ec6b620a53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
import android.app.Notification;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -24,9 +26,11 @@ import android.util.ArraySet;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.Assert;
@@ -43,6 +47,8 @@ import javax.inject.Singleton;
* Tags notifications with appOps
* Lifetime extends notifications associated with an ongoing ForegroundService.
* Filters out notifications that represent foreground services that are no longer running
+ * Puts foreground service notifications into the FGS section. See {@link NotifCoordinators} for
+ * section ordering priority.
*
* Previously this logic lived in
* frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceController
@@ -86,6 +92,10 @@ public class AppOpsCoordinator implements Coordinator {
mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
}
+ public NotifSection getSection() {
+ return mNotifSection;
+ }
+
/**
* Filters out notifications that represent foreground services that are no longer running or
* that already have an app notification with the appOps tagged to
@@ -204,6 +214,23 @@ public class AppOpsCoordinator implements Coordinator {
}
};
+ /**
+ * Puts foreground service notifications into its own section.
+ */
+ private final NotifSection mNotifSection = new NotifSection("ForegroundService") {
+ @Override
+ public boolean isInSection(ListEntry entry) {
+ NotificationEntry notificationEntry = entry.getRepresentativeEntry();
+ if (notificationEntry != null) {
+ Notification notification = notificationEntry.getSbn().getNotification();
+ return notification.isForegroundService()
+ && notification.isColorized()
+ && entry.getRepresentativeEntry().getImportance() > IMPORTANCE_MIN;
+ }
+ return false;
+ }
+ };
+
private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index 1bac938a9fca..1a9de8829faf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -16,17 +16,25 @@
package com.android.systemui.statusbar.notification.collection.coordinator
+import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import javax.inject.Inject
import javax.inject.Singleton
/**
- * A coordinator that elevates important conversation notifications
+ * A Conversation/People Coordinator that:
+ * - Elevates important conversation notifications
+ * - Puts conversations into its own people section. @see [NotifCoordinators] for section ordering.
*/
@Singleton
-class ConversationCoordinator @Inject constructor() : Coordinator {
+class ConversationCoordinator @Inject constructor(
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier
+) : Coordinator {
private val notificationPromoter = object : NotifPromoter(TAG) {
override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean {
@@ -34,10 +42,24 @@ class ConversationCoordinator @Inject constructor() : Coordinator {
}
}
+ private val mNotifSection: NotifSection = object : NotifSection("People") {
+ override fun isInSection(entry: ListEntry): Boolean {
+ return isConversation(entry.representativeEntry!!)
+ }
+ }
+
override fun attach(pipeline: NotifPipeline) {
pipeline.addPromoter(notificationPromoter)
}
+ fun getSection(): NotifSection {
+ return mNotifSection
+ }
+
+ private fun isConversation(entry: NotificationEntry): Boolean =
+ peopleNotificationIdentifier.getPeopleNotificationType(entry.sbn, entry.ranking) !=
+ TYPE_NON_PERSON
+
companion object {
private const val TAG = "ConversationCoordinator"
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
index d8b2e4089f30..c1a11b2f64c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
/**
@@ -29,8 +28,4 @@ public interface Coordinator {
* Coordinators should register their listeners and {@link Pluggable}s to the pipeline.
*/
void attach(NotifPipeline pipeline);
-
- default NotifSection getSection() {
- return null;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index 3fde2ed249d9..72597afc3b90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -88,7 +88,6 @@ public class HeadsUpCoordinator implements Coordinator {
pipeline.addNotificationLifetimeExtender(mLifetimeExtender);
}
- @Override
public NotifSection getSection() {
return mNotifSection;
}
@@ -192,7 +191,7 @@ public class HeadsUpCoordinator implements Coordinator {
}
};
- private final NotifSection mNotifSection = new NotifSection(TAG) {
+ private final NotifSection mNotifSection = new NotifSection("HeadsUp") {
@Override
public boolean isInSection(ListEntry entry) {
return isCurrentlyShowingHun(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 99e822c66a8f..a09c6509e65e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -60,6 +60,7 @@ public class NotifCoordinators implements Dumpable {
PreparationCoordinator preparationCoordinator,
MediaCoordinator mediaCoordinator) {
dumpManager.registerDumpable(TAG, this);
+
mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
mCoordinators.add(hideNotifsForOtherUsersCoordinator);
mCoordinators.add(keyguardCoordinator);
@@ -67,20 +68,22 @@ public class NotifCoordinators implements Dumpable {
mCoordinators.add(appOpsCoordinator);
mCoordinators.add(deviceProvisionedCoordinator);
mCoordinators.add(bubbleCoordinator);
+ mCoordinators.add(mediaCoordinator);
+ mCoordinators.add(conversationCoordinator);
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
- mCoordinators.add(conversationCoordinator);
mCoordinators.add(headsUpCoordinator);
mCoordinators.add(preparationCoordinator);
}
- // TODO: add new Coordinators here! (b/112656837)
- mCoordinators.add(mediaCoordinator);
- // TODO: add the sections in a particular ORDER (HeadsUp < People < Alerting)
- for (Coordinator c : mCoordinators) {
- if (c.getSection() != null) {
- mOrderedSections.add(c.getSection());
- }
+ // Manually add Ordered Sections
+ // HeadsUp > FGS > People > Alerting > Silent > Unknown/Default
+ if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mOrderedSections.add(headsUpCoordinator.getSection()); // HeadsUp
}
+ mOrderedSections.add(appOpsCoordinator.getSection()); // ForegroundService
+ mOrderedSections.add(conversationCoordinator.getSection()); // People
+ mOrderedSections.add(rankingCoordinator.getAlertingSection()); // Alerting
+ mOrderedSections.add(rankingCoordinator.getSilentSection()); // Silent
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index e9cbf32ee052..0d2f9da77db7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -17,15 +17,19 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Filters out NotificationEntries based on its Ranking and dozing state.
+ * Assigns alerting / silent section based on the importance of the notification entry.
* We check the NotificationEntry's Ranking for:
* - whether the notification's app is suspended or hiding its notifications
* - whether DND settings are hiding notifications from ambient display or the notification list
@@ -35,10 +39,14 @@ public class RankingCoordinator implements Coordinator {
private static final String TAG = "RankingNotificationCoordinator";
private final StatusBarStateController mStatusBarStateController;
+ private final HighPriorityProvider mHighPriorityProvider;
@Inject
- public RankingCoordinator(StatusBarStateController statusBarStateController) {
+ public RankingCoordinator(
+ StatusBarStateController statusBarStateController,
+ HighPriorityProvider highPriorityProvider) {
mStatusBarStateController = statusBarStateController;
+ mHighPriorityProvider = highPriorityProvider;
}
@Override
@@ -49,6 +57,28 @@ public class RankingCoordinator implements Coordinator {
pipeline.addPreGroupFilter(mDozingFilter);
}
+ public NotifSection getAlertingSection() {
+ return mAlertingNotifSection;
+ }
+
+ public NotifSection getSilentSection() {
+ return mSilentNotifSection;
+ }
+
+ private final NotifSection mAlertingNotifSection = new NotifSection("Alerting") {
+ @Override
+ public boolean isInSection(ListEntry entry) {
+ return mHighPriorityProvider.isHighPriority(entry);
+ }
+ };
+
+ private final NotifSection mSilentNotifSection = new NotifSection("Silent") {
+ @Override
+ public boolean isInSection(ListEntry entry) {
+ return !mHighPriorityProvider.isHighPriority(entry);
+ }
+ };
+
/**
* Checks whether to filter out the given notification based the notification's Ranking object.
* NotifListBuilder invalidates the notification list each time the ranking is updated,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 673aa3903156..85a3bc91dc7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -142,7 +142,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
.expandableNotificationRow(row)
.notificationEntry(entry)
.onDismissRunnable(onDismissRunnable)
- .rowContentBindStage(mRowContentBindStage)
.onExpandClickListener(mPresenter)
.build();
ExpandableNotificationRowController rowController =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 76751eaaecb1..f8a778d6b1d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -121,6 +121,14 @@ class NotifCollectionLogger @Inject constructor(
})
}
+ fun logNoNotificationToRemoveWithKey(key: String) {
+ buffer.log(TAG, ERROR, {
+ str1 = key
+ }, {
+ "No notification to remove with key $str1"
+ })
+ }
+
fun logRankingMissing(key: String, rankingMap: RankingMap) {
buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index d2c202cb485f..d661b5e2b7cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -59,6 +59,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
import java.util.concurrent.Executor;
@@ -88,7 +89,9 @@ public interface NotificationsModule {
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController) {
+ ForegroundServiceDismissalFeatureController fgsFeatureController,
+ HeadsUpManager headsUpManager,
+ StatusBarStateController statusBarStateController) {
return new NotificationEntryManager(
logger,
groupManager,
@@ -98,7 +101,9 @@ public interface NotificationsModule {
notificationRowBinderLazy,
notificationRemoteInputManagerLazy,
leakDetector,
- fgsFeatureController);
+ fgsFeatureController,
+ headsUpManager,
+ statusBarStateController);
}
/** Provides an instance of {@link NotificationGutsManager} */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index bd0d0b31e4dc..4441270f895b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -21,7 +21,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -44,7 +43,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.Collection;
import java.util.Collections;
@@ -74,7 +72,6 @@ public class NotificationLogger implements StateListener {
private final Executor mUiBgExecutor;
private final NotificationEntryManager mEntryManager;
private final NotificationPanelLogger mNotificationPanelLogger;
- private HeadsUpManager mHeadsUpManager;
private final ExpansionStateLogger mExpansionStateLogger;
protected Handler mHandler = new Handler();
@@ -226,9 +223,6 @@ public class NotificationLogger implements StateListener {
NotificationVisibility visibility,
boolean removedByUser,
int reason) {
- if (removedByUser && visibility != null) {
- logNotificationClear(entry.getKey(), entry.getSbn(), visibility);
- }
mExpansionStateLogger.onEntryRemoved(entry.getKey());
}
@@ -250,10 +244,6 @@ public class NotificationLogger implements StateListener {
mListContainer = listContainer;
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
public void stopNotificationLogging() {
if (mLogging) {
mLogging = false;
@@ -296,30 +286,6 @@ public class NotificationLogger implements StateListener {
}
}
- // TODO: This method has side effects, it is NOT just logging that a notification
- // was cleared, it also actually removes the notification
- private void logNotificationClear(String key, StatusBarNotification notification,
- NotificationVisibility nv) {
- final String pkg = notification.getPackageName();
- final String tag = notification.getTag();
- final int id = notification.getId();
- final int userId = notification.getUserId();
- try {
- int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (mHeadsUpManager.isAlerting(key)) {
- dismissalSurface = NotificationStats.DISMISSAL_PEEK;
- } else if (mListContainer.hasPulsingNotifications()) {
- dismissalSurface = NotificationStats.DISMISSAL_AOD;
- }
- int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
- mBarService.onNotificationClear(pkg, tag, id, userId, notification.getKey(),
- dismissalSurface,
- dismissalSentiment, nv);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- }
-
/**
* Logs Notification inflation error
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 26ccd721460e..bb78f6168d28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -77,6 +77,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
protected boolean mShouldTranslateContents;
private boolean mTopAmountRounded;
private float mDistanceToTopRoundness = -1;
+ private float[] mTmpCornerRadii = new float[8];
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@Override
@@ -138,38 +139,22 @@ public abstract class ExpandableOutlineView extends ExpandableView {
bottomRoundness -= overShoot * mCurrentBottomRoundness
/ (mCurrentTopRoundness + mCurrentBottomRoundness);
}
- getRoundedRectPath(left, top, right, bottom, topRoundness,
- bottomRoundness, mTmpPath);
+ getRoundedRectPath(left, top, right, bottom, topRoundness, bottomRoundness, mTmpPath);
return mTmpPath;
}
- public static void getRoundedRectPath(int left, int top, int right, int bottom,
+ public void getRoundedRectPath(int left, int top, int right, int bottom,
float topRoundness, float bottomRoundness, Path outPath) {
outPath.reset();
- int width = right - left;
- float topRoundnessX = topRoundness;
- float bottomRoundnessX = bottomRoundness;
- topRoundnessX = Math.min(width / 2, topRoundnessX);
- bottomRoundnessX = Math.min(width / 2, bottomRoundnessX);
- if (topRoundness > 0.0f) {
- outPath.moveTo(left, top + topRoundness);
- outPath.quadTo(left, top, left + topRoundnessX, top);
- outPath.lineTo(right - topRoundnessX, top);
- outPath.quadTo(right, top, right, top + topRoundness);
- } else {
- outPath.moveTo(left, top);
- outPath.lineTo(right, top);
- }
- if (bottomRoundness > 0.0f) {
- outPath.lineTo(right, bottom - bottomRoundness);
- outPath.quadTo(right, bottom, right - bottomRoundnessX, bottom);
- outPath.lineTo(left + bottomRoundnessX, bottom);
- outPath.quadTo(left, bottom, left, bottom - bottomRoundness);
- } else {
- outPath.lineTo(right, bottom);
- outPath.lineTo(left, bottom);
- }
- outPath.close();
+ mTmpCornerRadii[0] = topRoundness;
+ mTmpCornerRadii[1] = topRoundness;
+ mTmpCornerRadii[2] = topRoundness;
+ mTmpCornerRadii[3] = topRoundness;
+ mTmpCornerRadii[4] = bottomRoundness;
+ mTmpCornerRadii[5] = bottomRoundness;
+ mTmpCornerRadii[6] = bottomRoundness;
+ mTmpCornerRadii[7] = bottomRoundness;
+ outPath.addRoundRect(left, top, right, bottom, mTmpCornerRadii, Path.Direction.CW);
}
public ExpandableOutlineView(Context context, AttributeSet attrs) {
@@ -188,9 +173,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
int right = getWidth() + (int) (mExtraWidthForClipping + left);
int bottom = (int) Math.max(mMinimumHeightForClipping,
Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius));
- ExpandableOutlineView.getRoundedRectPath(left, top, right, bottom, mOutlineRadius,
- 0.0f,
- mClipPath);
+ getRoundedRectPath(left, top, right, bottom, mOutlineRadius, 0.0f, mClipPath);
intersectPath = mClipPath;
}
boolean clipped = false;
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 582e3e5b6c34..a7d83b3b2774 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
@@ -37,6 +37,8 @@ import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.MediaDataManagerKt;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
@@ -71,6 +73,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
public static final String TAG = "NotifContentInflater";
private boolean mInflateSynchronously = false;
+ private final boolean mIsMediaInQS;
private final NotificationRemoteInputManager mRemoteInputManager;
private final NotifRemoteViewCache mRemoteViewCache;
private final Lazy<SmartReplyConstants> mSmartReplyConstants;
@@ -85,12 +88,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
Lazy<SmartReplyConstants> smartReplyConstants,
Lazy<SmartReplyController> smartReplyController,
ConversationNotificationProcessor conversationProcessor,
+ MediaFeatureFlag mediaFeatureFlag,
@Background Executor bgExecutor) {
mRemoteViewCache = remoteViewCache;
mRemoteInputManager = remoteInputManager;
mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController;
mConversationProcessor = conversationProcessor;
+ mIsMediaInQS = mediaFeatureFlag.getEnabled();
mBgExecutor = bgExecutor;
}
@@ -135,7 +140,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
bindParams.usesIncreasedHeight,
bindParams.usesIncreasedHeadsUpHeight,
callback,
- mRemoteInputManager.getRemoteViewsOnClickHandler());
+ mRemoteInputManager.getRemoteViewsOnClickHandler(),
+ mIsMediaInQS);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
@@ -711,6 +717,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private CancellationSignal mCancellationSignal;
private final ConversationNotificationProcessor mConversationProcessor;
+ private final boolean mIsMediaInQS;
private AsyncInflationTask(
Executor bgExecutor,
@@ -726,7 +733,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight,
InflationCallback callback,
- RemoteViews.OnClickHandler remoteViewClickHandler) {
+ RemoteViews.OnClickHandler remoteViewClickHandler,
+ boolean isMediaFlagEnabled) {
mEntry = entry;
mRow = row;
mSmartReplyConstants = smartReplyConstants;
@@ -742,6 +750,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
mConversationProcessor = conversationProcessor;
+ mIsMediaInQS = isMediaFlagEnabled;
entry.setInflationTask(this);
}
@@ -765,7 +774,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
packageContext = new RtlEnabledContext(packageContext);
}
Notification notification = sbn.getNotification();
- if (notification.isMediaNotification()) {
+ if (notification.isMediaNotification() && !(mIsMediaInQS
+ && MediaDataManagerKt.isMediaNotification(sbn))) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
processor.processNotification(notification, recoveredBuilder);
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 fa40ba6eee2e..2986b9b75b98 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
@@ -661,6 +661,14 @@ public class NotificationContentView extends FrameLayout {
private void updateContentTransformation() {
int visibleType = calculateVisibleType();
+ if (getTransformableViewForVisibleType(mVisibleType) == null) {
+ // Case where visible view was removed in middle of transformation. In this case, we
+ // just update immediately to the appropriate view.
+ mVisibleType = visibleType;
+ updateViewVisibilities(visibleType);
+ updateBackgroundColor(false);
+ return;
+ }
if (visibleType != mVisibleType) {
// A new transformation starts
mTransformationStartVisibleType = mVisibleType;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index 52f7c2cfee96..7bd192d850c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -124,6 +124,9 @@ public class NotificationInlineImageResolver implements ImageResolver {
*/
Drawable resolveImage(Uri uri) throws IOException {
BitmapDrawable image = resolveImageInternal(uri);
+ if (image == null || image.getBitmap() == null) {
+ throw new IOException("resolveImageInternal returned null for uri: " + uri);
+ }
Bitmap bitmap = image.getBitmap();
image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight));
return image;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index cdcfac87f1c8..d264af94947d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -84,7 +84,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
private final Map<View, MenuItem> mMenuItemsByView = new ArrayMap<>();
private OnMenuEventListener mMenuListener;
private boolean mDismissRtl;
- private boolean mIsForeground;
private ValueAnimator mFadeAnimator;
private boolean mAnimating;
@@ -191,9 +190,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
@Override
public void createMenu(ViewGroup parent, StatusBarNotification sbn) {
mParent = (ExpandableNotificationRow) parent;
- createMenuViews(true /* resetState */,
- sbn != null && (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)
- != 0);
+ createMenuViews(true /* resetState */);
}
@Override
@@ -237,8 +234,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
// Menu hasn't been created yet, no need to do anything.
return;
}
- createMenuViews(!isMenuVisible() /* resetState */,
- (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0);
+ createMenuViews(!isMenuVisible() /* resetState */);
}
@Override
@@ -253,9 +249,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
mParent.removeListener();
}
- private void createMenuViews(boolean resetState, final boolean isForeground) {
- mIsForeground = isForeground;
-
+ private void createMenuViews(boolean resetState) {
final Resources res = mContext.getResources();
mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
@@ -266,7 +260,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
SHOW_NOTIFICATION_SNOOZE, 0) == 1;
// Construct the menu items based on the notification
- if (!isForeground && showSnooze) {
+ if (showSnooze) {
// Only show snooze for non-foreground notifications, and if the setting is on
mSnoozeItem = createSnoozeItem(mContext);
}
@@ -283,7 +277,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
mInfoItem = createInfoItem(mContext);
}
- if (!isForeground && showSnooze) {
+ if (showSnooze) {
mRightMenuItems.add(mSnoozeItem);
}
mRightMenuItems.add(mInfoItem);
@@ -789,7 +783,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
public void setDismissRtl(boolean dismissRtl) {
mDismissRtl = dismissRtl;
if (mMenuContainer != null) {
- createMenuViews(true, mIsForeground);
+ createMenuViews(true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index 9846f2dcd170..321656df504a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
-import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.phone.StatusBar;
import dagger.Binds;
@@ -57,8 +56,6 @@ public interface ExpandableNotificationRowComponent {
@BindsInstance
Builder onDismissRunnable(@DismissRunnable Runnable runnable);
@BindsInstance
- Builder rowContentBindStage(RowContentBindStage rowContentBindStage);
- @BindsInstance
Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter);
ExpandableNotificationRowComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index c87b9986ca55..ff7793d506fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -41,8 +41,8 @@ import com.android.systemui.statusbar.notification.row.StackScrollerDecorView
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.children
-import com.android.systemui.util.takeUntil
import com.android.systemui.util.foldToSparseArray
+import com.android.systemui.util.takeUntil
import javax.inject.Inject
/**
@@ -166,6 +166,9 @@ class NotificationSectionsManager @Inject internal constructor(
peopleHubSubscription?.unsubscribe()
peopleHubSubscription = null
peopleHeaderView = reinflateView(peopleHeaderView, layoutInflater, R.layout.people_strip)
+ .apply {
+ setOnHeaderClickListener(View.OnClickListener { onGentleHeaderClick() })
+ }
if (ENABLE_SNOOZED_CONVERSATION_HUB) {
peopleHubSubscription = peopleHubViewAdapter.bindView(peopleHubViewBoundary)
}
@@ -326,6 +329,7 @@ class NotificationSectionsManager @Inject internal constructor(
// shade.
for (i in parent.childCount - 1 downTo -1) {
val child: View? = parent.getChildAt(i)
+
child?.let {
logShadeChild(i, child)
// If this child is a header, update the tracked positions
@@ -339,7 +343,8 @@ class NotificationSectionsManager @Inject internal constructor(
}
}
- val row = child as? ExpandableNotificationRow
+ val row = (child as? ExpandableNotificationRow)
+ ?.takeUnless { it.visibility == View.GONE }
// Is there a section discontinuity? This usually occurs due to HUNs
inIncomingSection = inIncomingSection || nextBucket?.let { next ->
@@ -386,7 +391,7 @@ class NotificationSectionsManager @Inject internal constructor(
// Offset the target to account for the current position of the people header.
peopleState?.targetPosition = peopleState?.currentPosition?.let { current ->
- peopleState?.targetPosition?.let { target ->
+ peopleState.targetPosition?.let { target ->
if (current < target) target - 1 else target
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index d7d09e05c238..d3b8a8cd2093 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -34,6 +34,8 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import java.lang.ref.WeakReference;
+
class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper {
@VisibleForTesting
@@ -47,7 +49,11 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
private static final long SWIPE_MENU_TIMING = 200;
- private NotificationMenuRowPlugin mCurrMenuRow;
+ // Hold a weak ref to the menu row so that it isn't accidentally retained in memory. The
+ // lifetime of the row should be the same as the ActivatableView, which is owned by the
+ // NotificationStackScrollLayout. If the notification isn't in the notification shade, then it
+ // isn't possible to swipe it and, so, this class doesn't need to "help."
+ private WeakReference<NotificationMenuRowPlugin> mCurrMenuRowRef;
private boolean mIsExpanded;
private boolean mPulsing;
@@ -82,11 +88,17 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
return mMenuExposedView;
}
- public void setCurrentMenuRow(NotificationMenuRowPlugin menuRow) {
- mCurrMenuRow = menuRow;
+ @VisibleForTesting
+ void setCurrentMenuRow(NotificationMenuRowPlugin menuRow) {
+ mCurrMenuRowRef = menuRow != null ? new WeakReference(menuRow) : null;
}
- public NotificationMenuRowPlugin getCurrentMenuRow() { return mCurrMenuRow; }
+ public NotificationMenuRowPlugin getCurrentMenuRow() {
+ if (mCurrMenuRowRef == null) {
+ return null;
+ }
+ return mCurrMenuRowRef.get();
+ }
@VisibleForTesting
protected Handler getHandler() { return mHandler; }
@@ -102,8 +114,9 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
@Override
protected void onChildSnappedBack(View animView, float targetLeft) {
- if (mCurrMenuRow != null && targetLeft == 0) {
- mCurrMenuRow.resetMenu();
+ final NotificationMenuRowPlugin menuRow = getCurrentMenuRow();
+ if (menuRow != null && targetLeft == 0) {
+ menuRow.resetMenu();
clearCurrentMenuRow();
}
}
@@ -129,10 +142,11 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
@VisibleForTesting
protected void initializeRow(SwipeableView row) {
if (row.hasFinishedInitialization()) {
- mCurrMenuRow = row.createMenu();
- if (mCurrMenuRow != null) {
- mCurrMenuRow.setMenuClickListener(mMenuListener);
- mCurrMenuRow.onTouchStart();
+ final NotificationMenuRowPlugin menuRow = row.createMenu();
+ setCurrentMenuRow(menuRow);
+ if (menuRow != null) {
+ menuRow.setMenuClickListener(mMenuListener);
+ menuRow.onTouchStart();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
index 8f77a1d776e4..b13e7fb839ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -93,6 +93,8 @@ class PeopleHubView(context: Context, attrs: AttributeSet) :
}
}
+ fun setOnHeaderClickListener(listener: OnClickListener) = label.setOnClickListener(listener)
+
private inner class PersonDataListenerImpl(val avatarView: ImageView) :
DataListener<PersonViewModel?> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index e03db2c8b9c6..39f5847ce2a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -14,6 +14,7 @@
package com.android.systemui.statusbar.phone;
+import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
import static android.app.StatusBarManager.DISABLE_CLOCK;
import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
@@ -63,6 +64,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private View mNotificationIconAreaInner;
private View mCenteredIconArea;
private int mDisabled1;
+ private int mDisabled2;
private StatusBar mStatusBarComponent;
private DarkIconManager mDarkIconManager;
private View mOperatorNameFrame;
@@ -173,9 +175,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
state1 = adjustDisableFlags(state1);
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
+ final int old2 = mDisabled2;
+ final int diff2 = state2 ^ old2;
mDisabled1 = state1;
- if ((diff1 & DISABLE_SYSTEM_INFO) != 0) {
- if ((state1 & DISABLE_SYSTEM_INFO) != 0) {
+ mDisabled2 = state2;
+ if ((diff1 & DISABLE_SYSTEM_INFO) != 0 || ((diff2 & DISABLE2_SYSTEM_ICONS) != 0)) {
+ if ((state1 & DISABLE_SYSTEM_INFO) != 0 || ((state2 & DISABLE2_SYSTEM_ICONS) != 0)) {
hideSystemIconArea(animate);
hideOperatorName(animate);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
index 5bc17f5bc2c6..53b369c3543e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
@@ -50,12 +50,12 @@ public class ContextualButton extends ButtonDispatcher {
/**
* Reload the drawable from resource id, should reapply the previous dark intensity.
*/
- public void updateIcon() {
+ public void updateIcon(int lightIconColor, int darkIconColor) {
if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) {
return;
}
final KeyButtonDrawable currentDrawable = getImageDrawable();
- KeyButtonDrawable drawable = getNewDrawable();
+ KeyButtonDrawable drawable = getNewDrawable(lightIconColor, darkIconColor);
if (currentDrawable != null) {
drawable.setDarkIntensity(currentDrawable.getDarkIntensity());
}
@@ -116,9 +116,9 @@ public class ContextualButton extends ButtonDispatcher {
mGroup = group;
}
- protected KeyButtonDrawable getNewDrawable() {
- return KeyButtonDrawable.create(getContext().getApplicationContext(), mIconResId,
- false /* shadow */);
+ protected KeyButtonDrawable getNewDrawable(int lightIconColor, int darkIconColor) {
+ return KeyButtonDrawable.create(getContext().getApplicationContext(), lightIconColor,
+ darkIconColor, mIconResId, false /* shadow */, null /* ovalBackground */);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
index 9e843f93d00e..c1017f4def0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
@@ -111,9 +111,9 @@ public class ContextualButtonGroup extends ButtonDispatcher {
* Update all the icons that are attached to this group. This will get all the buttons to update
* their icons for their buttons.
*/
- public void updateIcons() {
+ public void updateIcons(int lightIconColor, int darkIconColor) {
for (ButtonData data : mButtonData) {
- data.button.updateIcon();
+ data.button.updateIcon(lightIconColor, darkIconColor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index f5999f5c8294..5fab4bea9a04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -28,6 +28,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
import java.io.PrintWriter;
@@ -52,6 +53,7 @@ public class DozeParameters implements TunerService.Tunable,
private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
private final Resources mResources;
+ private final BatteryController mBatteryController;
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
@@ -62,10 +64,12 @@ public class DozeParameters implements TunerService.Tunable,
AmbientDisplayConfiguration ambientDisplayConfiguration,
AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
PowerManager powerManager,
+ BatteryController batteryController,
TunerService tunerService) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
+ mBatteryController = batteryController;
mControlScreenOffAnimation = !getDisplayNeedsBlanking();
mPowerManager = powerManager;
@@ -164,7 +168,7 @@ public class DozeParameters implements TunerService.Tunable,
* @return {@code true} if enabled and available.
*/
public boolean getAlwaysOn() {
- return mDozeAlwaysOn;
+ return mDozeAlwaysOn && !mBatteryController.isAodPowerSave();
}
/**
@@ -211,8 +215,4 @@ public class DozeParameters implements TunerService.Tunable,
public void onTuningChanged(String key, String newValue) {
mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
}
-
- public AlwaysOnDisplayPolicy getPolicy() {
- return mAlwaysOnPolicy;
- }
}
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 304fe0090e77..9606318e1992 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -121,6 +121,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
private final Context mContext;
private final OverviewProxyService mOverviewProxyService;
+ private final SysUiState mSysUiState;
private final Runnable mStateChangeCallback;
private final PluginManager mPluginManager;
@@ -197,14 +198,22 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
}
};
+ private final SysUiState.SysUiStateCallback mSysUiStateCallback =
+ new SysUiState.SysUiStateCallback() {
+ @Override
+ public void onSystemUiStateChanged(int sysUiFlags) {
+ mSysUiFlags = sysUiFlags;
+ }
+ };
+
public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
- SysUiState sysUiFlagContainer, PluginManager pluginManager,
- Runnable stateChangeCallback) {
+ SysUiState sysUiState, PluginManager pluginManager, Runnable stateChangeCallback) {
super(Dependency.get(BroadcastDispatcher.class));
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
mOverviewProxyService = overviewProxyService;
+ mSysUiState = sysUiState;
mPluginManager = pluginManager;
mStateChangeCallback = stateChangeCallback;
ComponentName recentsComponentName = ComponentName.unflattenFromString(
@@ -231,7 +240,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
}
}
- Dependency.get(ProtoTracer.class).add(this);
mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
ViewConfiguration.getLongPressTimeout());
@@ -239,7 +247,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged);
updateCurrentUserResources();
- sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
}
public void updateCurrentUserResources() {
@@ -286,7 +293,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
*/
public void onNavBarAttached() {
mIsAttached = true;
+ Dependency.get(ProtoTracer.class).add(this);
mOverviewProxyService.addCallback(mQuickSwitchListener);
+ mSysUiState.addCallback(mSysUiStateCallback);
updateIsEnabled();
startTracking();
}
@@ -296,7 +305,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
*/
public void onNavBarDetached() {
mIsAttached = false;
+ Dependency.get(ProtoTracer.class).remove(this);
mOverviewProxyService.removeCallback(mQuickSwitchListener);
+ mSysUiState.removeCallback(mSysUiStateCallback);
updateIsEnabled();
stopTracking();
}
@@ -614,8 +625,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
// Bubble controller will give us a valid display id if it should get the back event
BubbleController bubbleController = Dependency.get(BubbleController.class);
int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
- if (code == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
+ if (bubbleDisplayId != INVALID_DISPLAY) {
ev.setDisplayId(bubbleDisplayId);
+ } else {
+ ev.setDisplayId(mContext.getDisplay().getDisplayId());
}
InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index 16b5a2389ec6..687f5f15a78c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -16,19 +16,16 @@
package com.android.systemui.statusbar.phone;
-import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import com.android.systemui.statusbar.policy.KeyButtonView;
@@ -65,6 +62,8 @@ public class FloatingRotationButton implements RotationButton {
@Override
public void setRotationButtonController(RotationButtonController rotationButtonController) {
mRotationButtonController = rotationButtonController;
+ updateIcon(mRotationButtonController.getLightIconColor(),
+ mRotationButtonController.getDarkIconColor());
}
@Override
@@ -101,7 +100,6 @@ public class FloatingRotationButton implements RotationButton {
default:
break;
}
- updateIcon();
mWindowManager.addView(mKeyButtonView, lp);
if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
mKeyButtonDrawable.resetAnimation();
@@ -126,17 +124,13 @@ public class FloatingRotationButton implements RotationButton {
}
@Override
- public void updateIcon() {
- if (!mIsShowing) {
- return;
- }
- mKeyButtonDrawable = getImageDrawable();
+ public void updateIcon(int lightIconColor, int darkIconColor) {
+ Color ovalBackgroundColor = Color.valueOf(Color.red(darkIconColor),
+ Color.green(darkIconColor), Color.blue(darkIconColor), BACKGROUND_ALPHA);
+ mKeyButtonDrawable = KeyButtonDrawable.create(mRotationButtonController.getContext(),
+ lightIconColor, darkIconColor, mRotationButtonController.getIconResId(),
+ false /* shadow */, ovalBackgroundColor);
mKeyButtonView.setImageDrawable(mKeyButtonDrawable);
- mKeyButtonDrawable.setCallback(mKeyButtonView);
- if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
- mKeyButtonDrawable.resetAnimation();
- mKeyButtonDrawable.startAnimation();
- }
}
@Override
@@ -151,20 +145,7 @@ public class FloatingRotationButton implements RotationButton {
@Override
public KeyButtonDrawable getImageDrawable() {
- Context context = new ContextThemeWrapper(mContext.getApplicationContext(),
- mRotationButtonController.getStyleRes());
- final int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme);
- final int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme);
- Context lightContext = new ContextThemeWrapper(context, dualToneLightTheme);
- Context darkContext = new ContextThemeWrapper(context, dualToneDarkTheme);
- @ColorInt int darkColor = Utils.getColorAttrDefaultColor(darkContext,
- R.attr.singleToneColor);
- Color ovalBackgroundColor = Color.valueOf(Color.red(darkColor), Color.green(darkColor),
- Color.blue(darkColor), BACKGROUND_ALPHA);
-
- return KeyButtonDrawable.create(lightContext,
- Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor), darkColor,
- R.drawable.ic_sysbar_rotate_button, false /* shadow */, ovalBackgroundColor);
+ return mKeyButtonDrawable;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 3dcf7ed674c7..e05ba12781c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -428,7 +428,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
@Override
- protected boolean isSticky() {
+ public boolean isSticky() {
return super.isSticky() || mMenuShownPinned;
}
@@ -568,6 +568,17 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
}
mKeysToRemoveWhenLeavingKeyguard.clear();
}
+ if (wasKeyguard && !isKeyguard && mBypassController.getBypassEnabled()) {
+ ArrayList<String> keysToRemove = new ArrayList<>();
+ for (AlertEntry entry : mAlertEntries.values()) {
+ if (entry.mEntry != null && entry.mEntry.isBubble() && !entry.isSticky()) {
+ keysToRemove.add(entry.mEntry.getKey());
+ }
+ }
+ for (String key : keysToRemove) {
+ removeAlertEntry(key);
+ }
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 39949c82661f..b6a284c5e3c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -26,7 +26,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.util.MathUtils;
-import android.util.Slog;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -169,7 +168,7 @@ public class KeyguardBouncer {
// This condition may indicate an error on Android, so log it.
if (!allowDismissKeyguard) {
- Slog.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId);
+ Log.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId);
}
mShowingSoon = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 27daf8615a31..f43fa648a1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -120,6 +120,7 @@ import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -131,6 +132,7 @@ import com.android.systemui.util.LifecycleFragment;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
@@ -354,6 +356,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
// If the button will actually become visible and the navbar is about to hide,
// tell the statusbar to keep it around for longer
mAutoHideController.touchAutoHide();
+ mNavigationBarView.notifyActiveTouchRegions();
}
};
@@ -550,6 +553,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
mOrientationHandleGlobalLayoutListener);
}
+ mHandler.removeCallbacks(mAutoDim);
+ mNavigationBarView = null;
+ mOrientationHandle = null;
}
@Override
@@ -591,6 +597,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
.registerDisplayListener(this, new Handler(Looper.getMainLooper()));
mOrientationHandle = new QuickswitchOrientedNavHandle(getContext());
+ mOrientationHandle.setId(R.id.secondary_home_handle);
getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener);
mOrientationParams = new WindowManager.LayoutParams(0, 0,
@@ -962,13 +969,22 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
}
// Change the cancel pin gesture to home and back if recents button is invisible
- boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible();
+ boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
ButtonDispatcher backButton = mNavigationBarView.getBackButton();
- if (recentsVisible) {
- backButton.setOnLongClickListener(this::onLongPressBackRecents);
+ ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
+ if (pinningActive) {
+ boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible();
+ backButton.setOnLongClickListener(recentsVisible
+ ? this::onLongPressBackRecents
+ : this::onLongPressBackHome);
+ recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
} else {
- backButton.setOnLongClickListener(this::onLongPressBackHome);
+ backButton.setOnLongClickListener(null);
+ recentsButton.setOnLongClickListener(null);
}
+ // Note, this needs to be set after even if we're setting the listener to null
+ backButton.setLongClickable(pinningActive);
+ recentsButton.setLongClickable(pinningActive);
}
private void notifyNavigationBarScreenOn() {
@@ -981,11 +997,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
recentsButton.setOnClickListener(this::onRecentsClick);
recentsButton.setOnTouchListener(this::onRecentsTouch);
- recentsButton.setLongClickable(true);
- recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
-
- ButtonDispatcher backButton = mNavigationBarView.getBackButton();
- backButton.setLongClickable(true);
ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
homeButton.setOnTouchListener(this::onHomeTouch);
@@ -1093,6 +1104,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps);
}
+
/**
* This handles long-press of both back and recents/home. Back is the common button with
* combination of recents if it is visible or home if recents is invisible.
@@ -1452,11 +1464,11 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
if (navigationBarView == null) return null;
- final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
- .create(NavigationBarFragment.class);
navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
+ final NavigationBarFragment fragment =
+ FragmentHostManager.get(v).create(NavigationBarFragment.class);
final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
fragmentHost.getFragmentManager().beginTransaction()
.replace(R.id.navigation_bar_frame, fragment, TAG)
@@ -1466,6 +1478,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
@Override
public void onViewDetachedFromWindow(View v) {
+ final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
+ fragmentHost.removeTagListener(TAG, listener);
FragmentHostManager.removeAndDestroy(v);
navigationBarView.removeOnAttachStateChangeListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 1eab427b4155..84512ac85fa9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -48,6 +48,7 @@ import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -63,6 +64,7 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -113,17 +115,16 @@ public class NavigationBarView extends FrameLayout implements
int mNavigationIconHints = 0;
private int mNavBarMode;
- private Rect mHomeButtonBounds = new Rect();
- private Rect mBackButtonBounds = new Rect();
- private Rect mRecentsButtonBounds = new Rect();
- private Rect mRotationButtonBounds = new Rect();
private final Region mActiveRegion = new Region();
- private int[] mTmpPosition = new int[2];
+ private Rect mTmpBounds = new Rect();
private KeyButtonDrawable mBackIcon;
private KeyButtonDrawable mHomeDefaultIcon;
private KeyButtonDrawable mRecentIcon;
private KeyButtonDrawable mDockedIcon;
+ private Context mLightContext;
+ private int mLightIconColor;
+ private int mDarkIconColor;
private EdgeBackGestureHandler mEdgeBackGestureHandler;
private final DeadZone mDeadZone;
@@ -278,6 +279,12 @@ public class NavigationBarView extends FrameLayout implements
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
+ final Context darkContext = new ContextThemeWrapper(context,
+ Utils.getThemeAttr(context, R.attr.darkIconTheme));
+ mLightContext = new ContextThemeWrapper(context,
+ Utils.getThemeAttr(context, R.attr.lightIconTheme));
+ mLightIconColor = Utils.getColorAttrDefaultColor(mLightContext, R.attr.singleToneColor);
+ mDarkIconColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor);
mIsVertical = false;
mLongClickableAccessibilityButton = false;
mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
@@ -290,7 +297,7 @@ public class NavigationBarView extends FrameLayout implements
final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
R.drawable.ic_ime_switcher_default);
final RotationContextButton rotateSuggestionButton = new RotationContextButton(
- R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button);
+ R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button_ccw_start_0);
final ContextualButton accessibilityButton =
new ContextualButton(R.id.accessibility_button,
R.drawable.ic_sysbar_accessibility_button);
@@ -303,8 +310,8 @@ public class NavigationBarView extends FrameLayout implements
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mFloatingRotationButton = new FloatingRotationButton(context);
- mRotationButtonController = new RotationButtonController(context,
- R.style.RotateButtonCCWStart90,
+ mRotationButtonController = new RotationButtonController(mLightContext,
+ mLightIconColor, mDarkIconColor,
isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton);
mConfiguration = new Configuration();
@@ -501,7 +508,7 @@ public class NavigationBarView extends FrameLayout implements
}
if (densityChange || dirChange) {
mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent);
- mContextualButtonGroup.updateIcons();
+ mContextualButtonGroup.updateIcons(mLightIconColor, mDarkIconColor);
}
if (orientationChange || densityChange || dirChange) {
mBackIcon = getBackDrawable();
@@ -559,11 +566,6 @@ public class NavigationBarView extends FrameLayout implements
drawable.setRotation(mIsVertical ? 90 : 0);
}
- private KeyButtonDrawable chooseNavigationIconDrawable(@DrawableRes int icon,
- @DrawableRes int quickStepIcon) {
- return getDrawable(chooseNavigationIconDrawableRes(icon, quickStepIcon));
- }
-
private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon,
@DrawableRes int quickStepIcon) {
final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
@@ -571,11 +573,8 @@ public class NavigationBarView extends FrameLayout implements
}
private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
- return KeyButtonDrawable.create(mContext, icon, true /* hasShadow */);
- }
-
- private KeyButtonDrawable getDrawable(@DrawableRes int icon, boolean hasShadow) {
- return KeyButtonDrawable.create(mContext, icon, hasShadow);
+ return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon,
+ true /* hasShadow */, null /* ovalBackgroundColor */);
}
/** To be called when screen lock/unlock state changes */
@@ -709,6 +708,7 @@ public class NavigationBarView extends FrameLayout implements
getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
+ notifyActiveTouchRegions();
}
@VisibleForTesting
@@ -861,7 +861,6 @@ public class NavigationBarView extends FrameLayout implements
mBarTransitions.onNavigationModeChanged(mNavBarMode);
mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
- getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
if (isGesturalMode(mNavBarMode)) {
mRegionSamplingHelper.start(mSamplingBounds);
@@ -927,42 +926,30 @@ public class NavigationBarView extends FrameLayout implements
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
+ notifyActiveTouchRegions();
+ mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
+ }
+
+ /**
+ * Notifies the overview service of the active touch regions.
+ */
+ public void notifyActiveTouchRegions() {
mActiveRegion.setEmpty();
- updateButtonLocation(getBackButton(), mBackButtonBounds, true);
- updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
- updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
- updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
- // TODO: Handle button visibility changes
+ updateButtonLocation(getBackButton());
+ updateButtonLocation(getHomeButton());
+ updateButtonLocation(getRecentsButton());
+ updateButtonLocation(getRotateSuggestionButton());
mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
- mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
}
- private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds,
- boolean isActive) {
+ private void updateButtonLocation(ButtonDispatcher button) {
View view = button.getCurrentView();
- if (view == null) {
- buttonBounds.setEmpty();
+ if (view == null || !button.isVisible()) {
return;
}
- // Temporarily reset the translation back to origin to get the position in window
- final float posX = view.getTranslationX();
- final float posY = view.getTranslationY();
- view.setTranslationX(0);
- view.setTranslationY(0);
-
- if (isActive) {
- view.getLocationOnScreen(mTmpPosition);
- buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
- mTmpPosition[0] + view.getMeasuredWidth(),
- mTmpPosition[1] + view.getMeasuredHeight());
- mActiveRegion.op(buttonBounds, Op.UNION);
- }
- view.getLocationInWindow(mTmpPosition);
- buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
- mTmpPosition[0] + view.getMeasuredWidth(),
- mTmpPosition[1] + view.getMeasuredHeight());
- view.setTranslationX(posX);
- view.setTranslationY(posY);
+
+ view.getBoundsOnScreen(mTmpBounds);
+ mActiveRegion.op(mTmpBounds, Op.UNION);
}
private void updateOrientationViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 07eaaa187fbe..bf858520c338 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -19,8 +19,6 @@ package com.android.systemui.statusbar.phone;
import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY;
import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
@@ -807,6 +805,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
} else {
super.applyToView(view);
}
+ sTempProperties.setAnimationEndAction(null);
boolean inShelf = iconAppearAmount == 1.0f;
icon.setIsInShelf(inShelf);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f2eec39ed17e..375af6b099c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -166,6 +166,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
+ private final BiometricUnlockController mBiometricUnlockController;
private final NotificationPanelView mView;
private final MetricsLogger mMetricsLogger;
private final ActivityManager mActivityManager;
@@ -227,7 +228,8 @@ public class NotificationPanelViewController extends PanelViewController {
mBarState == StatusBarState.KEYGUARD
|| mBarState == StatusBarState.SHADE_LOCKED;
if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
- && !mDelayShowingKeyguardStatusBar) {
+ && !mDelayShowingKeyguardStatusBar
+ && !mBiometricUnlockController.isBiometricUnlock()) {
mFirstBypassAttempt = false;
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
}
@@ -433,6 +435,11 @@ public class NotificationPanelViewController extends PanelViewController {
private Runnable mExpandAfterLayoutRunnable;
/**
+ * Is this a collapse that started on the panel where we should allow the panel to intercept
+ */
+ private boolean mIsPanelCollapseOnQQS;
+
+ /**
* If face auth with bypass is running for the first time after you turn on the screen.
* (From aod or screen off)
*/
@@ -487,6 +494,7 @@ public class NotificationPanelViewController extends PanelViewController {
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
ConversationNotificationManager conversationNotificationManager,
MediaHierarchyManager mediaHierarchyManager,
+ BiometricUnlockController biometricUnlockController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
@@ -511,6 +519,7 @@ public class NotificationPanelViewController extends PanelViewController {
mDisplayId = displayId;
mPulseExpansionHandler = pulseExpansionHandler;
mDozeParameters = dozeParameters;
+ mBiometricUnlockController = biometricUnlockController;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) {
mQs.animateHeaderSlidingOut();
@@ -1060,7 +1069,11 @@ public class NotificationPanelViewController extends PanelViewController {
mInitialTouchX = x;
initVelocityTracker();
trackMovement(event);
- if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+ if (mKeyguardShowing
+ && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+ // Dragging down on the lockscreen statusbar should prohibit other interactions
+ // immediately, otherwise we'll wait on the touchslop. This is to allow
+ // dragging down to expanded quick settings directly on the lockscreen.
mView.getParent().requestDisallowInterceptTouchEvent(true);
}
if (mQsExpansionAnimator != null) {
@@ -1093,9 +1106,10 @@ public class NotificationPanelViewController extends PanelViewController {
trackMovement(event);
return true;
}
- if (Math.abs(h) > getTouchSlop(event)
+ if ((h > getTouchSlop(event) || (h < -getTouchSlop(event) && mQsExpanded))
&& Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
+ mView.getParent().requestDisallowInterceptTouchEvent(true);
mQsTracking = true;
onQsExpansionStarted();
notifyExpandingFinished();
@@ -1135,6 +1149,7 @@ public class NotificationPanelViewController extends PanelViewController {
mDownX = event.getX();
mDownY = event.getY();
mCollapsedOnDown = isFullyCollapsed();
+ mIsPanelCollapseOnQQS = canPanelCollapseOnQQS(mDownX, mDownY);
mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
@@ -1150,6 +1165,24 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
+ /**
+ * Can the panel collapse in this motion because it was started on QQS?
+ *
+ * @param downX the x location where the touch started
+ * @param downY the y location where the touch started
+ *
+ * @return true if the panel could be collapsed because it stared on QQS
+ */
+ private boolean canPanelCollapseOnQQS(float downX, float downY) {
+ if (mCollapsedOnDown || mKeyguardShowing || mQsExpanded) {
+ return false;
+ }
+ View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader();
+ return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth()
+ && downY <= header.getBottom();
+
+ }
+
private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
float vel = getCurrentQSVelocity();
final boolean expandsQs = flingExpandsQs(vel);
@@ -1899,10 +1932,11 @@ public class NotificationPanelViewController extends PanelViewController {
}
@Override
- protected boolean isScrolledToBottom() {
+ protected boolean canCollapsePanelOnTouch() {
if (!isInSettings()) {
return mBarState == StatusBarState.KEYGUARD
- || mNotificationStackScroller.isScrolledToBottom();
+ || mNotificationStackScroller.isScrolledToBottom()
+ || mIsPanelCollapseOnQQS;
} else {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 2e4a929ded5b..bc73be19ab59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -61,6 +61,8 @@ import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -84,6 +86,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
private final boolean mKeyguardScreenRotation;
private final long mLockScreenDisplayTimeout;
private final Display.Mode mKeyguardDisplayMode;
+ private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardBypassController mKeyguardBypassController;
private ViewGroup mNotificationShadeView;
private LayoutParams mLp;
@@ -104,6 +107,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
IActivityManager activityManager, DozeParameters dozeParameters,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
+ KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor,
DumpManager dumpManager) {
mContext = context;
@@ -113,6 +117,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
mLpChanged = new LayoutParams();
+ mKeyguardViewMediator = keyguardViewMediator;
mKeyguardBypassController = keyguardBypassController;
mColorExtractor = colorExtractor;
dumpManager.registerDumpable(getClass().getName(), this);
@@ -202,6 +207,11 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
mWindowManager.addView(mNotificationShadeView, mLp);
mLpChanged.copyFrom(mLp);
onThemeChanged();
+
+ // Make the state consistent with KeyguardViewMediator#setupLocked during initialization.
+ if (mKeyguardViewMediator.isShowingAndNotOccluded()) {
+ setKeyguardShowing(true);
+ }
}
public void setNotificationShadeView(ViewGroup view) {
@@ -424,7 +434,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
}
private void applyHasTopUi(State state) {
- mHasTopUiChanged = state.mForceHasTopUi || isExpanded(state);
+ mHasTopUiChanged = !state.mComponentsForcingTopUi.isEmpty() || isExpanded(state);
}
private void applyNotTouchable(State state) {
@@ -627,12 +637,17 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
apply(mCurrentState);
}
- public boolean getForceHasTopUi() {
- return mCurrentState.mForceHasTopUi;
- }
-
- public void setForceHasTopUi(boolean forceHasTopUi) {
- mCurrentState.mForceHasTopUi = forceHasTopUi;
+ /**
+ * SystemUI may need top-ui to avoid jank when performing animations. After the
+ * animation is performed, the component should remove itself from the list of features that
+ * are forcing SystemUI to be top-ui.
+ */
+ public void setRequestTopUi(boolean requestTopUi, String componentTag) {
+ if (requestTopUi) {
+ mCurrentState.mComponentsForcingTopUi.add(componentTag);
+ } else {
+ mCurrentState.mComponentsForcingTopUi.remove(componentTag);
+ }
apply(mCurrentState);
}
@@ -655,7 +670,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
boolean mBackdropShowing;
boolean mWallpaperSupportsAmbientMode;
boolean mNotTouchable;
- boolean mForceHasTopUi;
+ Set<String> mComponentsForcingTopUi = new HashSet<>();
/**
* The {@link StatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index caddc4a874f4..e942d85790c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -371,12 +371,26 @@ public abstract class PanelViewController {
float vectorVel = (float) Math.hypot(
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
- boolean expand = flingExpands(vel, vectorVel, x, y)
- || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel;
+ final boolean onKeyguard =
+ mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+
+ final boolean expand;
+ if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
+ // If we get a cancel, put the shade back to the state it was in when the gesture
+ // started
+ if (onKeyguard) {
+ expand = true;
+ } else {
+ expand = !mPanelClosedOnDown;
+ }
+ } else {
+ expand = flingExpands(vel, vectorVel, x, y);
+ }
+
mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch());
// Log collapse gesture if on lock screen.
- if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+ if (!expand && onKeyguard) {
float displayDensity = mStatusBar.getDisplayDensity();
int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
int velocityDp = (int) Math.abs(vel / displayDensity);
@@ -460,7 +474,7 @@ public abstract class PanelViewController {
}
}
- protected boolean isScrolledToBottom() {
+ protected boolean canCollapsePanelOnTouch() {
return true;
}
@@ -1081,7 +1095,7 @@ public abstract class PanelViewController {
* upwards. This allows closing the shade from anywhere inside the panel.
*
* We only do this if the current content is scrolled to the bottom,
- * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling
+ * i.e canCollapsePanelOnTouch() is true and therefore there is no conflicting scrolling
* gesture
* possible.
*/
@@ -1092,7 +1106,7 @@ public abstract class PanelViewController {
}
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
- boolean scrolledToBottom = isScrolledToBottom();
+ boolean canCollapsePanel = canCollapsePanelOnTouch();
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
@@ -1139,7 +1153,7 @@ public abstract class PanelViewController {
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
addMovement(event);
- if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
+ if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown) {
float hAbs = Math.abs(h);
float touchSlop = getTouchSlop(event);
if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 06d35a36e3c4..b2cfceae2cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -47,6 +47,9 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.privacy.PrivacyType;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.screenrecord.RecordingController;
@@ -70,6 +73,9 @@ import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.time.DateFormatUtil;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
@@ -87,13 +93,13 @@ public class PhoneStatusBarPolicy
ZenModeController.Callback,
DeviceProvisionedListener,
KeyguardStateController.Callback,
+ PrivacyItemController.Callback,
LocationController.LocationChangeCallback,
RecordingController.RecordingStateChangeCallback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- static final int LOCATION_STATUS_ICON_ID =
- com.android.internal.R.drawable.perm_group_location;
+ static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId();
private final String mSlotCast;
private final String mSlotHotspot;
@@ -107,6 +113,8 @@ public class PhoneStatusBarPolicy
private final String mSlotHeadset;
private final String mSlotDataSaver;
private final String mSlotLocation;
+ private final String mSlotMicrophone;
+ private final String mSlotCamera;
private final String mSlotSensorsOff;
private final String mSlotScreenRecord;
private final int mDisplayId;
@@ -132,6 +140,7 @@ public class PhoneStatusBarPolicy
private final DeviceProvisionedController mProvisionedController;
private final KeyguardStateController mKeyguardStateController;
private final LocationController mLocationController;
+ private final PrivacyItemController mPrivacyItemController;
private final Executor mUiBgExecutor;
private final SensorPrivacyController mSensorPrivacyController;
private final RecordingController mRecordingController;
@@ -162,7 +171,8 @@ public class PhoneStatusBarPolicy
RecordingController recordingController,
@Nullable TelecomManager telecomManager, @DisplayId int displayId,
@Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
- RingerModeTracker ringerModeTracker) {
+ RingerModeTracker ringerModeTracker,
+ PrivacyItemController privacyItemController) {
mIconController = iconController;
mCommandQueue = commandQueue;
mBroadcastDispatcher = broadcastDispatcher;
@@ -181,6 +191,7 @@ public class PhoneStatusBarPolicy
mProvisionedController = deviceProvisionedController;
mKeyguardStateController = keyguardStateController;
mLocationController = locationController;
+ mPrivacyItemController = privacyItemController;
mSensorPrivacyController = sensorPrivacyController;
mRecordingController = recordingController;
mUiBgExecutor = uiBgExecutor;
@@ -200,6 +211,8 @@ public class PhoneStatusBarPolicy
mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
mSlotDataSaver = resources.getString(com.android.internal.R.string.status_bar_data_saver);
mSlotLocation = resources.getString(com.android.internal.R.string.status_bar_location);
+ mSlotMicrophone = resources.getString(com.android.internal.R.string.status_bar_microphone);
+ mSlotCamera = resources.getString(com.android.internal.R.string.status_bar_camera);
mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off);
mSlotScreenRecord = resources.getString(
com.android.internal.R.string.status_bar_screen_record);
@@ -271,6 +284,13 @@ public class PhoneStatusBarPolicy
mResources.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
+ // privacy items
+ mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
+ mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId()));
+ mIconController.setIconVisibility(mSlotMicrophone, false);
+ mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
+ mResources.getString(PrivacyType.TYPE_CAMERA.getNameId()));
+ mIconController.setIconVisibility(mSlotCamera, false);
mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
mResources.getString(R.string.accessibility_location_active));
mIconController.setIconVisibility(mSlotLocation, false);
@@ -294,6 +314,7 @@ public class PhoneStatusBarPolicy
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardStateController.addCallback(this);
+ mPrivacyItemController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
mRecordingController.addCallback(this);
@@ -609,9 +630,44 @@ public class PhoneStatusBarPolicy
mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
}
+ @Override // PrivacyItemController.Callback
+ public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+ updatePrivacyItems(privacyItems);
+ }
+
+ private void updatePrivacyItems(List<PrivacyItem> items) {
+ boolean showCamera = false;
+ boolean showMicrophone = false;
+ boolean showLocation = false;
+ for (PrivacyItem item : items) {
+ if (item == null /* b/124234367 */) {
+ Log.e(TAG, "updatePrivacyItems - null item found");
+ StringWriter out = new StringWriter();
+ mPrivacyItemController.dump(null, new PrintWriter(out), null);
+ // Throw so we can look into this
+ throw new NullPointerException(out.toString());
+ }
+ switch (item.getPrivacyType()) {
+ case TYPE_CAMERA:
+ showCamera = true;
+ break;
+ case TYPE_LOCATION:
+ showLocation = true;
+ break;
+ case TYPE_MICROPHONE:
+ showMicrophone = true;
+ break;
+ }
+ }
+
+ mIconController.setIconVisibility(mSlotCamera, showCamera);
+ mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
+ mIconController.setIconVisibility(mSlotLocation, showLocation);
+ }
+
@Override
public void onLocationActiveChanged(boolean active) {
- updateLocation();
+ if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation();
}
// Updates the status view based on the current state of location requests.
@@ -688,8 +744,8 @@ public class PhoneStatusBarPolicy
if (DEBUG) Log.d(TAG, "screenrecord: hiding icon during countdown");
mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
// Reset talkback priority
- mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord,
- View.ACCESSIBILITY_LIVE_REGION_NONE);
+ mHandler.post(() -> mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord,
+ View.ACCESSIBILITY_LIVE_REGION_NONE));
}
@Override
@@ -698,7 +754,7 @@ public class PhoneStatusBarPolicy
mIconController.setIcon(mSlotScreenRecord,
R.drawable.stat_sys_screen_record,
mResources.getString(R.string.screenrecord_ongoing_screen_only));
- mIconController.setIconVisibility(mSlotScreenRecord, true);
+ mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, true));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java
index 2580c0e77013..687efd34ee30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java
@@ -27,7 +27,7 @@ interface RotationButton {
boolean show();
boolean hide();
boolean isVisible();
- void updateIcon();
+ void updateIcon(int lightIconColor, int darkIconColor);
void setOnClickListener(View.OnClickListener onClickListener);
void setOnHoverListener(View.OnHoverListener onHoverListener);
KeyButtonDrawable getImageDrawable();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
index 59b10e416b03..f83cdd488c04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
@@ -21,6 +21,8 @@ import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
import android.annotation.StyleRes;
import android.app.StatusBarManager;
import android.content.ContentResolver;
@@ -30,6 +32,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.IRotationWatcher.Stub;
import android.view.MotionEvent;
import android.view.Surface;
@@ -40,6 +43,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
+import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -61,10 +65,12 @@ public class RotationButtonController {
private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
+ private final Context mContext;
+ private final RotationButton mRotationButton;
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
private final ViewRippler mViewRippler = new ViewRippler();
- private @StyleRes int mStyleRes;
private int mLastRotationSuggestion;
private boolean mPendingRotationSuggestion;
private boolean mHoveringRotationSuggestion;
@@ -75,6 +81,9 @@ public class RotationButtonController {
private boolean mListenersRegistered = false;
private boolean mIsNavigationBarShowing;
private boolean mSkipOverrideUserLockPrefsOnce;
+ private int mLightIconColor;
+ private int mDarkIconColor;
+ private int mIconResId = R.drawable.ic_sysbar_rotate_button_ccw_start_90;
private final Runnable mRemoveRotationProposal =
() -> setRotateSuggestionButtonState(false /* visible */);
@@ -82,9 +91,6 @@ public class RotationButtonController {
() -> mPendingRotationSuggestion = false;
private Animator mRotateHideAnimator;
- private final Context mContext;
- private final RotationButton mRotationButton;
- private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final Stub mRotationWatcher = new Stub() {
@Override
@@ -117,12 +123,14 @@ public class RotationButtonController {
return (disable2Flags & StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS) != 0;
}
- RotationButtonController(Context context, @StyleRes int style, RotationButton rotationButton) {
+ RotationButtonController(Context context, @ColorInt int lightIconColor,
+ @ColorInt int darkIconColor, RotationButton rotationButton) {
mContext = context;
+ mLightIconColor = lightIconColor;
+ mDarkIconColor = darkIconColor;
mRotationButton = rotationButton;
mRotationButton.setRotationButtonController(this);
- mStyleRes = style;
mIsNavigationBarShowing = true;
mRotationLockController = Dependency.get(RotationLockController.class);
mAccessibilityManagerWrapper = Dependency.get(AccessibilityManagerWrapper.class);
@@ -275,17 +283,20 @@ public class RotationButtonController {
return;
}
+ // TODO: Remove styles?
// Prepare to show the navbar icon by updating the icon style to change anim params
mLastRotationSuggestion = rotation; // Remember rotation for click
final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation);
- int style;
if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
- style = rotationCCW ? R.style.RotateButtonCCWStart90 : R.style.RotateButtonCWStart90;
+ mIconResId = rotationCCW
+ ? R.drawable.ic_sysbar_rotate_button_ccw_start_90
+ : R.drawable.ic_sysbar_rotate_button_cw_start_90;
} else { // 90 or 270
- style = rotationCCW ? R.style.RotateButtonCCWStart0 : R.style.RotateButtonCWStart0;
+ mIconResId = rotationCCW
+ ? R.drawable.ic_sysbar_rotate_button_ccw_start_0
+ : R.drawable.ic_sysbar_rotate_button_ccw_start_0;
}
- mStyleRes = style;
- mRotationButton.updateIcon();
+ mRotationButton.updateIcon(mLightIconColor, mDarkIconColor);
if (mIsNavigationBarShowing) {
// The navbar is visible so show the icon right away
@@ -316,14 +327,26 @@ public class RotationButtonController {
}
}
- @StyleRes int getStyleRes() {
- return mStyleRes;
+ Context getContext() {
+ return mContext;
}
RotationButton getRotationButton() {
return mRotationButton;
}
+ @DrawableRes int getIconResId() {
+ return mIconResId;
+ }
+
+ @ColorInt int getLightIconColor() {
+ return mLightIconColor;
+ }
+
+ @ColorInt int getDarkIconColor() {
+ return mDarkIconColor;
+ }
+
private void onRotateSuggestionClick(View v) {
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
incrementNumAcceptedRotationSuggestionsIfNeeded();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
index bd9675280b0b..d7e95e43ea8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -16,22 +16,16 @@
package com.android.systemui.statusbar.phone;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
-
import android.annotation.DrawableRes;
import android.annotation.IdRes;
-import android.content.Context;
-import android.view.ContextThemeWrapper;
import android.view.View;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
/** Containing logic for the rotation button in nav bar. */
-public class RotationContextButton extends ContextualButton implements
- NavigationModeController.ModeChangedListener, RotationButton {
+public class RotationContextButton extends ContextualButton implements RotationButton {
public static final boolean DEBUG_ROTATION = false;
- private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private RotationButtonController mRotationButtonController;
public RotationContextButton(@IdRes int buttonResId, @DrawableRes int iconResId) {
@@ -56,16 +50,10 @@ public class RotationContextButton extends ContextualButton implements
}
@Override
- protected KeyButtonDrawable getNewDrawable() {
- Context context = new ContextThemeWrapper(getContext().getApplicationContext(),
- mRotationButtonController.getStyleRes());
- return KeyButtonDrawable.create(context, mIconResId, false /* shadow */,
- null /* ovalBackgroundColor */);
- }
-
- @Override
- public void onNavigationModeChanged(int mode) {
- mNavBarMode = mode;
+ protected KeyButtonDrawable getNewDrawable(int lightIconColor, int darkIconColor) {
+ return KeyButtonDrawable.create(mRotationButtonController.getContext(),
+ lightIconColor, darkIconColor, mRotationButtonController.getIconResId(),
+ false /* shadow */, null /* ovalBackgroundColor */);
}
@Override
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 60fc17d9474a..686b87127239 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -348,10 +348,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
}
if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
- // In case the user isn't unlocked, make sure to delay a bit because the system is hosed
- // with too many things at this case, in order to not skip the initial frames.
- mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
+ scheduleUpdate();
} else if ((!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD)
|| (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
// Scheduling a frame isn't enough when:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 5a605df14892..c5571e8ceb20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -101,6 +101,7 @@ import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Slog;
import android.view.Display;
import android.view.IWindowManager;
@@ -212,7 +213,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -305,6 +305,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public static final int FADE_KEYGUARD_DURATION = 300;
public static final int FADE_KEYGUARD_DURATION_PULSING = 96;
+
/** If true, the system is in the half-boot-to-decryption-screen state.
* Prudently disable QS and notifications. */
public static final boolean ONLY_CORE_APPS;
@@ -1097,7 +1098,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
- mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
createNavigationBar(result);
@@ -1153,6 +1153,15 @@ public class StatusBar extends SystemUI implements DemoMode,
BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
+ float maxWallpaperZoom = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_wallpaperMaxScale);
+ mNotificationShadeDepthControllerLazy.get().addListener(depth -> {
+ float scale = MathUtils.lerp(maxWallpaperZoom, 1f, depth);
+ backdrop.setPivotX(backdrop.getWidth() / 2f);
+ backdrop.setPivotY(backdrop.getHeight() / 2f);
+ backdrop.setScaleX(scale);
+ backdrop.setScaleY(scale);
+ });
mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
@@ -2387,18 +2396,31 @@ public class StatusBar extends SystemUI implements DemoMode,
new WirelessChargingAnimation.Callback() {
@Override
public void onAnimationStarting() {
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1);
}
@Override
public void onAnimationEnded() {
CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView());
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
}
}, mDozing).show(animationDelay);
} else {
// workspace
WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
- transmittingBatteryLevel, batteryLevel, null, false).show(animationDelay);
+ transmittingBatteryLevel, batteryLevel,
+ new WirelessChargingAnimation.Callback() {
+ @Override
+ public void onAnimationStarting() {
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
+ }
+
+ @Override
+ public void onAnimationEnded() {
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
+ }
+ }, false).show(animationDelay);
}
}
@@ -2451,7 +2473,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private final Runnable mCheckBarModes = this::checkBarModes;
public void setInteracting(int barWindow, boolean interacting) {
- final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting;
mInteractingWindows = interacting
? (mInteractingWindows | barWindow)
: (mInteractingWindows & ~barWindow);
@@ -2460,11 +2481,6 @@ public class StatusBar extends SystemUI implements DemoMode,
} else {
mAutoHideController.resumeSuspendedAutoHide();
}
- // manually dismiss the volume panel when interacting with the nav bar
- if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) {
- mNavigationBarController.touchAutoDim(mDisplayId);
- dismissVolumeDialog();
- }
checkBarModes();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 93df14f18fda..3ae84ecfc2f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -14,9 +14,6 @@
package com.android.systemui.statusbar.phone;
-import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
-import static android.app.StatusBarManager.DISABLE_NONE;
-
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
@@ -47,7 +44,6 @@ import com.android.systemui.statusbar.StatusBarWifiView;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
-import com.android.systemui.util.Utils.DisableStateTracker;
import java.util.List;
@@ -218,14 +214,6 @@ public interface StatusBarIconController {
mContext = group.getContext();
mIconSize = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
-
- DisableStateTracker tracker =
- new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS, commandQueue);
- mGroup.addOnAttachStateChangeListener(tracker);
- if (mGroup.isAttachedToWindow()) {
- // In case we miss the first onAttachedToWindow event
- tracker.onViewAttachedToWindow(mGroup);
- }
}
public boolean isDemoable() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 5bf4b465fa48..d30f01a658f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -75,7 +75,8 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
private boolean mAodPowerSave;
protected boolean mWirelessCharging;
private boolean mTestmode = false;
- private boolean mHasReceivedBattery = false;
+ @VisibleForTesting
+ boolean mHasReceivedBattery = false;
private Estimate mEstimate;
private boolean mFetchingEstimate = false;
@@ -103,6 +104,16 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
@Override
public void init() {
registerReceiver();
+ if (!mHasReceivedBattery) {
+ // Get initial state. Relying on Sticky behavior until API for getting info.
+ Intent intent = mContext.registerReceiver(
+ null,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+ );
+ if (intent != null && !mHasReceivedBattery) {
+ onReceive(mContext, intent);
+ }
+ }
updatePowerSave();
updateEstimate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index da1ef2f0f650..6106f38c0e60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -35,6 +35,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.R;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.Utils;
import java.io.FileDescriptor;
@@ -68,7 +69,7 @@ public class CastControllerImpl implements CastController {
private MediaProjectionInfo mProjection;
@Inject
- public CastControllerImpl(Context context) {
+ public CastControllerImpl(Context context, DumpManager dumpManager) {
mContext = context;
mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mMediaRouter.setRouterGroupId(MediaRouter.MIRRORING_GROUP_ID);
@@ -76,6 +77,7 @@ public class CastControllerImpl implements CastController {
context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
mProjection = mProjectionManager.getActiveProjectionInfo();
mProjectionManager.addCallback(mProjectionCallback, new Handler());
+ dumpManager.registerDumpable(TAG, this);
if (DEBUG) Log.d(TAG, "new CastController()");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 812ce1c62677..95601955ec04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.icu.text.DateTimePatternGenerator;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
@@ -53,8 +54,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
-import libcore.icu.LocaleData;
-
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
@@ -82,6 +81,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
private boolean mClockVisibleByUser = true;
private boolean mAttached;
+ private boolean mScreenReceiverRegistered;
private Calendar mCalendar;
private String mClockFormatString;
private SimpleDateFormat mClockFormat;
@@ -213,6 +213,14 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+ if (mScreenReceiverRegistered) {
+ mScreenReceiverRegistered = false;
+ mBroadcastDispatcher.unregisterReceiver(mScreenReceiver);
+ if (mSecondsHandler != null) {
+ mSecondsHandler.removeCallbacks(mSecondTick);
+ mSecondsHandler = null;
+ }
+ }
if (mAttached) {
mBroadcastDispatcher.unregisterReceiver(mIntentReceiver);
mAttached = false;
@@ -363,12 +371,14 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
mSecondsHandler.postAtTime(mSecondTick,
SystemClock.uptimeMillis() / 1000 * 1000 + 1000);
}
+ mScreenReceiverRegistered = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
mBroadcastDispatcher.registerReceiver(mScreenReceiver, filter);
}
} else {
if (mSecondsHandler != null) {
+ mScreenReceiverRegistered = false;
mBroadcastDispatcher.unregisterReceiver(mScreenReceiver);
mSecondsHandler.removeCallbacks(mSecondTick);
mSecondsHandler = null;
@@ -380,15 +390,16 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
private final CharSequence getSmallTime() {
Context context = getContext();
boolean is24 = DateFormat.is24HourFormat(context, mCurrentUserId);
- LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+ context.getResources().getConfiguration().locale);
final char MAGIC1 = '\uEF00';
final char MAGIC2 = '\uEF01';
SimpleDateFormat sdf;
String format = mShowSeconds
- ? is24 ? d.timeFormat_Hms : d.timeFormat_hms
- : is24 ? d.timeFormat_Hm : d.timeFormat_hm;
+ ? is24 ? dtpg.getBestPattern("Hms") : dtpg.getBestPattern("hms")
+ : is24 ? dtpg.getBestPattern("Hm") : dtpg.getBestPattern("hm");
if (!format.equals(mClockFormatString)) {
mContentDescriptionFormat = new SimpleDateFormat(format);
/*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 0d5a14960850..c43ad36d4462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -106,9 +106,9 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
public void updateNotification(@NonNull String key, boolean alert) {
super.updateNotification(key, alert);
- AlertEntry alertEntry = getHeadsUpEntry(key);
- if (alert && alertEntry != null) {
- setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(alertEntry.mEntry));
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ if (alert && headsUpEntry != null) {
+ setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry));
}
}
@@ -368,7 +368,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
protected boolean expanded;
@Override
- protected boolean isSticky() {
+ public boolean isSticky() {
return (mEntry.isRowPinned() && expanded)
|| remoteInputActive || hasFullScreenIntent(mEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
index 8fcaa67e7614..755938863b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -37,7 +37,6 @@ import android.graphics.Rect;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
-import android.view.ContextThemeWrapper;
import android.view.View;
import com.android.settingslib.Utils;
@@ -80,6 +79,22 @@ public class KeyButtonDrawable extends Drawable {
private final Paint mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private final ShadowDrawableState mState;
private AnimatedVectorDrawable mAnimatedDrawable;
+ private final Callback mAnimatedDrawableCallback = new Callback() {
+ @Override
+ public void invalidateDrawable(@NonNull Drawable who) {
+ invalidateSelf();
+ }
+
+ @Override
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+ scheduleSelf(what, when);
+ }
+
+ @Override
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+ unscheduleSelf(what);
+ }
+ };
public KeyButtonDrawable(Drawable d, @ColorInt int lightColor, @ColorInt int darkColor,
boolean horizontalFlip, Color ovalBackgroundColor) {
@@ -97,6 +112,7 @@ public class KeyButtonDrawable extends Drawable {
}
if (canAnimate()) {
mAnimatedDrawable = (AnimatedVectorDrawable) mState.mChildState.newDrawable().mutate();
+ mAnimatedDrawable.setCallback(mAnimatedDrawableCallback);
setDrawableBounds(mAnimatedDrawable);
}
}
@@ -422,34 +438,6 @@ public class KeyButtonDrawable extends Drawable {
}
/**
- * Creates a KeyButtonDrawable with a shadow given its icon. The tint applied to the drawable
- * is determined by the dark and light theme given by the context.
- * @param ctx Context to get the drawable and determine the dark and light theme
- * @param icon the icon resource id
- * @param hasShadow if a shadow will appear with the drawable
- * @param ovalBackgroundColor the color of the oval bg that will be drawn
- * @return KeyButtonDrawable
- */
- public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
- boolean hasShadow, Color ovalBackgroundColor) {
- final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
- final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
- Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
- Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
- return KeyButtonDrawable.create(lightContext, darkContext, icon, hasShadow,
- ovalBackgroundColor);
- }
-
- /**
- * Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
- * {@link #create(Context, int, boolean, boolean)}.
- */
- public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
- boolean hasShadow) {
- return create(ctx, icon, hasShadow, null /* ovalBackgroundColor */);
- }
-
- /**
* Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
* {@link #create(Context, int, boolean, boolean)}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 512d0f3910f8..f52a6e0191a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -24,7 +24,6 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.database.DataSetObserver;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
@@ -36,6 +35,7 @@ import android.view.ViewStub;
import android.widget.FrameLayout;
import com.android.settingslib.animation.AppearAnimationUtils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -288,7 +288,9 @@ public class KeyguardUserSwitcher {
if (item.picture == null) {
v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
} else {
- Drawable drawable = new BitmapDrawable(v.getResources(), item.picture);
+ int avatarSize =
+ (int) mContext.getResources().getDimension(R.dimen.kg_framed_avatar_size);
+ Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
drawable.setColorFilter(
item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
v.bind(name, drawable, item.info.id);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 3bd33ccca911..adfc14e1d72b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.policy;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+
import static com.android.settingslib.Utils.updateLocationEnabled;
import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -36,6 +36,8 @@ import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.BootCompleteCache;
+import com.android.systemui.appops.AppOpItem;
+import com.android.systemui.appops.AppOpsController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -51,44 +53,35 @@ import javax.inject.Singleton;
* A controller to manage changes of location related states and update the views accordingly.
*/
@Singleton
-public class LocationControllerImpl extends BroadcastReceiver implements LocationController {
-
- private static final int[] mHighPowerRequestAppOpArray
- = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION};
+public class LocationControllerImpl extends BroadcastReceiver implements LocationController,
+ AppOpsController.Callback {
- private Context mContext;
+ private final Context mContext;
+ private final AppOpsController mAppOpsController;
+ private final BootCompleteCache mBootCompleteCache;
+ private final H mHandler;
- private AppOpsManager mAppOpsManager;
- private StatusBarManager mStatusBarManager;
- private BroadcastDispatcher mBroadcastDispatcher;
- private BootCompleteCache mBootCompleteCache;
private boolean mAreActiveLocationRequests;
- private final H mHandler;
-
@Inject
- public LocationControllerImpl(Context context, @Main Looper mainLooper,
- @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher,
- BootCompleteCache bootCompleteCache) {
+ public LocationControllerImpl(Context context, AppOpsController appOpsController,
+ @Main Looper mainLooper, @Background Handler backgroundHandler,
+ BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache) {
mContext = context;
- mBroadcastDispatcher = broadcastDispatcher;
+ mAppOpsController = appOpsController;
mBootCompleteCache = bootCompleteCache;
mHandler = new H(mainLooper);
// Register to listen for changes in location settings.
IntentFilter filter = new IntentFilter();
- filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
filter.addAction(LocationManager.MODE_CHANGED_ACTION);
- mBroadcastDispatcher.registerReceiverWithHandler(this, filter,
- new Handler(bgLooper), UserHandle.ALL);
+ broadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler, UserHandle.ALL);
- mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- mStatusBarManager
- = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
+ mAppOpsController.addCallback(new int[]{OP_MONITOR_HIGH_POWER_LOCATION}, this);
// Examine the current location state and initialize the status view.
- updateActiveLocationRequests();
+ backgroundHandler.post(this::updateActiveLocationRequests);
}
/**
@@ -160,27 +153,12 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
*/
@VisibleForTesting
protected boolean areActiveHighPowerLocationRequests() {
- List<AppOpsManager.PackageOps> packages
- = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray);
- // AppOpsManager can return null when there is no requested data.
- if (packages != null) {
- final int numPackages = packages.size();
- for (int packageInd = 0; packageInd < numPackages; packageInd++) {
- AppOpsManager.PackageOps packageOp = packages.get(packageInd);
- List<AppOpsManager.OpEntry> opEntries = packageOp.getOps();
- if (opEntries != null) {
- final int numOps = opEntries.size();
- for (int opInd = 0; opInd < numOps; opInd++) {
- AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
- // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because
- // of the mHighPowerRequestAppOpArray filter, but checking defensively.
- if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
- if (opEntry.isRunning()) {
- return true;
- }
- }
- }
- }
+ List<AppOpItem> appOpsItems = mAppOpsController.getActiveAppOps();
+
+ final int numItems = appOpsItems.size();
+ for (int i = 0; i < numItems; i++) {
+ if (appOpsItems.get(i).getCode() == OP_MONITOR_HIGH_POWER_LOCATION) {
+ return true;
}
}
@@ -198,14 +176,16 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
@Override
public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
- updateActiveLocationRequests();
- } else if (LocationManager.MODE_CHANGED_ACTION.equals(action)) {
- mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED);
+ if (LocationManager.MODE_CHANGED_ACTION.equals(intent.getAction())) {
+ mHandler.locationSettingsChanged();
}
}
+ @Override
+ public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
+ updateActiveLocationRequests();
+ }
+
private final class H extends Handler {
private static final int MSG_LOCATION_SETTINGS_CHANGED = 1;
private static final int MSG_LOCATION_ACTIVE_CHANGED = 2;
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 18a7adda3f7d..cf83603997c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -284,6 +284,9 @@ public class MobileSignalController extends SignalController<
mNetworkToIconLookup.put(toDisplayIconKey(
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE),
TelephonyIcons.NR_5G_PLUS);
+ mNetworkToIconLookup.put(toIconKey(
+ TelephonyManager.NETWORK_TYPE_NR),
+ TelephonyIcons.NR_5G);
}
private String getIconKey() {
@@ -306,9 +309,9 @@ public class MobileSignalController extends SignalController<
case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO:
return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA_Plus";
case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA:
- return "5G";
+ return toIconKey(TelephonyManager.NETWORK_TYPE_NR);
case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE:
- return "5G_Plus";
+ return toIconKey(TelephonyManager.NETWORK_TYPE_NR) + "_Plus";
default:
return "unsupported";
}
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 3c0bee8d12ab..32c4aec39923 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -24,6 +24,7 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NO
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -175,7 +176,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
public NetworkControllerImpl(Context context, @Background Looper bgLooper,
DeviceProvisionedController deviceProvisionedController,
BroadcastDispatcher broadcastDispatcher, ConnectivityManager connectivityManager,
- TelephonyManager telephonyManager, WifiManager wifiManager,
+ TelephonyManager telephonyManager, @Nullable WifiManager wifiManager,
NetworkScoreManager networkScoreManager) {
this(context, connectivityManager,
telephonyManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 53ac65700a05..9c3395f9332d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -79,7 +79,7 @@ import java.util.function.Consumer;
/**
* Host for the remote input.
*/
-public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
+public class RemoteInputView extends LinearLayout implements View.OnClickListener {
private static final String TAG = "RemoteInput";
@@ -88,6 +88,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
public final Object mToken = new Object();
+ private final SendButtonTextWatcher mTextWatcher;
+ private final TextView.OnEditorActionListener mEditorActionHandler;
private RemoteEditText mEditText;
private ImageButton mSendButton;
private ProgressBar mProgressBar;
@@ -113,6 +115,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mTextWatcher = new SendButtonTextWatcher();
+ mEditorActionHandler = new EditorActionHandler();
mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
mStatusBarManagerService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -128,30 +132,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mSendButton.setOnClickListener(this);
mEditText = (RemoteEditText) getChildAt(0);
- mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- final boolean isSoftImeEvent = event == null
- && (actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT
- || actionId == EditorInfo.IME_ACTION_SEND);
- final boolean isKeyboardEnterKey = event != null
- && KeyEvent.isConfirmKey(event.getKeyCode())
- && event.getAction() == KeyEvent.ACTION_DOWN;
-
- if (isSoftImeEvent || isKeyboardEnterKey) {
- if (mEditText.length() > 0) {
- sendRemoteInput(prepareRemoteInputFromText());
- }
- // Consume action to prevent IME from closing.
- return true;
- }
- return false;
- }
- });
- mEditText.addTextChangedListener(this);
mEditText.setInnerFocusable(false);
- mEditText.mRemoteInputView = this;
}
protected Intent prepareRemoteInputFromText() {
@@ -292,6 +273,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
+ mEditText.mRemoteInputView = this;
+ mEditText.setOnEditorActionListener(mEditorActionHandler);
+ mEditText.addTextChangedListener(mTextWatcher);
if (mEntry.getRow().isChangingPosition()) {
if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
mEditText.requestFocus();
@@ -302,6 +286,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+ mEditText.removeTextChangedListener(mTextWatcher);
+ mEditText.setOnEditorActionListener(null);
+ mEditText.mRemoteInputView = null;
if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
return;
}
@@ -412,17 +399,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mSendButton.setEnabled(mEditText.getText().length() != 0);
}
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
-
- @Override
- public void afterTextChanged(Editable s) {
- updateSendButton();
- }
-
public void close() {
mEditText.defocusIfNeeded(false /* animated */);
}
@@ -545,6 +521,45 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken);
}
+ /** Handler for button click on send action in IME. */
+ private class EditorActionHandler implements TextView.OnEditorActionListener {
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ final boolean isSoftImeEvent = event == null
+ && (actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT
+ || actionId == EditorInfo.IME_ACTION_SEND);
+ final boolean isKeyboardEnterKey = event != null
+ && KeyEvent.isConfirmKey(event.getKeyCode())
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+
+ if (isSoftImeEvent || isKeyboardEnterKey) {
+ if (mEditText.length() > 0) {
+ sendRemoteInput(prepareRemoteInputFromText());
+ }
+ // Consume action to prevent IME from closing.
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /** Observes text change events and updates the visibility of the send button accordingly. */
+ private class SendButtonTextWatcher implements TextWatcher {
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ updateSendButton();
+ }
+ }
+
/**
* An EditText that changes appearance based on whether it's focusable and becomes
* un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -599,7 +614,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
if (!focused) {
defocusIfNeeded(true /* animate */);
}
- if (!mRemoteInputView.mRemoved) {
+ if (mRemoteInputView != null && !mRemoteInputView.mRemoved) {
mLightBarController.setDirectReplying(focused);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 6bc0565510a2..c0602762ef29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -26,6 +26,7 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.statusbar.CommandQueue;
@@ -72,6 +73,11 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
+
+ if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) {
+ // Creating AudioRecordingDisclosureBar and just letting it run
+ new AudioRecordingDisclosureBar(mContext);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
index 36e360da857f..8e4e12358836 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
@@ -112,6 +112,13 @@ public class AudioRecordingDisclosureBar implements
*/
private final AudioActivityObserver[] mAudioActivityObservers;
/**
+ * Whether the indicator should expand and show the recording application's label.
+ * If disabled ({@code false}) the "minimized" ({@link #STATE_MINIMIZED}) indicator would appear
+ * on the screen whenever an application is recording, but will not reveal to the user what
+ * application this is.
+ */
+ private final boolean mRevealRecordingPackages;
+ /**
* Set of applications that we've notified the user about since the indicator came up. Meaning
* that if an application is in this list then at some point since the indicator came up, it
* was expanded showing this application's title.
@@ -137,6 +144,8 @@ public class AudioRecordingDisclosureBar implements
public AudioRecordingDisclosureBar(Context context) {
mContext = context;
+ mRevealRecordingPackages = mContext.getResources().getBoolean(
+ R.bool.audio_recording_disclosure_reveal_packages);
mExemptPackages = new ArraySet<>(
Arrays.asList(mContext.getResources().getStringArray(
R.array.audio_recording_disclosure_exempt_apps)));
@@ -149,11 +158,6 @@ public class AudioRecordingDisclosureBar implements
};
}
- private String[] getGlobalStringArray(String setting) {
- String result = Settings.Global.getString(mContext.getContentResolver(), setting);
- return TextUtils.isEmpty(result) ? new String[0] : result.split(",");
- }
-
@UiThread
@Override
public void onAudioActivityStateChange(boolean active, String packageName) {
@@ -190,7 +194,9 @@ public class AudioRecordingDisclosureBar implements
break;
case STATE_MINIMIZED:
- expand(packageName);
+ if (mRevealRecordingPackages) {
+ expand(packageName);
+ }
break;
case STATE_DISAPPEARING:
@@ -228,9 +234,8 @@ public class AudioRecordingDisclosureBar implements
@UiThread
private void show(String packageName) {
- final String label = getApplicationLabel(packageName);
if (DEBUG) {
- Log.d(TAG, "Showing indicator for " + packageName + " (" + label + ")...");
+ Log.d(TAG, "Showing indicator for " + packageName);
}
mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection()
@@ -247,19 +252,31 @@ public class AudioRecordingDisclosureBar implements
mTextView = mTextsContainers.findViewById(R.id.text);
mBgEnd = mIndicatorView.findViewById(R.id.bg_end);
- // Swap background drawables depending on layout directions (both drawables have rounded
- // corners only on one side)
- if (mIsLtr) {
- mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
- mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
+ // Set up the notification text
+ if (mRevealRecordingPackages) {
+ // Swap background drawables depending on layout directions (both drawables have rounded
+ // corners only on one side)
+ if (mIsLtr) {
+ mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
+ mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
+ } else {
+ mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
+ mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
+ }
+
+ final String label = getApplicationLabel(packageName);
+ mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
} else {
- mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
- mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
+ mTextsContainers.setVisibility(View.GONE);
+ mIconContainerBg.setVisibility(View.GONE);
+ mTextView.setVisibility(View.GONE);
+ mBgEnd.setVisibility(View.GONE);
+ mTextsContainers = null;
+ mIconContainerBg = null;
+ mTextView = null;
+ mBgEnd = null;
}
- // Set up the notification text
- mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
-
// Initially change the visibility to INVISIBLE, wait until and receives the size and
// then animate it moving from "off" the screen correctly
mIndicatorView.setVisibility(View.INVISIBLE);
@@ -296,7 +313,11 @@ public class AudioRecordingDisclosureBar implements
@Override
public void onAnimationEnd(Animator animation) {
startPulsatingAnimation();
- onExpanded();
+ if (mRevealRecordingPackages) {
+ onExpanded();
+ } else {
+ onMinimized();
+ }
}
});
set.start();
@@ -321,6 +342,8 @@ public class AudioRecordingDisclosureBar implements
@UiThread
private void expand(String packageName) {
+ assertRevealingRecordingPackages();
+
final String label = getApplicationLabel(packageName);
if (DEBUG) {
Log.d(TAG, "Expanding for " + packageName + " (" + label + ")...");
@@ -348,6 +371,8 @@ public class AudioRecordingDisclosureBar implements
@UiThread
private void minimize() {
+ assertRevealingRecordingPackages();
+
if (DEBUG) Log.d(TAG, "Minimizing...");
final int targetOffset = (mIsLtr ? 1 : -1) * mTextsContainers.getWidth();
final AnimatorSet set = new AnimatorSet();
@@ -393,6 +418,8 @@ public class AudioRecordingDisclosureBar implements
@UiThread
private void onExpanded() {
+ assertRevealingRecordingPackages();
+
if (DEBUG) Log.d(TAG, "Expanded");
mState = STATE_SHOWN;
@@ -404,11 +431,13 @@ public class AudioRecordingDisclosureBar implements
if (DEBUG) Log.d(TAG, "Minimized");
mState = STATE_MINIMIZED;
- if (!mPendingNotificationPackages.isEmpty()) {
- // There is a new application that started recording, tell the user about it.
- expand(mPendingNotificationPackages.poll());
- } else {
- hideIndicatorIfNeeded();
+ if (mRevealRecordingPackages) {
+ if (!mPendingNotificationPackages.isEmpty()) {
+ // There is a new application that started recording, tell the user about it.
+ expand(mPendingNotificationPackages.poll());
+ } else {
+ hideIndicatorIfNeeded();
+ }
}
}
@@ -451,7 +480,14 @@ public class AudioRecordingDisclosureBar implements
animator.start();
}
+ private String[] getGlobalStringArray(String setting) {
+ String result = Settings.Global.getString(mContext.getContentResolver(), setting);
+ return TextUtils.isEmpty(result) ? new String[0] : result.split(",");
+ }
+
private String getApplicationLabel(String packageName) {
+ assertRevealingRecordingPackages();
+
final PackageManager pm = mContext.getPackageManager();
final ApplicationInfo appInfo;
try {
@@ -461,4 +497,11 @@ public class AudioRecordingDisclosureBar implements
}
return pm.getApplicationLabel(appInfo).toString();
}
+
+ private void assertRevealingRecordingPackages() {
+ if (!mRevealRecordingPackages) {
+ Log.e(TAG, "Not revealing recording packages",
+ DEBUG ? new RuntimeException("Should not be called") : null);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
index 2c8297cbfd88..dce38c109930 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
@@ -16,8 +16,6 @@
package com.android.systemui.tv;
-import android.content.Context;
-
import com.android.systemui.dagger.DefaultComponentBinder;
import com.android.systemui.dagger.DependencyBinder;
import com.android.systemui.dagger.DependencyProvider;
@@ -30,7 +28,6 @@ import com.android.systemui.onehanded.dagger.OneHandedModule;
import javax.inject.Singleton;
-import dagger.BindsInstance;
import dagger.Component;
/**
@@ -52,9 +49,7 @@ public interface TvSystemUIRootComponent extends SystemUIRootComponent {
* Component Builder interface. This allows to bind Context instance in the component
*/
@Component.Builder
- interface Builder {
- @BindsInstance Builder context(Context context);
-
+ interface Builder extends SystemUIRootComponent.Builder {
TvSystemUIRootComponent build();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index bc2a55c8d5c4..b1241b160d70 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -70,6 +70,8 @@ public class UsbDebuggingActivity extends AlertActivity
if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+ IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+ mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
}
Intent intent = getIntent();
@@ -119,6 +121,7 @@ public class UsbDebuggingActivity extends AlertActivity
}
boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
if (!connected) {
+ Log.d(TAG, "USB disconnected, notifying service");
notifyService(false);
mActivity.finish();
}
@@ -126,26 +129,22 @@ public class UsbDebuggingActivity extends AlertActivity
}
@Override
- public void onStart() {
- super.onStart();
- if (mDisconnectedReceiver != null) {
- IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
- mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
- }
- }
-
- @Override
- protected void onStop() {
+ protected void onDestroy() {
if (mDisconnectedReceiver != null) {
mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
}
- // If the ADB service has not yet been notified due to this dialog being closed in some
- // other way then notify the service to deny the connection to ensure system_server sends
- // a response to adbd.
- if (!mServiceNotified) {
- notifyService(false);
+ // Only notify the service if the activity is finishing; if onDestroy has been called due to
+ // a configuration change then allow the user to still authorize the connection the next
+ // time the activity is in the foreground.
+ if (isFinishing()) {
+ // If the ADB service has not yet been notified due to this dialog being closed in some
+ // other way then notify the service to deny the connection to ensure system_server
+ // sends a response to adbd.
+ if (!mServiceNotified) {
+ notifyService(false);
+ }
}
- super.onStop();
+ super.onDestroy();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java
index 3f05657ed09e..e08936cbf75e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Assert.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java
@@ -25,16 +25,21 @@ import androidx.annotation.VisibleForTesting;
*/
public class Assert {
private static final Looper sMainLooper = Looper.getMainLooper();
- private static Looper sTestLooper = null;
+ private static Thread sTestThread = null;
@VisibleForTesting
public static void setTestableLooper(Looper testLooper) {
- sTestLooper = testLooper;
+ setTestThread(testLooper == null ? null : testLooper.getThread());
+ }
+
+ @VisibleForTesting
+ public static void setTestThread(Thread thread) {
+ sTestThread = thread;
}
public static void isMainThread() {
if (!sMainLooper.isCurrentThread()
- && (sTestLooper == null || !sTestLooper.isCurrentThread())) {
+ && (sTestThread == null || sTestThread != Thread.currentThread())) {
throw new IllegalStateException("should be called from the main thread."
+ " sMainLooper.threadName=" + sMainLooper.getThread().getName()
+ " Thread.currentThread()=" + Thread.currentThread().getName());
@@ -43,7 +48,7 @@ public class Assert {
public static void isNotMainThread() {
if (sMainLooper.isCurrentThread()
- && (sTestLooper == null || sTestLooper.isCurrentThread())) {
+ && (sTestThread == null || sTestThread == Thread.currentThread())) {
throw new IllegalStateException("should not be called from the main thread.");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java b/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java
index a31ea7c3ab17..8946c97a4b58 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java
+++ b/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java
@@ -40,7 +40,7 @@ public class DismissCircleView extends FrameLayout {
setBackground(res.getDrawable(R.drawable.dismiss_circle_background));
- mIconView.setImageDrawable(res.getDrawable(R.drawable.ic_close_white));
+ mIconView.setImageDrawable(res.getDrawable(R.drawable.pip_ic_close_white));
addView(mIconView);
setViewSizes();
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
index 016f4de724b6..2a5424ce4ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
@@ -20,6 +20,7 @@ import android.os.Looper
import android.util.ArrayMap
import android.util.Log
import android.view.View
+import androidx.dynamicanimation.animation.AnimationHandler
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FlingAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
@@ -124,6 +125,12 @@ class PhysicsAnimator<T> private constructor (target: T) {
private var defaultFling: FlingConfig = globalDefaultFling
/**
+ * AnimationHandler to use if it need custom AnimationHandler, if this is null, it will use
+ * the default AnimationHandler in the DynamicAnimation.
+ */
+ private var customAnimationHandler: AnimationHandler? = null
+
+ /**
* Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to
* the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add
* just one permanent update and end listener to the DynamicAnimations.
@@ -447,6 +454,14 @@ class PhysicsAnimator<T> private constructor (target: T) {
this.defaultFling = defaultFling
}
+ /**
+ * Set the custom AnimationHandler for all aniatmion in this animator. Set this with null for
+ * restoring to default AnimationHandler.
+ */
+ fun setCustomAnimationHandler(handler: AnimationHandler) {
+ this.customAnimationHandler = handler
+ }
+
/** Starts the animations! */
fun start() {
startAction()
@@ -501,10 +516,13 @@ class PhysicsAnimator<T> private constructor (target: T) {
// springs) on this property before flinging.
cancel(animatedProperty)
+ // Apply the custom animation handler if it not null
+ val flingAnim = getFlingAnimation(animatedProperty, target)
+ flingAnim.animationHandler =
+ customAnimationHandler ?: flingAnim.animationHandler
+
// Apply the configuration and start the animation.
- getFlingAnimation(animatedProperty, target)
- .also { flingConfig.applyToAnimation(it) }
- .start()
+ flingAnim.also { flingConfig.applyToAnimation(it) }.start()
}
}
@@ -516,6 +534,21 @@ class PhysicsAnimator<T> private constructor (target: T) {
if (flingConfig == null) {
// Apply the configuration and start the animation.
val springAnim = getSpringAnimation(animatedProperty, target)
+
+ // If customAnimationHander is exist and has not been set to the animation,
+ // it should set here.
+ if (customAnimationHandler != null &&
+ springAnim.animationHandler != customAnimationHandler) {
+ // Cancel the animation before set animation handler
+ if (springAnim.isRunning) {
+ cancel(animatedProperty)
+ }
+ // Apply the custom animation handler if it not null
+ springAnim.animationHandler =
+ customAnimationHandler ?: springAnim.animationHandler
+ }
+
+ // Apply the configuration and start the animation.
springConfig.applyToAnimation(springAnim)
animationStartActions.add(springAnim::start)
} else {
@@ -570,10 +603,13 @@ class PhysicsAnimator<T> private constructor (target: T) {
}
}
+ // Apply the custom animation handler if it not null
+ val springAnim = getSpringAnimation(animatedProperty, target)
+ springAnim.animationHandler =
+ customAnimationHandler ?: springAnim.animationHandler
+
// Apply the configuration and start the spring animation.
- getSpringAnimation(animatedProperty, target)
- .also { springConfig.applyToAnimation(it) }
- .start()
+ springAnim.also { springConfig.applyToAnimation(it) }.start()
}
}
})
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
index 3c0a23aa2eca..3347cf6ca2a4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
@@ -20,9 +20,11 @@ import android.content.Context
import android.graphics.Canvas
import android.graphics.PointF
import android.graphics.Rect
+import android.text.Layout
import android.util.AttributeSet
import android.view.View
import android.view.ViewTreeObserver
+import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.statusbar.CrossFadeHelper
@@ -45,12 +47,29 @@ class TransitionLayout @JvmOverloads constructor(
private var currentState: TransitionViewState = TransitionViewState()
private var updateScheduled = false
+ private var desiredMeasureWidth = 0
+ private var desiredMeasureHeight = 0
/**
* The measured state of this view which is the one we will lay ourselves out with. This
* may differ from the currentState if there is an external animation or transition running.
* This state will not be used to measure the widgets, where the current state is preferred.
*/
var measureState: TransitionViewState = TransitionViewState()
+ set(value) {
+ val newWidth = value.width
+ val newHeight = value.height
+ if (newWidth != desiredMeasureWidth || newHeight != desiredMeasureHeight) {
+ desiredMeasureWidth = newWidth
+ desiredMeasureHeight = newHeight
+ // We need to make sure next time we're measured that our onMeasure will be called.
+ // Otherwise our parent thinks we still have the same height
+ if (isInLayout()) {
+ forceLayout()
+ } else {
+ requestLayout()
+ }
+ }
+ }
private val preDrawApplicator = object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
updateScheduled = false
@@ -80,9 +99,28 @@ class TransitionLayout @JvmOverloads constructor(
*/
private fun applyCurrentState() {
val childCount = childCount
+ val contentTranslationX = currentState.contentTranslation.x.toInt()
+ val contentTranslationY = currentState.contentTranslation.y.toInt()
for (i in 0 until childCount) {
val child = getChildAt(i)
val widgetState = currentState.widgetStates.get(child.id) ?: continue
+
+ // TextViews which are measured and sized differently should be handled with a
+ // "clip mode", which means we clip explicitly rather than implicitly by passing
+ // different sizes to measure/layout than setLeftTopRightBottom.
+ // Then to accommodate RTL text, we need a "clip shift" which allows us to have the
+ // clipBounds be attached to the right side of the view instead of the left.
+ val clipModeShift =
+ if (child is TextView && widgetState.width < widgetState.measureWidth) {
+ if (child.layout.getParagraphDirection(0) == Layout.DIR_RIGHT_TO_LEFT) {
+ widgetState.measureWidth - widgetState.width
+ } else {
+ 0
+ }
+ } else {
+ null
+ }
+
if (child.measuredWidth != widgetState.measureWidth ||
child.measuredHeight != widgetState.measureHeight) {
val measureWidthSpec = MeasureSpec.makeMeasureSpec(widgetState.measureWidth,
@@ -92,14 +130,17 @@ class TransitionLayout @JvmOverloads constructor(
child.measure(measureWidthSpec, measureHeightSpec)
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
}
- val left = widgetState.x.toInt()
- val top = widgetState.y.toInt()
- child.setLeftTopRightBottom(left, top, left + widgetState.width,
- top + widgetState.height)
+ val clipShift = clipModeShift ?: 0
+ val left = widgetState.x.toInt() + contentTranslationX - clipShift
+ val top = widgetState.y.toInt() + contentTranslationY
+ val clipMode = clipModeShift != null
+ val boundsWidth = if (clipMode) widgetState.measureWidth else widgetState.width
+ val boundsHeight = if (clipMode) widgetState.measureHeight else widgetState.height
+ child.setLeftTopRightBottom(left, top, left + boundsWidth, top + boundsHeight)
child.scaleX = widgetState.scale
child.scaleY = widgetState.scale
val clipBounds = child.clipBounds ?: Rect()
- clipBounds.set(0, 0, widgetState.width, widgetState.height)
+ clipBounds.set(clipShift, 0, widgetState.width + clipShift, widgetState.height)
child.clipBounds = clipBounds
CrossFadeHelper.fadeIn(child, widgetState.alpha)
child.visibility = if (widgetState.gone || widgetState.alpha == 0.0f) {
@@ -109,6 +150,9 @@ class TransitionLayout @JvmOverloads constructor(
}
}
updateBounds()
+ translationX = currentState.translation.x
+ translationY = currentState.translation.y
+ CrossFadeHelper.fadeIn(this, currentState.alpha)
}
private fun applyCurrentStateOnPredraw() {
@@ -131,7 +175,7 @@ class TransitionLayout @JvmOverloads constructor(
MeasureSpec.EXACTLY)
child.measure(measureWidthSpec, measureHeightSpec)
}
- setMeasuredDimension(measureState.width, measureState.height)
+ setMeasuredDimension(desiredMeasureWidth, desiredMeasureHeight)
}
}
@@ -161,9 +205,7 @@ class TransitionLayout @JvmOverloads constructor(
val layoutTop = top
setLeftTopRightBottom(layoutLeft, layoutTop, layoutLeft + currentState.width,
layoutTop + currentState.height)
- translationX = currentState.translation.x
- translationY = currentState.translation.y
- boundsRect.set(0, 0, (width + translationX).toInt(), (height + translationY).toInt())
+ boundsRect.set(0, 0, width.toInt(), height.toInt())
}
/**
@@ -247,13 +289,17 @@ class TransitionViewState {
var widgetStates: MutableMap<Int, WidgetState> = mutableMapOf()
var width: Int = 0
var height: Int = 0
+ var alpha: Float = 1.0f
val translation = PointF()
+ val contentTranslation = PointF()
fun copy(reusedState: TransitionViewState? = null): TransitionViewState {
// we need a deep copy of this, so we can't use a data class
val copy = reusedState ?: TransitionViewState()
copy.width = width
copy.height = height
+ copy.alpha = alpha
copy.translation.set(translation.x, translation.y)
+ copy.contentTranslation.set(contentTranslation.x, contentTranslation.y)
for (entry in widgetStates) {
copy.widgetStates[entry.key] = entry.value.copy()
}
@@ -272,6 +318,8 @@ class TransitionViewState {
width = transitionLayout.measuredWidth
height = transitionLayout.measuredHeight
translation.set(0.0f, 0.0f)
+ contentTranslation.set(0.0f, 0.0f)
+ alpha = 1.0f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
index 5143e429768e..9638ac1096f7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
@@ -19,6 +19,7 @@ package com.android.systemui.util.animation
import android.animation.ValueAnimator
import android.graphics.PointF
import android.util.MathUtils
+import com.android.internal.R.attr.width
import com.android.systemui.Interpolators
/**
@@ -44,7 +45,6 @@ open class TransitionLayoutController {
private var currentState = TransitionViewState()
private var animationStartState: TransitionViewState? = null
private var state = TransitionViewState()
- private var pivot = PointF()
private var animator: ValueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
private var currentHeight: Int = 0
private var currentWidth: Int = 0
@@ -63,13 +63,11 @@ open class TransitionLayoutController {
if (animationStartState == null || !animator.isRunning) {
return
}
- val view = transitionLayout ?: return
- getInterpolatedState(
+ currentState = getInterpolatedState(
startState = animationStartState!!,
endState = state,
progress = animator.animatedFraction,
- pivot = pivot,
- resultState = currentState)
+ reusedState = currentState)
applyStateToLayout(currentState)
}
@@ -83,6 +81,49 @@ open class TransitionLayoutController {
}
/**
+ * Obtain a state that is gone, based on parameters given.
+ *
+ * @param viewState the viewState to make gone
+ * @param disappearParameters parameters that determine how the view should disappear
+ * @param goneProgress how much is the view gone? 0 for not gone at all and 1 for fully
+ * disappeared
+ * @param reusedState optional parameter for state to be reused to avoid allocations
+ */
+ fun getGoneState(
+ viewState: TransitionViewState,
+ disappearParameters: DisappearParameters,
+ goneProgress: Float,
+ reusedState: TransitionViewState? = null
+ ): TransitionViewState {
+ var remappedProgress = MathUtils.map(
+ disappearParameters.disappearStart,
+ disappearParameters.disappearEnd,
+ 0.0f, 1.0f,
+ goneProgress)
+ remappedProgress = MathUtils.constrain(remappedProgress, 0.0f, 1.0f)
+ val result = viewState.copy(reusedState).apply {
+ width = MathUtils.lerp(
+ viewState.width.toFloat(),
+ viewState.width * disappearParameters.disappearSize.x,
+ remappedProgress).toInt()
+ height = MathUtils.lerp(
+ viewState.height.toFloat(),
+ viewState.height * disappearParameters.disappearSize.y,
+ remappedProgress).toInt()
+ translation.x = (viewState.width - width) * disappearParameters.gonePivot.x
+ translation.y = (viewState.height - height) * disappearParameters.gonePivot.y
+ contentTranslation.x = (disappearParameters.contentTranslationFraction.x - 1.0f) *
+ translation.x
+ contentTranslation.y = (disappearParameters.contentTranslationFraction.y - 1.0f) *
+ translation.y
+ val alphaProgress = MathUtils.map(
+ disappearParameters.fadeStartPosition, 1.0f, 1.0f, 0.0f, remappedProgress)
+ alpha = MathUtils.constrain(alphaProgress, 0.0f, 1.0f)
+ }
+ return result
+ }
+
+ /**
* Get an interpolated state between two viewstates. This interpolates all positions for all
* widgets as well as it's bounds based on the given input.
*/
@@ -90,11 +131,10 @@ open class TransitionLayoutController {
startState: TransitionViewState,
endState: TransitionViewState,
progress: Float,
- pivot: PointF,
- resultState: TransitionViewState
- ) {
- this.pivot.set(pivot)
- val view = transitionLayout ?: return
+ reusedState: TransitionViewState? = null
+ ): TransitionViewState {
+ val resultState = reusedState ?: TransitionViewState()
+ val view = transitionLayout ?: return resultState
val childCount = view.childCount
for (i in 0 until childCount) {
val id = view.getChildAt(i).id
@@ -195,9 +235,21 @@ open class TransitionLayoutController {
progress).toInt()
height = MathUtils.lerp(startState.height.toFloat(), endState.height.toFloat(),
progress).toInt()
- translation.x = (endState.width - width) * pivot.x
- translation.y = (endState.height - height) * pivot.y
+ translation.x = MathUtils.lerp(startState.translation.x, endState.translation.x,
+ progress)
+ translation.y = MathUtils.lerp(startState.translation.y, endState.translation.y,
+ progress)
+ alpha = MathUtils.lerp(startState.alpha, endState.alpha, progress)
+ contentTranslation.x = MathUtils.lerp(
+ startState.contentTranslation.x,
+ endState.contentTranslation.x,
+ progress)
+ contentTranslation.y = MathUtils.lerp(
+ startState.contentTranslation.y,
+ endState.contentTranslation.y,
+ progress)
}
+ return resultState
}
fun attach(transitionLayout: TransitionLayout) {
@@ -250,3 +302,97 @@ open class TransitionLayoutController {
transitionLayout?.measureState = state
}
}
+
+class DisappearParameters() {
+
+ /**
+ * The pivot point when clipping view when disappearing, which describes how the content will
+ * be translated.
+ * The default value of (0.0f, 1.0f) means that the view will not be translated in horizontally
+ * and the vertical disappearing will be aligned on the bottom of the view,
+ */
+ var gonePivot = PointF(0.0f, 1.0f)
+
+ /**
+ * The fraction of the width and height that will remain when disappearing. The default of
+ * (1.0f, 0.0f) means that 100% of the width, but 0% of the height will remain at the end of
+ * the transition.
+ */
+ var disappearSize = PointF(1.0f, 0.0f)
+
+ /**
+ * The fraction of the normal translation, by which the content will be moved during the
+ * disappearing. The values here can be both negative as well as positive. The default value
+ * of (0.0f, 0.2f) means that the content doesn't move horizontally but moves 20% of the
+ * translation imposed by the pivot downwards. 1.0f means that the content will be translated
+ * in sync with the translation of the bounds
+ */
+ var contentTranslationFraction = PointF(0.0f, 0.8f)
+
+ /**
+ * The point during the progress from [0.0, 1.0f] where the view is fully appeared. 0.0f
+ * means that the content will start disappearing immediately, while 0.5f means that it
+ * starts disappearing half way through the progress.
+ */
+ var disappearStart = 0.0f
+
+ /**
+ * The point during the progress from [0.0, 1.0f] where the view has fully disappeared. 1.0f
+ * means that the view will disappear in sync with the progress, while 0.5f means that it
+ * is fully gone half way through the progress.
+ */
+ var disappearEnd = 1.0f
+
+ /**
+ * The point during the mapped progress from [0.0, 1.0f] where the view starts fading out. 1.0f
+ * means that the view doesn't fade at all, while 0.5 means that the content fades starts
+ * fading at the midpoint between [disappearStart] and [disappearEnd]
+ */
+ var fadeStartPosition = 0.9f
+
+ override fun equals(other: Any?): Boolean {
+ if (!(other is DisappearParameters)) {
+ return false
+ }
+ if (!disappearSize.equals(other.disappearSize)) {
+ return false
+ }
+ if (!gonePivot.equals(other.gonePivot)) {
+ return false
+ }
+ if (!contentTranslationFraction.equals(other.contentTranslationFraction)) {
+ return false
+ }
+ if (disappearStart != other.disappearStart) {
+ return false
+ }
+ if (disappearEnd != other.disappearEnd) {
+ return false
+ }
+ if (fadeStartPosition != other.fadeStartPosition) {
+ return false
+ }
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = disappearSize.hashCode()
+ result = 31 * result + gonePivot.hashCode()
+ result = 31 * result + contentTranslationFraction.hashCode()
+ result = 31 * result + disappearStart.hashCode()
+ result = 31 * result + disappearEnd.hashCode()
+ result = 31 * result + fadeStartPosition.hashCode()
+ return result
+ }
+
+ fun deepCopy(): DisappearParameters {
+ val result = DisappearParameters()
+ result.disappearSize.set(disappearSize)
+ result.gonePivot.set(gonePivot)
+ result.contentTranslationFraction.set(contentTranslationFraction)
+ result.disappearStart = disappearStart
+ result.disappearEnd = disappearEnd
+ result.fadeStartPosition = fadeStartPosition
+ return result
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt b/packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt
index d6e7a8b28f0c..47a2d35e57f5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt
@@ -53,20 +53,20 @@ class UniqueObjectHostView(
// size.
val (cachedWidth, cachedHeight) = measurementManager.onMeasure(measurementInput)
- if (!isCurrentHost()) {
- // We're not currently the host, let's use the dimension from our cache
- // The goal here is that the view will always have a consistent measuring, regardless
- // if it's attached or not.
- // The behavior is therefore very similar to the view being persistently attached to
- // this host, which can prevent flickers. It also makes sure that we always know
- // the size of the view during transitions even if it has never been attached here
- // before.
- setMeasuredDimension(cachedWidth + paddingHorizontal, cachedHeight + paddingVertical)
- } else {
+ if (isCurrentHost()) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- // Let's update our cache
getChildAt(0)?.requiresRemeasuring = false
}
+ // The goal here is that the view will always have a consistent measuring, regardless
+ // if it's attached or not.
+ // The behavior is therefore very similar to the view being persistently attached to
+ // this host, which can prevent flickers. It also makes sure that we always know
+ // the size of the view during transitions even if it has never been attached here
+ // before.
+ // We previously still measured the size when the view was attached, but this doesn't
+ // work properly because we can set the measuredState while still attached to the
+ // old host, which will trigger an inconsistency in height
+ setMeasuredDimension(cachedWidth + paddingHorizontal, cachedHeight + paddingVertical)
}
override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
index 7729965b56c4..bf22a9897d16 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
@@ -32,6 +32,7 @@ import java.util.concurrent.Executors;
import javax.inject.Singleton;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -199,4 +200,10 @@ public abstract class ConcurrencyModule {
public static Executor provideUiBackgroundExecutor() {
return Executors.newSingleThreadExecutor();
}
+
+ /**
+ * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
+ */
+ @Binds
+ public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
new file mode 100644
index 000000000000..0352fb51bc21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Factory for building Executors running on a unique named thread.
+ *
+ * Use this when our generally available @Main, @Background, @UiBackground, @LongRunning, or
+ * similar global qualifiers don't quite cut it. Note that the methods here create entirely new
+ * threads; there are no singletons here. Use responsibly.
+ */
+public interface ThreadFactory {
+ /**
+ * Return an {@link java.util.concurrent.Executor} running on a named thread.
+ *
+ * The thread is implicitly started and may be left running indefinitely, depending on the
+ * implementation. Assume this is the case and use responsibly.
+ **/
+ Executor buildExecutorOnNewThread(String threadName);
+
+ /**
+ * Return an {@link DelayableExecutor} running on a named thread.
+ *
+ * The thread is implicitly started and may be left running indefinitely, depending on the
+ * implementation. Assume this is the case and use responsibly.
+ **/
+ DelayableExecutor buildDelayableExecutorOnNewThread(String threadName);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
new file mode 100644
index 000000000000..ca8d83607634
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import android.os.HandlerThread;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+class ThreadFactoryImpl implements ThreadFactory {
+ @Inject
+ ThreadFactoryImpl() {}
+
+ public Executor buildExecutorOnNewThread(String threadName) {
+ return buildDelayableExecutorOnNewThread(threadName);
+ }
+
+ public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
+ HandlerThread handlerThread = new HandlerThread(threadName);
+ handlerThread.start();
+ return new ExecutorImpl(handlerThread.getLooper());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index b12224bf583d..d1805af06434 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -44,13 +44,17 @@ import android.text.format.DateUtils;
import android.util.Log;
import android.util.LongSparseArray;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import java.io.FileDescriptor;
@@ -396,15 +400,23 @@ public class GarbageMonitor implements Dumpable {
public static final String TILE_SPEC = "dbg:mem";
private final GarbageMonitor gm;
- private final ActivityStarter mActivityStarter;
private ProcessMemInfo pmi;
private boolean dumpInProgress;
@Inject
- public MemoryTile(QSHost host, GarbageMonitor monitor, ActivityStarter starter) {
- super(host);
+ public MemoryTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ GarbageMonitor monitor
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
gm = monitor;
- mActivityStarter = starter;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
index 450336a6b73b..ed4df175b1a6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -25,18 +25,18 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEventListener;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.MemoryFile;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.util.concurrency.ThreadFactory;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -56,25 +56,14 @@ public class AsyncSensorManager extends SensorManager
private final SensorManager mInner;
private final List<Sensor> mSensorCache;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final List<SensorManagerPlugin> mPlugins;
@Inject
- public AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
- this(sensorManager, pluginManager, null);
- }
-
- @VisibleForTesting
- public AsyncSensorManager(
- SensorManager sensorManager, PluginManager pluginManager, Handler handler) {
+ public AsyncSensorManager(SensorManager sensorManager, ThreadFactory threadFactory,
+ PluginManager pluginManager) {
mInner = sensorManager;
- if (handler == null) {
- HandlerThread handlerThread = new HandlerThread("async_sensor");
- handlerThread.start();
- mHandler = new Handler(handlerThread.getLooper());
- } else {
- mHandler = handler;
- }
+ mExecutor = threadFactory.buildExecutorOnNewThread("async_sensor");
mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
mPlugins = new ArrayList<>();
if (pluginManager != null) {
@@ -97,7 +86,7 @@ public class AsyncSensorManager extends SensorManager
protected boolean registerListenerImpl(SensorEventListener listener,
Sensor sensor, int delayUs, Handler handler, int maxReportLatencyUs,
int reservedFlags) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (!mInner.registerListener(listener, sensor, delayUs, maxReportLatencyUs, handler)) {
Log.e(TAG, "Registering " + listener + " for " + sensor + " failed.");
}
@@ -129,12 +118,12 @@ public class AsyncSensorManager extends SensorManager
@Override
protected void registerDynamicSensorCallbackImpl(DynamicSensorCallback callback,
Handler handler) {
- mHandler.post(() -> mInner.registerDynamicSensorCallback(callback, handler));
+ mExecutor.execute(() -> mInner.registerDynamicSensorCallback(callback, handler));
}
@Override
protected void unregisterDynamicSensorCallbackImpl(DynamicSensorCallback callback) {
- mHandler.post(() -> mInner.unregisterDynamicSensorCallback(callback));
+ mExecutor.execute(() -> mInner.unregisterDynamicSensorCallback(callback));
}
@Override
@@ -145,7 +134,7 @@ public class AsyncSensorManager extends SensorManager
if (sensor == null) {
throw new IllegalArgumentException("sensor cannot be null");
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (!mInner.requestTriggerSensor(listener, sensor)) {
Log.e(TAG, "Requesting " + listener + " for " + sensor + " failed.");
}
@@ -158,7 +147,7 @@ public class AsyncSensorManager extends SensorManager
boolean disable) {
Preconditions.checkArgument(disable);
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (!mInner.cancelTriggerSensor(listener, sensor)) {
Log.e(TAG, "Canceling " + listener + " for " + sensor + " failed.");
}
@@ -178,7 +167,7 @@ public class AsyncSensorManager extends SensorManager
Log.w(TAG, "No plugins registered");
return false;
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
for (int i = 0; i < mPlugins.size(); i++) {
mPlugins.get(i).registerListener(sensor, listener);
}
@@ -194,7 +183,7 @@ public class AsyncSensorManager extends SensorManager
*/
public void unregisterPluginListener(SensorManagerPlugin.Sensor sensor,
SensorManagerPlugin.SensorEventListener listener) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
for (int i = 0; i < mPlugins.size(); i++) {
mPlugins.get(i).unregisterListener(sensor, listener);
}
@@ -214,14 +203,14 @@ public class AsyncSensorManager extends SensorManager
@Override
protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
- mHandler.post(() -> mInner.setOperationParameter(parameter));
+ mExecutor.execute(() -> mInner.setOperationParameter(parameter));
return true;
}
@Override
protected void unregisterListenerImpl(SensorEventListener listener,
Sensor sensor) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (sensor == null) {
mInner.unregisterListener(listener);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index 19c477dfe0a5..9fa03df4229a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -323,7 +323,7 @@ public class ProximitySensor implements ThresholdSensor {
private final AtomicBoolean mRegistered = new AtomicBoolean();
@Inject
- public ProximityCheck(ProximitySensor sensor, DelayableExecutor delayableExecutor) {
+ public ProximityCheck(ProximitySensor sensor, @Main DelayableExecutor delayableExecutor) {
mSensor = sensor;
mSensor.setTag("prox_check");
mDelayableExecutor = delayableExecutor;
diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
index 3270565727d9..84ab66b66a7c 100644
--- a/core/java/android/app/IRequestFinishCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package android.app;
+package com.android.systemui.util.settings;
/**
- * This callback allows ActivityTaskManager to ask the calling Activity
- * to finish in response to a call to onBackPressedOnTaskRoot.
+ * Public interface that can be injected to interact with Settings.Global.
*
- * {@hide}
+ * See {@link SettingsProxy} for details.
*/
-oneway interface IRequestFinishCallback {
- void requestFinish();
+public interface GlobalSettings extends SettingsProxy {
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
new file mode 100644
index 000000000000..1a30b0a8d8bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class GlobalSettingsImpl implements GlobalSettings {
+ private final ContentResolver mContentResolver;
+
+ @Inject
+ GlobalSettingsImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Settings.Global.getUriFor(name);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.Global.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ throw new UnsupportedOperationException(
+ "This method only exists publicly for Settings.System and Settings.Secure");
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return Settings.Global.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ return Settings.Global.putStringForUser(
+ mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ return Settings.Global.putString(mContentResolver, name, value, tag, makeDefault);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
new file mode 100644
index 000000000000..798033e841d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+/**
+ * Public interface that can be injected to interact with Settings.Secure.
+ *
+ * See {@link SettingsProxy} for details.
+ */
+
+public interface SecureSettings extends SettingsProxy {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
new file mode 100644
index 000000000000..020c234191e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class SecureSettingsImpl implements SecureSettings {
+ private final ContentResolver mContentResolver;
+
+ @Inject
+ SecureSettingsImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Settings.Secure.getUriFor(name);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.Secure.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ return Settings.Secure.putString(mContentResolver, name, value, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return Settings.Secure.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ return Settings.Secure.putStringForUser(
+ mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ return Settings.Secure.putString(mContentResolver, name, value, tag, makeDefault);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
new file mode 100644
index 000000000000..5c37f797b678
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+
+/**
+ * Used to interact with Settings.Secure, Settings.Global, and Settings.System.
+ *
+ * This interface can be implemented to give instance method (instead of static method) versions
+ * of Settings.Secure, Settings.Global, and Settings.System. It can be injected into class
+ * constructors and then faked or mocked as needed in tests.
+ *
+ * You can ask for {@link SecureSettings}, {@link GlobalSettings}, or {@link SystemSettings} to be
+ * injected as needed.
+ *
+ * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
+ * normally found on {@link ContentResolver} instances, unifying setting related actions in one
+ * place.
+ */
+public interface SettingsProxy {
+
+ /**
+ * Returns the {@link ContentResolver} this instance was constructed with.
+ */
+ ContentResolver getContentResolver();
+
+ /**
+ * Returns the user id for the associated {@link ContentResolver}.
+ */
+ default int getUserId() {
+ return getContentResolver().getUserId();
+ }
+
+ /**
+ * Construct the content URI for a particular name/value pair,
+ * useful for monitoring changes with a ContentObserver.
+ * @param name to look up in the table
+ * @return the corresponding content URI, or null if not present
+ */
+ Uri getUriFor(String name);
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserver(String name, ContentObserver settingsObserver) {
+ registerContentObserverForUser(name, settingsObserver, getUserId());
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserverForUser(
+ String name, ContentObserver settingsObserver, int userHandle) {
+ getContentResolver().registerContentObserver(
+ getUriFor(name), false, settingsObserver, userHandle);
+ }
+
+ /** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */
+ default void unregisterContentObserver(ContentObserver settingsObserver) {
+ getContentResolver().unregisterContentObserver(settingsObserver);
+ }
+
+ /**
+ * Look up a name in the database.
+ * @param name to look up in the table
+ * @return the corresponding value, or null if not present
+ */
+ default String getString(String name) {
+ return getStringForUser(name, getUserId());
+ }
+
+ /**See {@link #getString(String)}. */
+ String getStringForUser(String name, int userHandle);
+
+ /**
+ * Store a name/value pair into the database. Values written by this method will be
+ * overridden if a restore happens in the future.
+ *
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ */
+ boolean putString(String name, String value, boolean overrideableByRestore);
+
+ /**
+ * Store a name/value pair into the database.
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putString(String name, String value) {
+ return putStringForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(String name, String value, int userHandle);
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);
+
+ /**
+ * Store a name/value pair into the database.
+ * <p>
+ * The method takes an optional tag to associate with the setting
+ * which can be used to clear only settings made by your package and
+ * associated with this tag by passing the tag to {@link
+ * #resetToDefaults(String)}. Anyone can override
+ * the current tag. Also if another package changes the setting
+ * then the tag will be set to the one specified in the set call
+ * which can be null. Also any of the settings setters that do not
+ * take a tag as an argument effectively clears the tag.
+ * </p><p>
+ * For example, if you set settings A and B with tags T1 and T2 and
+ * another app changes setting A (potentially to the same value), it
+ * can assign to it a tag T3 (note that now the package that changed
+ * the setting is not yours). Now if you reset your changes for T1 and
+ * T2 only setting B will be reset and A not (as it was changed by
+ * another package) but since A did not change you are in the desired
+ * initial state. Now if the other app changes the value of A (assuming
+ * you registered an observer in the beginning) you would detect that
+ * the setting was changed by another app and handle this appropriately
+ * (ignore, set back to some value, etc).
+ * </p><p>
+ * Also the method takes an argument whether to make the value the
+ * default for this setting. If the system already specified a default
+ * value, then the one passed in here will <strong>not</strong>
+ * be set as the default.
+ * </p>
+ *
+ * @param name to store.
+ * @param value to associate with the name.
+ * @param tag to associate with the setting.
+ * @param makeDefault whether to make the value the default one.
+ * @return true if the value was set, false on database errors.
+ *
+ * @see #resetToDefaults(String)
+ *
+ */
+ boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault);
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as an integer. Note that internally setting values are always
+ * stored as strings; this function converts the string to an integer
+ * for you. The default value will be returned if the setting is
+ * not defined or not an integer.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid integer.
+ */
+ default int getInt(String name, int def) {
+ return getIntForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getInt(String, int)}. */
+ default int getIntForUser(String name, int def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return v != null ? Integer.parseInt(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as an integer. Note that internally setting values are always
+ * stored as strings; this function converts the string to an integer
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not an integer.
+ *
+ * @return The setting's current value.
+ */
+ default int getInt(String name) throws Settings.SettingNotFoundException {
+ return getIntForUser(name, getUserId());
+ }
+
+ /** See {@link #getInt(String)}. */
+ default int getIntForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a single settings value as an
+ * integer. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putInt(String name, int value) {
+ return putIntForUser(name, value, getUserId());
+ }
+ /** See {@link #putInt(String, int)}. */
+ default boolean putIntForUser(String name, int value, int userHandle) {
+ return putStringForUser(name, Integer.toString(value), userHandle);
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a {@code long}. Note that internally setting values are always
+ * stored as strings; this function converts the string to a {@code long}
+ * for you. The default value will be returned if the setting is
+ * not defined or not a {@code long}.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid {@code long}.
+ */
+ default long getLong(String name, long def) {
+ return getLongForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getLong(String, long)}. */
+ default long getLongForUser(String name, long def, int userHandle) {
+ String valString = getStringForUser(name, userHandle);
+ long value;
+ try {
+ value = valString != null ? Long.parseLong(valString) : def;
+ } catch (NumberFormatException e) {
+ value = def;
+ }
+ return value;
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a {@code long}. Note that internally setting values are always
+ * stored as strings; this function converts the string to a {@code long}
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @return The setting's current value.
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not an integer.
+ */
+ default long getLong(String name) throws Settings.SettingNotFoundException {
+ return getLongForUser(name, getUserId());
+ }
+
+ /** See {@link #getLong(String)}. */
+ default long getLongForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String valString = getStringForUser(name, userHandle);
+ try {
+ return Long.parseLong(valString);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a secure settings value as a long
+ * integer. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putLong(String name, long value) {
+ return putLongForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putLong(String, long)}. */
+ default boolean putLongForUser(String name, long value, int userHandle) {
+ return putStringForUser(name, Long.toString(value), userHandle);
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a floating point number. Note that internally setting values are
+ * always stored as strings; this function converts the string to an
+ * float for you. The default value will be returned if the setting
+ * is not defined or not a valid float.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid float.
+ */
+ default float getFloat(String name, float def) {
+ return getFloatForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getFloat(String)}. */
+ default float getFloatForUser(String name, float def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return v != null ? Float.parseFloat(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a float. Note that internally setting values are always
+ * stored as strings; this function converts the string to a float
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not a float.
+ *
+ * @return The setting's current value.
+ */
+ default float getFloat(String name) throws Settings.SettingNotFoundException {
+ return getFloatForUser(name, getUserId());
+ }
+
+ /** See {@link #getFloat(String, float)}. */
+ default float getFloatForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ if (v == null) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ try {
+ return Float.parseFloat(v);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a single settings value as a
+ * floating point number. This will either create a new entry in the
+ * table if the given name does not exist, or modify the value of the
+ * existing row with that name. Note that internally setting values
+ * are always stored as strings, so this function converts the given
+ * value to a string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putFloat(String name, float value) {
+ return putFloatForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putFloat(String, float)} */
+ default boolean putFloatForUser(String name, float value, int userHandle) {
+ return putStringForUser(name, Float.toString(value), userHandle);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
new file mode 100644
index 000000000000..f36c335e0f44
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module for classes within com.android.systemui.util.settings.
+ */
+@Module
+public interface SettingsUtilModule {
+
+ /** Bind SecureSettingsImpl to SecureSettings. */
+ @Binds
+ SecureSettings bindsSecureSettings(SecureSettingsImpl impl);
+
+ /** Bind SystemSettingsImpl to SystemSettings. */
+ @Binds
+ SystemSettings bindsSystemSettings(SystemSettingsImpl impl);
+
+ /** Bind GlobalSettingsImpl to GlobalSettings. */
+ @Binds
+ GlobalSettings bindsGlobalSettings(GlobalSettingsImpl impl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
new file mode 100644
index 000000000000..d57d7496381c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+/**
+ * Public interface that can be injected to interact with Settings.System.
+ *
+ * See {@link SettingsProxy} for details.
+ */
+public interface SystemSettings extends SettingsProxy {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
new file mode 100644
index 000000000000..0dbb76f8f758
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class SystemSettingsImpl implements SystemSettings {
+ private final ContentResolver mContentResolver;
+
+ @Inject
+ SystemSettingsImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Settings.System.getUriFor(name);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.System.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ return Settings.System.putString(mContentResolver, name, value, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return Settings.System.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ throw new UnsupportedOperationException(
+ "This method only exists publicly for Settings.Secure and Settings.Global");
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ throw new UnsupportedOperationException(
+ "This method only exists publicly for Settings.Secure and Settings.Global");
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
new file mode 100644
index 000000000000..fbc167683a2a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wmshell;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.IWindowManager;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies from {@link com.android.wm.shell}.
+ */
+@Module
+// TODO(b/161116823) Clean up dependencies after wm shell migration finished.
+public class WindowManagerShellModule {
+ @Singleton
+ @Provides
+ static TransactionPool provideTransactionPool() {
+ return new TransactionPool();
+ }
+
+ @Singleton
+ @Provides
+ static DisplayController provideDisplayController(Context context, @Main Handler handler,
+ IWindowManager wmService) {
+ return new DisplayController(context, handler, wmService);
+ }
+
+ @Singleton
+ @Provides
+ static SystemWindows provideSystemWindows(DisplayController displayController,
+ IWindowManager wmService) {
+ return new SystemWindows(displayController, wmService);
+ }
+
+ @Singleton
+ @Provides
+ static DisplayImeController provideDisplayImeController(
+ IWindowManager wmService, DisplayController displayController,
+ @Main Handler mainHandler, TransactionPool transactionPool) {
+ return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
+ }
+}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 4e5464a4d5cb..c6331682f80c 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -75,6 +75,11 @@
android:excludeFromRecents="true"
android:exported="false" />
+ <activity android:name="com.android.systemui.controls.management.TestControlsRequestDialog"
+ android:exported="false"
+ android:excludeFromRecents="true"
+ />
+
<provider
android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 7bc453ac9aa1..4a8ada09b3d2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -52,6 +52,7 @@ import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorProperties;
import android.hardware.fingerprint.FingerprintManager;
import android.media.AudioManager;
import android.os.Bundle;
@@ -131,6 +132,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Mock
private FaceManager mFaceManager;
@Mock
+ private List<FaceSensorProperties> mFaceSensorProperties;
+ @Mock
private BiometricManager mBiometricManager;
@Mock
private PackageManager mPackageManager;
@@ -174,6 +177,15 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFaceManager.getSensorProperties()).thenReturn(mFaceSensorProperties);
+
+ // IBiometricsFace@1.0 does not support detection, only authentication.
+ when(mFaceSensorProperties.isEmpty()).thenReturn(false);
+ when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorProperties(0 /* id */,
+ false /* supportsFaceDetection */));
+
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
when(mUserManager.isPrimaryUser()).thenReturn(true);
when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
@@ -211,6 +223,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testInitialBatteryLevelRequested() {
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.mBatteryStatus).isNotNull();
+ }
+
+ @Test
public void testReceiversRegistered() {
verify(mBroadcastDispatcher, atLeastOnce()).registerReceiverWithHandler(
eq(mKeyguardUpdateMonitor.mBroadcastReceiver),
@@ -412,11 +431,47 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testTriesToAuthenticateFingerprint_whenKeyguard() {
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt());
+ verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt(), any());
+ }
+
+ @Test
+ public void testFingerprintDoesNotAuth_whenEncrypted() {
+ testFingerprintWhenStrongAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ }
+
+ @Test
+ public void testFingerprintDoesNotAuth_whenDpmLocked() {
+ testFingerprintWhenStrongAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW);
+ }
+
+ @Test
+ public void testFingerprintDoesNotAuth_whenUserLockdown() {
+ testFingerprintWhenStrongAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ }
+
+ private void testFingerprintWhenStrongAuth(int strongAuth) {
+ when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+
+ verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+ verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt(), any());
+ }
+
+ @Test
public void testTriesToAuthenticate_whenBouncer() {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
mTestableLooper.processAllMessages();
- verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
verify(mFaceManager).isHardwareDetected();
verify(mFaceManager).hasEnrolledTemplates(anyInt());
}
@@ -426,7 +481,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
}
@Test
@@ -436,7 +491,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
}
@Test
@@ -448,7 +503,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
}
@Test
@@ -471,14 +526,14 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
// Stop scanning when bouncer becomes visible
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true /* showingBouncer */);
mTestableLooper.processAllMessages();
clearInvocations(mFaceManager);
mKeyguardUpdateMonitor.requestFaceAuth();
- verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
}
@Test
@@ -486,7 +541,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.setKeyguardOccluded(true);
mKeyguardUpdateMonitor.setAssistantVisible(true);
- verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
}
@Test
@@ -498,7 +553,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
}
@Test
@@ -508,7 +563,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
}
@Test
@@ -519,7 +574,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
}
@Test
@@ -530,7 +585,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT);
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
}
@Test
@@ -542,7 +597,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
mTestableLooper.processAllMessages();
- verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 475ddc1ea11a..35be496d1a2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -46,9 +46,8 @@ public class DependencyTest extends SysuiTestCase {
@Test
public void testInitDependency() {
Dependency.clearDependencies();
- Dependency dependency = new Dependency();
- SystemUIFactory
- .getInstance().getRootComponent().createDependency().createSystemUI(dependency);
+ Dependency dependency =
+ SystemUIFactory.getInstance().getRootComponent().createDependency();
dependency.start();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
index cf778504190a..3687b4ca7889 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -54,7 +54,10 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
@Before
public void SysuiSetup() {
SystemUIFactory.createFromConfig(mContext);
- mDependency = new TestableDependency(mContext);
+ mDependency = new TestableDependency(
+ SystemUIFactory.getInstance().getRootComponent().createDependency());
+ Dependency.setInstance(mDependency);
+
// TODO: Figure out another way to give reference to a SysuiTestableContext.
mSysuiContext = (SysuiTestableContext) mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index dd3a7858fd1f..08e27841de03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -73,7 +73,9 @@ public abstract class SysuiTestCase {
@Before
public void SysuiSetup() throws Exception {
SystemUIFactory.createFromConfig(mContext);
- mDependency = new TestableDependency(mContext);
+ mDependency = new TestableDependency(
+ SystemUIFactory.getInstance().getRootComponent().createDependency());
+ Dependency.setInstance(mDependency);
mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class),
mock(Executor.class), mock(DumpManager.class),
mock(BroadcastDispatcherLogger.class));
@@ -140,6 +142,10 @@ public abstract class SysuiTestCase {
return null;
}
+ protected FakeBroadcastDispatcher getFakeBroadcastDispatcher() {
+ return mFakeBroadcastDispatcher;
+ }
+
public SysuiTestableContext getContext() {
return mContext;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index 95ff98ae620b..3d679deaa426 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -26,11 +26,14 @@ import android.util.ArraySet;
import android.util.Log;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.Set;
public class SysuiTestableContext extends TestableContext {
- private Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
+ @GuardedBy("mRegisteredReceivers")
+ private final Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
public SysuiTestableContext(Context base) {
super(base);
@@ -54,7 +57,11 @@ public class SysuiTestableContext extends TestableContext {
}
public void cleanUpReceivers(String testName) {
- Set<BroadcastReceiver> copy = new ArraySet<>(mRegisteredReceivers);
+ Set<BroadcastReceiver> copy;
+ synchronized (mRegisteredReceivers) {
+ copy = new ArraySet<>(mRegisteredReceivers);
+ mRegisteredReceivers.clear();
+ }
for (BroadcastReceiver r : copy) {
try {
unregisterReceiver(r);
@@ -67,27 +74,43 @@ public class SysuiTestableContext extends TestableContext {
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- mRegisteredReceivers.add(receiver);
+ if (receiver != null) {
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.add(receiver);
+ }
+ }
return super.registerReceiver(receiver, filter);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
- mRegisteredReceivers.add(receiver);
+ if (receiver != null) {
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.add(receiver);
+ }
+ }
return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
}
@Override
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
IntentFilter filter, String broadcastPermission, Handler scheduler) {
- mRegisteredReceivers.add(receiver);
+ if (receiver != null) {
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.add(receiver);
+ }
+ }
return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler);
}
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
- mRegisteredReceivers.remove(receiver);
+ if (receiver != null) {
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.remove(receiver);
+ }
+ }
super.unregisterReceiver(receiver);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index a7f4fa5768b4..ee52c7804b69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -16,7 +16,6 @@ package com.android.systemui;
import static org.mockito.Mockito.mock;
-import android.content.Context;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -26,13 +25,10 @@ public class TestableDependency extends Dependency {
private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
private final ArraySet<Object> mInstantiatedObjects = new ArraySet<>();
+ private final Dependency mParent;
- public TestableDependency(Context context) {
- SystemUIFactory.createFromConfig(context);
- SystemUIFactory.getInstance().getRootComponent()
- .createDependency()
- .createSystemUI(this);
- start();
+ public TestableDependency(Dependency parent) {
+ mParent = parent;
}
public <T> T injectMockDependency(Class<T> cls) {
@@ -53,11 +49,11 @@ public class TestableDependency extends Dependency {
}
@Override
- protected <T> T createDependency(Object key) {
+ public <T> T createDependency(Object key) {
if (mObjs.containsKey(key)) return (T) mObjs.get(key);
mInstantiatedObjects.add(key);
- return super.createDependency(key);
+ return mParent.createDependency(key);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
new file mode 100644
index 000000000000..9cb4fb319fa2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.display.DisplayManager;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DisplayIdIndexSupplierTest extends SysuiTestCase {
+
+ private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier;
+
+ @Before
+ public void setUp() throws Exception {
+ mDisplayIdIndexSupplier = new DisplayIdIndexSupplier(
+ mContext.getSystemService(DisplayManager.class)) {
+
+ @NonNull
+ @Override
+ protected Object createInstance(Display display) {
+ return new Object();
+ }
+ };
+ }
+
+ @Test
+ public void get_instanceIsNotNull() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ assertNotNull(object);
+ }
+
+ @Test
+ public void get_removeExistedObject_newObject() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ mDisplayIdIndexSupplier.remove(Display.DEFAULT_DISPLAY);
+
+ Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+ assertNotEquals(object, newObject);
+ }
+
+ @Test
+ public void get_clearAllObjects_newObject() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ mDisplayIdIndexSupplier.clear();
+
+ Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+ assertNotEquals(object, newObject);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
new file mode 100644
index 000000000000..71f3d5bee225
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
+import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MagnificationModeSwitchTest extends SysuiTestCase {
+
+ @Mock
+ private ImageView mMockImageView;
+ @Mock
+ private WindowManager mWindowManager;
+ @Mock
+ private ViewPropertyAnimator mViewPropertyAnimator;
+ private MagnificationModeSwitch mMagnificationModeSwitch;
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
+ when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
+ when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
+ when(mViewPropertyAnimator.setStartDelay(anyLong())).thenReturn(mViewPropertyAnimator);
+ when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
+ mViewPropertyAnimator);
+
+ when(mMockImageView.animate()).thenReturn(mViewPropertyAnimator);
+
+ mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mMockImageView);
+ }
+
+ @Test
+ public void removeButton_removeView() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+ mMagnificationModeSwitch.removeButton();
+
+ verify(mWindowManager).removeView(mMockImageView);
+ // First invocation is in showButton.
+ verify(mViewPropertyAnimator, times(2)).cancel();
+ }
+
+ @Test
+ public void showWindowModeButton_fullscreenMode_addViewAndSetImageResource() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+ verify(mMockImageView).setAlpha(1.0f);
+ verify(mMockImageView).setImageResource(
+ getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
+ verify(mViewPropertyAnimator).cancel();
+ verify(mViewPropertyAnimator).setDuration(anyLong());
+ verify(mViewPropertyAnimator).setStartDelay(anyLong());
+ verify(mViewPropertyAnimator).alpha(anyFloat());
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mViewPropertyAnimator).withEndAction(captor.capture());
+ verify(mWindowManager).addView(eq(mMockImageView), any(WindowManager.LayoutParams.class));
+
+ captor.getValue().run();
+
+ // First invocation is in showButton.
+ verify(mViewPropertyAnimator, times(2)).cancel();
+ verify(mWindowManager).removeView(mMockImageView);
+ }
+
+ @Test
+ public void performClick_fullscreenMode_removeViewAndChangeSettingsValue() {
+ ArgumentCaptor<View.OnClickListener> captor = ArgumentCaptor.forClass(
+ View.OnClickListener.class);
+ verify(mMockImageView).setOnClickListener(captor.capture());
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+ captor.getValue().onClick(mMockImageView);
+
+ // First invocation is in showButton.
+ verify(mViewPropertyAnimator, times(2)).cancel();
+ verify(mMockImageView).setImageResource(
+ getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
+ verify(mWindowManager).removeView(mMockImageView);
+ final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+ assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, actualMode);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index d6d2fcd9610a..69482791ef23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -16,20 +16,13 @@
package com.android.systemui.accessibility;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.view.Display;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
import androidx.test.filters.SmallTest;
@@ -38,45 +31,43 @@ import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
/** Tests the ModeSwitchesController. */
public class ModeSwitchesControllerTest extends SysuiTestCase {
- private WindowManager mWindowManager;
+ @Mock
+ private ModeSwitchesController.SwitchSupplier mSupplier;
+ @Mock
+ private MagnificationModeSwitch mModeSwitch;
private ModeSwitchesController mModeSwitchesController;
+
@Before
public void setUp() {
- mWindowManager = mock(WindowManager.class);
- Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
- when(mWindowManager.getDefaultDisplay()).thenReturn(display);
- WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
- .getMaximumWindowMetrics();
- when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
- mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
- mModeSwitchesController = new ModeSwitchesController(mContext);
+ MockitoAnnotations.initMocks(this);
+ when(mSupplier.get(anyInt())).thenReturn(mModeSwitch);
+ mModeSwitchesController = new ModeSwitchesController(mSupplier);
}
@Test
public void testShowButton() {
mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- verify(mWindowManager).addView(any(), any());
+
+ verify(mModeSwitch).showButton(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
}
@Test
public void testRemoveButton() {
mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
- verify(mWindowManager).addView(captor.capture(), any(WindowManager.LayoutParams.class));
mModeSwitchesController.removeButton(Display.DEFAULT_DISPLAY);
- verify(mWindowManager).removeView(eq(captor.getValue()));
+ verify(mModeSwitch).removeButton();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index bfb95b0db3e3..a898c3ce2773 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -16,8 +16,8 @@
package com.android.systemui.accessibility;
-
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import android.app.Instrumentation;
@@ -27,6 +27,7 @@ import android.testing.AndroidTestingRunner;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
@@ -43,6 +44,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Mock
Handler mHandler;
@Mock
+ SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+ @Mock
MirrorWindowControl mMirrorWindowControl;
@Mock
WindowMagnifierCallback mWindowMagnifierCallback;
@@ -54,7 +57,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mWindowMagnificationController = new WindowMagnificationController(getContext(),
- mHandler,
+ mHandler, mSfVsyncFrameProvider,
mMirrorWindowControl, mWindowMagnifierCallback);
verify(mMirrorWindowControl).setWindowDelegate(
any(MirrorWindowControl.MirrorWindowDelegate.class));
@@ -65,7 +68,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.deleteWindowMagnification();
});
- mInstrumentation.waitForIdleSync();
}
@Test
@@ -74,7 +76,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN);
});
- mInstrumentation.waitForIdleSync();
verify(mMirrorWindowControl).showControl();
}
@@ -84,13 +85,22 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN);
});
- mInstrumentation.waitForIdleSync();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.deleteWindowMagnification();
});
- mInstrumentation.waitForIdleSync();
verify(mMirrorWindowControl).destroyControl();
}
+
+ @Test
+ public void moveMagnifier_schedulesFrame() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ mWindowMagnificationController.moveWindowMagnifier(100f, 100f);
+ });
+
+ verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index e0049d1349f1..4fdc06e64e2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -26,11 +26,14 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.content.pm.PackageManager;
import android.os.Looper;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
@@ -56,6 +59,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = UserHandle.getUid(0, 0);
private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
+ private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
@Mock
private AppOpsManager mAppOpsManager;
@@ -65,6 +69,10 @@ public class AppOpsControllerTest extends SysuiTestCase {
private AppOpsControllerImpl.H mMockHandler;
@Mock
private DumpManager mDumpManager;
+ @Mock
+ private PermissionFlagsCache mFlagsCache;
+ @Mock
+ private PackageManager mPackageManager;
private AppOpsControllerImpl mController;
private TestableLooper mTestableLooper;
@@ -76,8 +84,22 @@ public class AppOpsControllerTest extends SysuiTestCase {
getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
- mController =
- new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mDumpManager);
+ // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
+ // TEST_UID_NON_USER_SENSITIVE are user sensitive.
+ getContext().setMockPackageManager(mPackageManager);
+ when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID))).thenReturn(
+ PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
+ when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID_OTHER)))
+ .thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
+ when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
+ eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
+
+ mController = new AppOpsControllerImpl(
+ mContext,
+ mTestableLooper.getLooper(),
+ mDumpManager,
+ mFlagsCache
+ );
}
@Test
@@ -173,6 +195,26 @@ public class AppOpsControllerTest extends SysuiTestCase {
}
@Test
+ public void nonUserSensitiveOpsAreIgnored() {
+ mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+ TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+ assertEquals(0, mController.getActiveAppOpsForUser(
+ UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+ }
+
+ @Test
+ public void nonUserSensitiveOpsNotNotified() {
+ mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+ TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback, never())
+ .onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean());
+ }
+
+ @Test
public void opNotedScheduledForRemoval() {
mController.setBGHandler(mMockHandler);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
new file mode 100644
index 000000000000..0fb0ce087ee3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.appops
+
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class PermissionFlagsCacheTest : SysuiTestCase() {
+
+ companion object {
+ const val TEST_PERMISSION = "test_permission"
+ const val TEST_PACKAGE = "test_package"
+ const val TEST_UID1 = 1000
+ const val TEST_UID2 = UserHandle.PER_USER_RANGE + 1000
+ }
+
+ @Mock
+ private lateinit var packageManager: PackageManager
+
+ private lateinit var executor: FakeExecutor
+ private lateinit var flagsCache: PermissionFlagsCache
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ executor = FakeExecutor(FakeSystemClock())
+
+ flagsCache = PermissionFlagsCache(packageManager, executor)
+ executor.runAllReady()
+ }
+
+ @Test
+ fun testNotListeningByDefault() {
+ verify(packageManager, never()).addOnPermissionsChangeListener(any())
+ }
+
+ @Test
+ fun testGetCorrectFlags() {
+ `when`(packageManager.getPermissionFlags(anyString(), anyString(), any())).thenReturn(0)
+ `when`(packageManager.getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID1))
+ ).thenReturn(1)
+
+ assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+ assertNotEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2))
+ }
+
+ @Test
+ fun testFlagIsCached() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ verify(packageManager, times(1)).getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID1)
+ )
+ }
+
+ @Test
+ fun testListeningAfterFirstRequest() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ verify(packageManager).addOnPermissionsChangeListener(any())
+ }
+
+ @Test
+ fun testListeningOnlyOnce() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2)
+
+ verify(packageManager, times(1)).addOnPermissionsChangeListener(any())
+ }
+
+ @Test
+ fun testUpdateFlag() {
+ assertEquals(0, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+
+ `when`(packageManager.getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID1))
+ ).thenReturn(1)
+
+ flagsCache.onPermissionsChanged(TEST_UID1)
+
+ executor.runAllReady()
+
+ assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+ }
+
+ @Test
+ fun testUpdateFlag_notUpdatedIfUidHasNotBeenRequestedBefore() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ flagsCache.onPermissionsChanged(TEST_UID2)
+
+ executor.runAllReady()
+
+ verify(packageManager, never()).getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID2)
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index e8a0c738f966..d4a94c5b9e66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -45,6 +45,7 @@ import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -530,6 +531,11 @@ public class AuthControllerTest extends SysuiTestCase {
IActivityTaskManager getActivityTaskManager() {
return mock(IActivityTaskManager.class);
}
+
+ @Override
+ FingerprintManager getFingerprintManager(Context context) {
+ return mock(FingerprintManager.class);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 5b46b7fa4d9f..15828b41cc7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -63,6 +63,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -135,6 +136,8 @@ public class BubbleControllerTest extends SysuiTestCase {
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
+ private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -198,8 +201,8 @@ public class BubbleControllerTest extends SysuiTestCase {
mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController, mColorExtractor,
- mDumpManager);
+ mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+ mColorExtractor, mDumpManager);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
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 ed4e6865e508..315caeebe0e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -107,6 +107,9 @@ public class BubbleDataTest extends SysuiTestCase {
@Mock
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+ @Mock
+ private BubbleController.PendingIntentCanceledListener mPendingIntentCanceledListener;
+
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(
@@ -127,20 +130,20 @@ public class BubbleDataTest extends SysuiTestCase {
modifyRanking(mEntryInterruptive)
.setVisuallyInterruptive(true)
.build();
- mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener);
+ mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null);
ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
mEntryDismissed.setRow(row);
- mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener);
+ mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null);
- mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener);
- mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener);
- mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener);
- mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener);
- mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener);
- mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener);
- mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener);
+ mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleData = new BubbleData(getContext());
@@ -847,14 +850,6 @@ public class BubbleDataTest extends SysuiTestCase {
when(entry.getSbn().getPostTime()).thenReturn(postTime);
}
- private void setOngoing(NotificationEntry entry, boolean ongoing) {
- if (ongoing) {
- entry.getSbn().getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
- } else {
- entry.getSbn().getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE;
- }
- }
-
/**
* No ExpandableNotificationRow is required to test BubbleData. This setup is all that is
* required for BubbleData functionality and verification. NotificationTestHelper is used only
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 be03923e7264..f7f3a377f31d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -35,10 +35,10 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
@@ -71,7 +71,7 @@ public class BubbleTest extends SysuiTestCase {
.setNotification(mNotif)
.build();
- mBubble = new Bubble(mEntry, mSuppressionListener);
+ mBubble = new Bubble(mEntry, mSuppressionListener, null);
Intent target = new Intent(mContext, BubblesTestActivity.class);
Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
index 8bc2e2bc77b6..43d2ad1dfe0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
@@ -20,7 +20,7 @@ import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import com.android.systemui.tests.R;
+import com.android.systemui.R;
/**
* Referenced by NotificationTestHelper#makeBubbleMetadata
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 058478fae963..686a09444ee6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -60,6 +60,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
@@ -132,6 +133,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
+ private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -194,8 +197,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Bubbles get added to status bar window view
mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController, mColorExtractor,
- mDumpManager);
+ mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+ mColorExtractor, mDumpManager);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
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 ae7387996322..c3c9ecc23d59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -29,8 +29,8 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
+import com.android.systemui.classifier.brightline.FalsingDataProvider;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.dump.DumpManager;
@@ -42,6 +42,8 @@ import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.utils.leaks.FakeBatteryController;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.After;
import org.junit.Before;
@@ -52,7 +54,7 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class FalsingManagerProxyTest extends SysuiTestCase {
+public class FalsingManagerProxyTest extends LeakCheckedTest {
@Mock(stubOnly = true)
PluginManager mPluginManager;
@Mock(stubOnly = true)
@@ -62,7 +64,7 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
@Mock DumpManager mDumpManager;
private FalsingManagerProxy mProxy;
private DeviceConfigProxy mDeviceConfig;
- private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private FalsingDataProvider mFalsingDataProvider;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private DockManager mDockManager = new DockManagerFake();
@@ -75,6 +77,8 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
mDeviceConfig = new DeviceConfigProxyFake();
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
+ mFalsingDataProvider = new FalsingDataProvider(
+ new DisplayMetrics(), new FakeBatteryController(getLeakCheck()));
}
@After
@@ -86,9 +90,9 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
@Test
public void test_brightLineFalsingManagerDisabled() {
- mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
+ mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor, mStatusBarStateController);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
}
@@ -97,17 +101,17 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
mExecutor.runAllReady();
- mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
+ mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor, mStatusBarStateController);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
}
@Test
public void test_brightLineFalsingManagerToggled() throws InterruptedException {
- mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
+ mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor, mStatusBarStateController);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
index 2f05f0b4c69b..061664b4f6d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.classifier.brightline;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -27,7 +28,6 @@ import android.util.DisplayMetrics;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.statusbar.StatusBarState;
@@ -37,6 +37,8 @@ import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.utils.leaks.FakeBatteryController;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
import org.junit.Test;
@@ -47,7 +49,7 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class BrightLineFalsingManagerTest extends SysuiTestCase {
+public class BrightLineFalsingManagerTest extends LeakCheckedTest {
@Mock
@@ -55,23 +57,26 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
@Mock
private ProximitySensor mProximitySensor;
private SysuiStatusBarStateController mStatusBarStateController;
+ private FalsingDataProvider mFalsingDataProvider;
+ private FakeBatteryController mFakeBatteryController;
private BrightLineFalsingManager mFalsingManager;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ mFakeBatteryController = new FakeBatteryController(getLeakCheck());
DisplayMetrics dm = new DisplayMetrics();
dm.xdpi = 100;
dm.ydpi = 100;
dm.widthPixels = 100;
dm.heightPixels = 100;
- FalsingDataProvider falsingDataProvider = new FalsingDataProvider(dm);
+ mFalsingDataProvider = new FalsingDataProvider(dm, mFakeBatteryController);
DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake();
DockManager dockManager = new DockManagerFake();
mStatusBarStateController = new StatusBarStateControllerImpl(new UiEventLoggerFake());
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
- mFalsingManager = new BrightLineFalsingManager(falsingDataProvider,
+ mFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager,
mStatusBarStateController);
}
@@ -83,6 +88,13 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
}
@Test
+ public void testNoProximityWhenWirelessCharging() {
+ mFakeBatteryController.setWirelessCharging(true);
+ mFalsingManager.onScreenTurningOn();
+ verify(mProximitySensor, never()).register(any(ThresholdSensor.Listener.class));
+ }
+
+ @Test
public void testUnregisterSensor() {
mFalsingManager.onScreenTurningOn();
reset(mProximitySensor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
index 3ba5d1ac79ea..a4d198a14541 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
@@ -21,29 +21,30 @@ import static com.android.systemui.classifier.Classifier.UNLOCK;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
-import com.android.systemui.SysuiTestCase;
+import com.android.systemui.utils.leaks.FakeBatteryController;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.After;
-import org.junit.Before;
import java.util.ArrayList;
import java.util.List;
-public class ClassifierTest extends SysuiTestCase {
+public class ClassifierTest extends LeakCheckedTest {
private FalsingDataProvider mDataProvider;
private List<MotionEvent> mMotionEvents = new ArrayList<>();
private float mOffsetX = 0;
private float mOffsetY = 0;
+ private FakeBatteryController mFakeBatteryController;
- @Before
public void setup() {
DisplayMetrics displayMetrics = new DisplayMetrics();
displayMetrics.xdpi = 100;
displayMetrics.ydpi = 100;
displayMetrics.widthPixels = 1000;
displayMetrics.heightPixels = 1000;
- mDataProvider = new FalsingDataProvider(displayMetrics);
+ mFakeBatteryController = new FakeBatteryController(getLeakCheck());
+ mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
mDataProvider.setInteractionType(UNLOCK);
}
@@ -56,6 +57,10 @@ public class ClassifierTest extends SysuiTestCase {
return mDataProvider;
}
+ FakeBatteryController getFakeBatteryController() {
+ return mFakeBatteryController;
+ }
+
void setOffsetX(float offsetX) {
mOffsetX = offsetX;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
index 448c2f7b33ad..f13bc7379436 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
@@ -26,6 +26,8 @@ import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
+import com.android.systemui.utils.leaks.FakeBatteryController;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -37,17 +39,19 @@ import java.util.List;
@RunWith(AndroidTestingRunner.class)
public class FalsingDataProviderTest extends ClassifierTest {
+ private FakeBatteryController mFakeBatteryController;
private FalsingDataProvider mDataProvider;
@Before
public void setup() {
super.setup();
+ mFakeBatteryController = new FakeBatteryController(getLeakCheck());
DisplayMetrics displayMetrics = new DisplayMetrics();
displayMetrics.xdpi = 100;
displayMetrics.ydpi = 100;
displayMetrics.widthPixels = 1000;
displayMetrics.heightPixels = 1000;
- mDataProvider = new FalsingDataProvider(displayMetrics);
+ mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
}
@After
@@ -246,4 +250,12 @@ public class FalsingDataProviderTest extends ClassifierTest {
assertThat(mDataProvider.isUp(), is(false));
mDataProvider.onSessionEnd();
}
+
+ @Test
+ public void test_isWirelessCharging() {
+ assertThat(mDataProvider.isWirelessCharging(), is(false));
+
+ mFakeBatteryController.setWirelessCharging(true);
+ assertThat(mDataProvider.isWirelessCharging(), is(true));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
new file mode 100644
index 000000000000..0122db6c4965
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.management
+
+import android.app.Dialog
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.IIntentSender
+import android.content.Intent
+import android.os.UserHandle
+import android.service.controls.Control
+import android.service.controls.ControlsProviderService
+import android.service.controls.DeviceTypes
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.lifecycle.Lifecycle
+import androidx.test.filters.MediumTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@MediumTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ControlsRequestDialogTest : SysuiTestCase() {
+
+ companion object {
+ private val CONTROL_COMPONENT = ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS")!!
+ private const val LABEL = "TEST_LABEL"
+
+ private val USER_ID = UserHandle.USER_SYSTEM
+ private const val CONTROL_ID = "id"
+ }
+
+ @Mock
+ private lateinit var controller: ControlsController
+
+ @Mock
+ private lateinit var listingController: ControlsListingController
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var iIntentSender: IIntentSender
+ @Captor
+ private lateinit var captor: ArgumentCaptor<ControlInfo>
+
+ @Rule
+ @JvmField
+ var activityRule = ActivityTestRule<TestControlsRequestDialog>(
+ object : SingleActivityFactory<TestControlsRequestDialog>(
+ TestControlsRequestDialog::class.java
+ ) {
+ override fun create(intent: Intent?): TestControlsRequestDialog {
+ return TestControlsRequestDialog(
+ controller,
+ broadcastDispatcher,
+ listingController
+ )
+ }
+ }, false, false)
+
+ private lateinit var control: Control
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ control = Control.StatelessBuilder(CONTROL_ID, PendingIntent(iIntentSender))
+ .setTitle("TITLE")
+ .setSubtitle("SUBTITLE")
+ .setDeviceType(DeviceTypes.TYPE_LIGHT)
+ .setStructure("STRUCTURE")
+ .build()
+
+ val intent = Intent(mContext, TestControlsRequestDialog::class.java)
+ intent.putExtra(Intent.EXTRA_USER_ID, USER_ID)
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME, CONTROL_COMPONENT)
+ intent.putExtra(ControlsProviderService.EXTRA_CONTROL, control)
+
+ `when`(controller.currentUserId).thenReturn(USER_ID)
+ `when`(controller.available).thenReturn(true)
+ `when`(listingController.getAppLabel(CONTROL_COMPONENT)).thenReturn(LABEL)
+ `when`(controller.getFavoritesForComponent(CONTROL_COMPONENT)).thenReturn(emptyList())
+
+ activityRule.launchActivity(intent)
+ }
+
+ @After
+ fun tearDown() {
+ activityRule.finishActivity()
+ }
+
+ @Test
+ fun testActivityNotFinished() {
+ assertNotEquals(Lifecycle.State.DESTROYED,
+ activityRule.getActivity().lifecycle.currentState)
+ }
+
+ @Test
+ fun testDialogAddsCorrectControl() {
+ activityRule.activity.onClick(null, Dialog.BUTTON_POSITIVE)
+
+ verify(controller)
+ .addFavorite(eq(CONTROL_COMPONENT), eq(control.structure!!), capture(captor))
+
+ captor.value.let {
+ assertEquals(control.controlId, it.controlId)
+ assertEquals(control.title, it.controlTitle)
+ assertEquals(control.subtitle, it.controlSubtitle)
+ assertEquals(control.deviceType, it.deviceType)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt
new file mode 100644
index 000000000000..3f6308b18d03
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.management
+
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.controller.ControlsController
+
+class TestControlsRequestDialog(
+ controller: ControlsController,
+ dispatcher: BroadcastDispatcher,
+ listingController: ControlsListingController
+) : ControlsRequestDialog(controller, dispatcher, listingController) \ No newline at end of file
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 00d333fb593b..c591c1bd42bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -16,13 +16,13 @@
package com.android.systemui.doze;
-import android.hardware.display.AmbientDisplayConfiguration;
-
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
+import android.hardware.display.AmbientDisplayConfiguration;
+
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.FakeSensorManager;
@@ -37,7 +37,6 @@ public class DozeConfigurationUtil {
when(params.getPulseOnSigMotion()).thenReturn(false);
when(params.getPickupVibrationThreshold()).thenReturn(0);
when(params.getProxCheckBeforePulse()).thenReturn(true);
- when(params.getPolicy()).thenReturn(mock(AlwaysOnDisplayPolicy.class));
when(params.doubleTapReportsTouchCoordinates()).thenReturn(false);
when(params.getDisplayNeedsBlanking()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index dc027997578f..5c2b153bf452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -57,7 +57,8 @@ public class DozeDockHandlerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mConfig = DozeConfigurationUtil.createMockConfig();
mDockManagerFake = spy(new DockManagerFake());
- mDockHandler = new DozeDockHandler(mConfig, mMachine, mDockManagerFake);
+ mDockHandler = new DozeDockHandler(mConfig, mDockManagerFake);
+ mDockHandler.setDozeMachine(mMachine);
when(mMachine.getState()).thenReturn(State.DOZE_AOD);
doReturn(true).when(mConfig).alwaysOnEnabled(anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 1f07f46bf764..8078b6c8bda4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -88,8 +88,7 @@ public class DozeMachineTest extends SysuiTestCase {
mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake,
mWakefulnessLifecycle, mock(BatteryController.class), mDozeLog, mDockManager,
- mHost);
- mMachine.setParts(new DozeMachine.Part[]{mPartMock});
+ mHost, new DozeMachine.Part[]{mPartMock});
}
@Test
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 3ef60274cd76..3e60e016a0a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -40,14 +40,17 @@ import android.content.Intent;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
import android.view.Display;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeThreadFactory;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.FakeSensorManager;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -55,22 +58,24 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
@SmallTest
+@RunWith(AndroidTestingRunner.class)
public class DozeScreenBrightnessTest extends SysuiTestCase {
- static final int DEFAULT_BRIGHTNESS = 10;
- static final int[] SENSOR_TO_BRIGHTNESS = new int[]{-1, 1, 2, 3, 4};
- static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0};
+ private static final int DEFAULT_BRIGHTNESS = 10;
+ private static final int[] SENSOR_TO_BRIGHTNESS = new int[]{-1, 1, 2, 3, 4};
+ private static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0};
- DozeServiceFake mServiceFake;
- FakeSensorManager.FakeGenericSensor mSensor;
- FakeSensorManager mSensorManager;
+ private DozeServiceFake mServiceFake;
+ private FakeSensorManager.FakeGenericSensor mSensor;
+ private AsyncSensorManager mSensorManager;
+ private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
@Mock
DozeHost mDozeHost;
- @Mock
- BroadcastDispatcher mBroadcastDispatcher;
- DozeScreenBrightness mScreen;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
+
+ private DozeScreenBrightness mScreen;
@Before
public void setUp() throws Exception {
@@ -83,12 +88,17 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
return null;
}).when(mDozeHost).prepareForGentleSleep(any());
mServiceFake = new DozeServiceFake();
- mSensorManager = new FakeSensorManager(mContext);
- mSensor = mSensorManager.getFakeLightSensor();
+ FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext);
+ mSensorManager = new AsyncSensorManager(fakeSensorManager, mFakeThreadFactory, null);
+
+ mAlwaysOnDisplayPolicy = new AlwaysOnDisplayPolicy(mContext);
+ mAlwaysOnDisplayPolicy.defaultDozeBrightness = DEFAULT_BRIGHTNESS;
+ mAlwaysOnDisplayPolicy.screenBrightnessArray = SENSOR_TO_BRIGHTNESS;
+ mAlwaysOnDisplayPolicy.dimmingScrimArray = SENSOR_TO_OPACITY;
+ mSensor = fakeSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- mSensor.getSensor(), mBroadcastDispatcher, mDozeHost, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
- true /* debuggable */);
+ mSensor.getSensor(), mDozeHost, null /* handler */,
+ mAlwaysOnDisplayPolicy);
mScreen.onScreenState(Display.STATE_ON);
}
@@ -106,6 +116,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(3);
@@ -116,6 +127,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
public void testAod_usesDebugValue() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS);
intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1);
@@ -141,6 +154,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -153,9 +167,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
@Test
public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
- true /* debuggable */);
+ null /* sensor */, mDozeHost, null /* handler */,
+ mAlwaysOnDisplayPolicy);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE);
reset(mDozeHost);
@@ -174,6 +187,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE);
mScreen.transitionTo(DOZE_PULSE_DONE, DOZE);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -183,9 +197,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
@Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
- true /* debuggable */);
+ null /* sensor */, mDozeHost, null /* handler */,
+ mAlwaysOnDisplayPolicy);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -198,6 +211,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.transitionTo(DOZE_AOD, FINISH);
+ waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -205,10 +219,11 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
- public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() throws Exception {
+ public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(1);
mSensor.sendSensorEvent(0);
@@ -222,6 +237,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(2);
@@ -233,6 +249,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
reset(mDozeHost);
mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(2);
verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
@@ -242,6 +259,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(2);
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
@@ -251,4 +269,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
+
+ private void waitForSensorManager() {
+ mFakeExecutor.runAllReady();
+ }
} \ No newline at end of file
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 ebd2c3afe646..4f0ff4242b6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -47,6 +47,7 @@ import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.wakelock.WakeLock;
import org.junit.Before;
@@ -84,6 +85,7 @@ public class DozeSensorsTest extends SysuiTestCase {
private DozeLog mDozeLog;
@Mock
private ProximitySensor mProximitySensor;
+ private FakeSettings mFakeSettings = new FakeSettings();
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
private TestableLooper mTestableLooper;
private DozeSensors mDozeSensors;
@@ -152,9 +154,9 @@ public class DozeSensorsTest extends SysuiTestCase {
private class TestableDozeSensors extends DozeSensors {
TestableDozeSensors() {
- super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
+ super(getContext(), mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
- mProximitySensor);
+ mProximitySensor, mFakeSettings);
for (TriggerSensor sensor : mSensors) {
if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 655f933d28fe..1ed58714fb9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -30,9 +30,7 @@ import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.hardware.Sensor;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.Handler;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
@@ -43,11 +41,13 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeThreadFactory;
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.FakeThresholdSensor;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -92,15 +92,16 @@ public class DozeTriggersTest extends SysuiTestCase {
mTapSensor = mSensors.getFakeTapSensor().getSensor();
WakeLock wakeLock = new WakeLockFake();
AsyncSensorManager asyncSensorManager =
- new AsyncSensorManager(mSensors, null, new Handler());
+ new AsyncSensorManager(mSensors, new FakeThreadFactory(mExecutor), null);
FakeThresholdSensor thresholdSensor = new FakeThresholdSensor();
thresholdSensor.setLoaded(true);
mProximitySensor = new FakeProximitySensor(thresholdSensor, null, mExecutor);
- mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
- asyncSensorManager, wakeLock, true, mDockManager, mProximitySensor,
- mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher);
+ mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters,
+ asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
+ mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings());
+ mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
@@ -186,6 +187,6 @@ public class DozeTriggersTest extends SysuiTestCase {
}
private void waitForSensorManager() {
- TestableLooper.get(this).processAllMessages();
+ mExecutor.runAllReady();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index c5bddc1f096f..069699c271f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -81,8 +81,9 @@ public class DozeUiTest extends SysuiTestCase {
mWakeLock = new WakeLockFake();
mHandler = mHandlerThread.getThreadHandler();
- mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
+ mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
+ mDozeUi.setDozeMachine(mMachine);
}
@After
@@ -136,8 +137,9 @@ public class DozeUiTest extends SysuiTestCase {
reset(mDozeParameters);
reset(mHost);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
- mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
+ mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
+ mDozeUi.setDozeMachine(mMachine);
// Never animate if display doesn't support it.
mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 24b128af9fbd..90e9ef4e9c56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -95,7 +95,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
@Test
public void testOnGoingToSleep_UpdatesKeyguardGoingAway() {
mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
- verify(mUpdateMonitor).setKeyguardGoingAway(false);
+ verify(mUpdateMonitor).dispatchKeyguardGoingAway(false);
verify(mStatusBarKeyguardViewManager, never()).setKeyguardGoingAwayState(anyBoolean());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 008dc12bba03..730c941cc414 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -36,6 +36,7 @@ import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -70,7 +71,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
`when`(mediaHost.visible).thenReturn(false)
triggerVisibilityListener()
- verify(mediaHeaderView).visibility = eq(GONE)
+ verify(mediaHeaderView, atLeastOnce()).visibility = eq(GONE)
}
@Test
fun testAttach_visibleOnKeyguard() {
@@ -80,7 +81,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
.thenReturn(true)
triggerVisibilityListener()
- verify(mediaHeaderView).visibility = eq(VISIBLE)
+ verify(mediaHeaderView, atLeastOnce()).visibility = eq(VISIBLE)
}
@Test
fun testAttach_hiddenOnKeyguard_whenNotificationsAreHidden() {
@@ -90,7 +91,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
.thenReturn(false)
triggerVisibilityListener()
- verify(mediaHeaderView).visibility = eq(GONE)
+ verify(mediaHeaderView, atLeastOnce()).visibility = eq(GONE)
}
private fun triggerVisibilityListener() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index b7f317b38743..c63781cb110a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -64,6 +64,7 @@ private const val DEVICE_NAME = "DEVICE_NAME"
private const val SESSION_KEY = "SESSION_KEY"
private const val SESSION_ARTIST = "SESSION_ARTIST"
private const val SESSION_TITLE = "SESSION_TITLE"
+private const val USER_ID = 0
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -180,7 +181,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindWhenUnattached() {
- val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, null, null, device, true, null)
player.bind(state)
assertThat(player.isPlaying()).isFalse()
@@ -189,7 +190,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindText() {
player.attach(holder)
- val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
player.bind(state)
assertThat(appName.getText()).isEqualTo(APP)
@@ -200,7 +201,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindBackgroundColor() {
player.attach(holder)
- val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
player.bind(state)
val list = ArgumentCaptor.forClass(ColorStateList::class.java)
@@ -211,7 +212,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindDevice() {
player.attach(holder)
- val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
player.bind(state)
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
@@ -223,7 +224,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
seamless.id = 1
seamlessFallback.id = 2
player.attach(holder)
- val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, true, null)
player.bind(state)
verify(expandedSet).setVisibility(seamless.id, View.GONE)
@@ -235,7 +236,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindNullDevice() {
player.attach(holder)
- val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null)
player.bind(state)
assertThat(seamless.isEnabled()).isTrue()
@@ -246,7 +247,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindDeviceResumptionPlayer() {
player.attach(holder)
- val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null,
resumption = true)
player.bind(state)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 9fdd9ad744ff..492b33e3c4a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.graphics.Color;
@@ -39,6 +40,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.util.ArrayList;
+import java.util.Map;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -46,12 +48,14 @@ import java.util.ArrayList;
public class MediaDataCombineLatestTest extends SysuiTestCase {
private static final String KEY = "TEST_KEY";
+ private static final String OLD_KEY = "TEST_KEY_OLD";
private static final String APP = "APP";
private static final String PACKAGE = "PKG";
private static final int BG_COLOR = Color.RED;
private static final String ARTIST = "ARTIST";
private static final String TITLE = "TITLE";
private static final String DEVICE_NAME = "DEVICE_NAME";
+ private static final int USER_ID = 0;
private MediaDataCombineLatest mManager;
@@ -78,7 +82,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
mManager.addListener(mListener);
- mMediaData = new MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null,
+ mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, false,
KEY, false);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
@@ -95,7 +99,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
@Test
public void eventNotEmittedWithoutMedia() {
// WHEN device source emits an event without media data
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN an event isn't emitted
verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any());
}
@@ -103,7 +107,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
@Test
public void emitEventAfterDeviceFirst() {
// GIVEN that a device event has already been received
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN media event is received
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// THEN the listener receives a combined event
@@ -117,7 +121,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
// GIVEN that media event has already been received
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// WHEN device event is received
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture());
@@ -125,6 +129,64 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
}
@Test
+ public void migrateKeyMediaFirst() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ reset(mListener);
+ // WHEN a key migration event is received
+ mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ // THEN the listener receives a combined event
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
+ public void migrateKeyDeviceFirst() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ reset(mListener);
+ // WHEN a key migration event is received
+ mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ // THEN the listener receives a combined event
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
+ public void migrateKeyMediaAfter() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ reset(mListener);
+ // WHEN a second key migration event is received for media
+ mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ // THEN the key has already been migrated
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
+ public void migrateKeyDeviceAfter() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ reset(mListener);
+ // WHEN a second key migration event is received for the device
+ mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ // THEN the key has already be migrated
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
public void mediaDataRemoved() {
// WHEN media data is removed without first receiving device or data
mDataListener.onMediaDataRemoved(KEY);
@@ -141,7 +203,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
@Test
public void mediaDataRemovedAfterDeviceEvent() {
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
mDataListener.onMediaDataRemoved(KEY);
verify(mListener).onMediaDataRemoved(eq(KEY));
}
@@ -150,7 +212,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
public void mediaDataKeyUpdated() {
// GIVEN that device and media events have already been received
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN the key is changed
mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData);
// THEN the listener gets a load event with the correct keys
@@ -158,6 +220,18 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
verify(mListener).onMediaDataLoaded(eq("NEW_KEY"), any(), captor.capture());
}
+ @Test
+ public void getDataIncludesDevice() {
+ // GIVEN that device and media events have been received
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
+ mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
+
+ // THEN the result of getData includes device info
+ Map<String, MediaData> results = mManager.getData();
+ assertThat(results.get(KEY)).isNotNull();
+ assertThat(results.get(KEY).getDevice()).isEqualTo(mDeviceData);
+ }
+
private MediaDataManager.Listener captureDataListener() {
ArgumentCaptor<MediaDataManager.Listener> captor = ArgumentCaptor.forClass(
MediaDataManager.Listener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
new file mode 100644
index 000000000000..afb64a7649b4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.graphics.Color
+import androidx.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+private const val KEY = "TEST_KEY"
+private const val KEY_ALT = "TEST_KEY_2"
+private const val USER_MAIN = 0
+private const val USER_GUEST = 10
+private const val APP = "APP"
+private const val BG_COLOR = Color.RED
+private const val PACKAGE = "PKG"
+private const val ARTIST = "ARTIST"
+private const val TITLE = "TITLE"
+private const val DEVICE_NAME = "DEVICE_NAME"
+
+private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+private fun <T> any(): T = Mockito.any()
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class MediaDataFilterTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var combineLatest: MediaDataCombineLatest
+ @Mock
+ private lateinit var listener: MediaDataManager.Listener
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var mediaResumeListener: MediaResumeListener
+ @Mock
+ private lateinit var mediaDataManager: MediaDataManager
+ @Mock
+ private lateinit var lockscreenUserManager: NotificationLockscreenUserManager
+ @Mock
+ private lateinit var executor: Executor
+
+ private lateinit var mediaDataFilter: MediaDataFilter
+ private lateinit var dataMain: MediaData
+ private lateinit var dataGuest: MediaData
+ private val device = MediaDeviceData(true, null, DEVICE_NAME)
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mediaDataFilter = MediaDataFilter(combineLatest, broadcastDispatcher, mediaResumeListener,
+ mediaDataManager, lockscreenUserManager, executor)
+ mediaDataFilter.addListener(listener)
+
+ // Start all tests as main user
+ setUser(USER_MAIN)
+
+ // Set up test media data
+ dataMain = MediaData(USER_MAIN, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ emptyList(), PACKAGE, null, null, device, true, null)
+
+ dataGuest = MediaData(USER_GUEST, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
+ emptyList(), emptyList(), PACKAGE, null, null, device, true, null)
+ }
+
+ private fun setUser(id: Int) {
+ `when`(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
+ `when`(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true)
+ mediaDataFilter.handleUserSwitched(id)
+ }
+
+ @Test
+ fun testOnDataLoadedForCurrentUser_callsListener() {
+ // GIVEN a media for main user
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+
+ // THEN we should tell the listener
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain))
+ }
+
+ @Test
+ fun testOnDataLoadedForGuest_doesNotCallListener() {
+ // GIVEN a media for guest user
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
+
+ // THEN we should NOT tell the listener
+ verify(listener, never()).onMediaDataLoaded(any(), any(), any())
+ }
+
+ @Test
+ fun testOnRemovedForCurrent_callsListener() {
+ // GIVEN a media was removed for main user
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+ mediaDataFilter.onMediaDataRemoved(KEY)
+
+ // THEN we should tell the listener
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ }
+
+ @Test
+ fun testOnRemovedForGuest_doesNotCallListener() {
+ // GIVEN a media was removed for guest user
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
+ mediaDataFilter.onMediaDataRemoved(KEY)
+
+ // THEN we should NOT tell the listener
+ verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ }
+
+ @Test
+ fun testOnUserSwitched_removesOldUserControls() {
+ // GIVEN that we have a media loaded for main user
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+
+ // and we switch to guest user
+ setUser(USER_GUEST)
+
+ // THEN we should remove the main user's media
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ }
+
+ @Test
+ fun testOnUserSwitched_addsNewUserControls() {
+ // GIVEN that we had some media for both users
+ val dataMap = mapOf(KEY to dataMain, KEY_ALT to dataGuest)
+ `when`(combineLatest.getData()).thenReturn(dataMap)
+
+ // and we switch to guest user
+ setUser(USER_GUEST)
+
+ // THEN we should add back the guest user media
+ verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest))
+
+ // but not the main user's
+ verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain))
+ }
+
+ @Test
+ fun testHasAnyMedia() {
+ assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
+
+ mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
+ assertThat(mediaDataFilter.hasAnyMedia()).isTrue()
+ }
+
+ @Test
+ fun testHasActiveMedia() {
+ assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
+ val data = dataMain.copy(active = true)
+
+ mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
+ assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+ }
+
+ @Test
+ fun testHasAnyMedia_onlyCurrentUser() {
+ assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
+
+ mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataGuest)
+ assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
+ }
+
+ @Test
+ fun testHasActiveMedia_onlyCurrentUser() {
+ assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
+ val data = dataGuest.copy(active = true)
+
+ mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
+ assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
+ }
+
+ @Test
+ fun testOnNotificationRemoved_doesntHaveMedia() {
+ mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
+ mediaDataFilter.onMediaDataRemoved(KEY)
+ assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
+ }
+
+ @Test
+ fun testOnSwipeToDismiss_setsTimedOut() {
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+ mediaDataFilter.onSwipeToDismiss()
+
+ verify(mediaDataManager).setTimedOut(eq(KEY), eq(true))
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index e56bbabfdc0b..59c2d0e86c56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -31,10 +31,12 @@ import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
private const val KEY = "KEY"
+private const val KEY_2 = "KEY_2"
private const val PACKAGE_NAME = "com.android.systemui"
private const val APP_NAME = "SystemUI"
private const val SESSION_ARTIST = "artist"
private const val SESSION_TITLE = "title"
+private const val USER_ID = 0
private fun <T> anyObject(): T {
return Mockito.anyObject<T>()
@@ -91,28 +93,15 @@ class MediaDataManagerTest : SysuiTestCase() {
}
@Test
- fun testHasActiveMedia() {
- assertThat(mediaDataManager.hasActiveMedia()).isFalse()
- val data = mock(MediaData::class.java)
-
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
- mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = data)
- assertThat(mediaDataManager.hasActiveMedia()).isFalse()
-
- whenever(data.active).thenReturn(true)
- assertThat(mediaDataManager.hasActiveMedia()).isTrue()
- }
-
- @Test
- fun testOnSwipeToDismiss_deactivatesMedia() {
- val data = MediaData(initialized = true, backgroundColor = 0, app = null, appIcon = null,
- artist = null, song = null, artwork = null, actions = emptyList(),
+ fun testSetTimedOut_deactivatesMedia() {
+ val data = MediaData(userId = USER_ID, initialized = true, backgroundColor = 0, app = null,
+ appIcon = null, artist = null, song = null, artwork = null, actions = emptyList(),
actionsToShowInCompact = emptyList(), packageName = "INVALID", token = null,
clickIntent = null, device = null, active = true, resumeAction = null)
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = data)
- mediaDataManager.onSwipeToDismiss()
+ mediaDataManager.setTimedOut(KEY, timedOut = true)
assertThat(data.active).isFalse()
}
@@ -141,37 +130,6 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
assertThat(listener.data!!.active).isTrue()
-
- // Swiping away makes the notification not active
- mediaDataManager.onSwipeToDismiss()
- assertThat(mediaDataManager.hasActiveMedia()).isFalse()
-
- // And when a notification is updated
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-
- // MediaData should still be inactive
- assertThat(mediaDataManager.hasActiveMedia()).isFalse()
- }
-
- @Test
- fun testHasAnyMedia_whenAddingMedia() {
- assertThat(mediaDataManager.hasAnyMedia()).isFalse()
- val data = mock(MediaData::class.java)
-
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
- mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = data)
- assertThat(mediaDataManager.hasAnyMedia()).isTrue()
- }
-
- @Test
- fun testOnNotificationRemoved_doesntHaveMedia() {
- val data = mock(MediaData::class.java)
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
- mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = data)
- mediaDataManager.onNotificationRemoved(KEY)
- assertThat(mediaDataManager.hasAnyMedia()).isFalse()
}
@Test
@@ -199,8 +157,43 @@ class MediaDataManagerTest : SysuiTestCase() {
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
mediaDataManager.onNotificationRemoved(KEY)
- // THEN the media data indicates that it is
+ // THEN the media data indicates that it is for resumption
assertThat(listener.data!!.resumption).isTrue()
+ // AND the new key is the package name
+ assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.oldKey!!).isEqualTo(KEY)
+ assertThat(listener.removedKey).isNull()
+ }
+
+ @Test
+ fun testOnNotificationRemoved_twoWithResumption() {
+ // GIVEN that the manager has two notifications with resume actions
+ val listener = TestListener()
+ mediaDataManager.addListener(listener)
+ whenever(controller.metadata).thenReturn(metadataBuilder.build())
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ mediaDataManager.onNotificationAdded(KEY_2, mediaNotification)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
+ val data = listener.data!!
+ assertThat(data.resumption).isFalse()
+ val resumableData = data.copy(resumeAction = Runnable {})
+ mediaDataManager.onMediaDataLoaded(KEY, null, resumableData)
+ mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData)
+ // WHEN the first is removed
+ mediaDataManager.onNotificationRemoved(KEY)
+ // THEN the data is for resumption and the key is migrated to the package name
+ assertThat(listener.data!!.resumption).isTrue()
+ assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.oldKey!!).isEqualTo(KEY)
+ assertThat(listener.removedKey).isNull()
+ // WHEN the second is removed
+ mediaDataManager.onNotificationRemoved(KEY_2)
+ // THEN the data is for resumption and the second key is removed
+ assertThat(listener.data!!.resumption).isTrue()
+ assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.removedKey!!).isEqualTo(KEY_2)
}
@Test
@@ -212,8 +205,8 @@ class MediaDataManagerTest : SysuiTestCase() {
setTitle(SESSION_TITLE)
build()
}
- mediaDataManager.addResumptionControls(desc, Runnable {}, session.sessionToken, APP_NAME,
- pendingIntent, PACKAGE_NAME)
+ mediaDataManager.addResumptionControls(USER_ID, desc, Runnable {}, session.sessionToken,
+ APP_NAME, pendingIntent, PACKAGE_NAME)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
// THEN the media data indicates that it is for resumption
@@ -233,6 +226,7 @@ class MediaDataManagerTest : SysuiTestCase() {
var data: MediaData? = null
var key: String? = null
var oldKey: String? = null
+ var removedKey: String? = null
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
this.key = key
@@ -241,9 +235,7 @@ class MediaDataManagerTest : SysuiTestCase() {
}
override fun onMediaDataRemoved(key: String) {
- this.key = key
- oldKey = null
- data = null
+ removedKey = key
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 6c7f2e8d7925..7bc15dd46cd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -58,6 +58,7 @@ private const val SESSION_KEY = "SESSION_KEY"
private const val SESSION_ARTIST = "SESSION_ARTIST"
private const val SESSION_TITLE = "SESSION_TITLE"
private const val DEVICE_NAME = "DEVICE_NAME"
+private const val USER_ID = 0
private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
@@ -71,7 +72,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Mock private lateinit var lmmFactory: LocalMediaManagerFactory
@Mock private lateinit var lmm: LocalMediaManager
@Mock private lateinit var mr2: MediaRouter2Manager
- private lateinit var fakeExecutor: FakeExecutor
+ private lateinit var fakeFgExecutor: FakeExecutor
+ private lateinit var fakeBgExecutor: FakeExecutor
@Mock private lateinit var dumpster: DumpManager
@Mock private lateinit var listener: MediaDeviceManager.Listener
@Mock private lateinit var device: MediaDevice
@@ -86,9 +88,10 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Before
fun setUp() {
- fakeExecutor = FakeExecutor(FakeSystemClock())
- manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager,
- dumpster)
+ fakeFgExecutor = FakeExecutor(FakeSystemClock())
+ fakeBgExecutor = FakeExecutor(FakeSystemClock())
+ manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor,
+ mediaDataManager, dumpster)
manager.addListener(listener)
// Configure mocks.
@@ -118,7 +121,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
setSmallIcon(android.R.drawable.ic_media_pause)
setStyle(Notification.MediaStyle().setMediaSession(session.getSessionToken()))
}
- mediaData = MediaData(true, 0, PACKAGE, null, null, SESSION_TITLE, null,
+ mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
device = null, active = true, resumeAction = null)
}
@@ -143,13 +146,15 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
fun loadAndRemoveMediaData() {
manager.onMediaDataLoaded(KEY, null, mediaData)
manager.onMediaDataRemoved(KEY)
+ fakeBgExecutor.runAllReady()
verify(lmm).unregisterCallback(any())
}
@Test
fun loadMediaDataWithNullToken() {
manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
- fakeExecutor.runAllReady()
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
assertThat(data.name).isEqualTo(DEVICE_NAME)
@@ -162,10 +167,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
reset(listener)
// WHEN data is loaded with a new key
manager.onMediaDataLoaded(KEY, KEY_OLD, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the listener for the old key should removed.
verify(lmm).unregisterCallback(any())
// AND a new device event emitted
- val data = captureDeviceData(KEY)
+ val data = captureDeviceData(KEY, KEY_OLD)
assertThat(data.enabled).isTrue()
assertThat(data.name).isEqualTo(DEVICE_NAME)
}
@@ -178,26 +185,32 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
// WHEN the new key is the same as the old key
manager.onMediaDataLoaded(KEY, KEY, mediaData)
// THEN no event should be emitted
- verify(listener, never()).onMediaDeviceChanged(eq(KEY), any())
+ verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any())
}
@Test
fun unknownOldKey() {
- manager.onMediaDataLoaded(KEY, "unknown", mediaData)
- verify(listener).onMediaDeviceChanged(eq(KEY), any())
+ val oldKey = "unknown"
+ manager.onMediaDataLoaded(KEY, oldKey, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any())
}
@Test
fun updateToSessionTokenWithNullRoute() {
// GIVEN that media data has been loaded with a null token
manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(listener)
// WHEN media data is loaded with a different token
// AND that token results in a null route
- reset(listener)
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the device should be disabled
- fakeExecutor.runAllReady()
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
assertThat(data.name).isNull()
@@ -208,7 +221,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
fun deviceEventOnAddNotification() {
// WHEN a notification is added
manager.onMediaDataLoaded(KEY, null, mediaData)
- val deviceCallback = captureCallback()
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the update is dispatched to the listener
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
@@ -222,16 +236,18 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
manager.removeListener(listener)
// THEN it doesn't receive device events
manager.onMediaDataLoaded(KEY, null, mediaData)
- verify(listener, never()).onMediaDeviceChanged(eq(KEY), any())
+ verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any())
}
@Test
fun deviceListUpdate() {
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
val deviceCallback = captureCallback()
// WHEN the device list changes
deviceCallback.onDeviceListUpdate(mutableListOf(device))
- assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+ assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
+ assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the update is dispatched to the listener
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
@@ -242,10 +258,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun selectedDeviceStateChanged() {
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
val deviceCallback = captureCallback()
// WHEN the selected device changes state
deviceCallback.onSelectedDeviceStateChanged(device, 1)
- assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+ assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
+ assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the update is dispatched to the listener
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
@@ -268,6 +286,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN a notification is added
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
@@ -279,13 +299,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() {
// GIVEN a notif is added
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
reset(listener)
// AND MR2Manager returns null for routing session
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN the selected device changes state
val deviceCallback = captureCallback()
deviceCallback.onSelectedDeviceStateChanged(device, 1)
- fakeExecutor.runAllReady()
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
@@ -297,13 +320,16 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() {
// GIVEN a notif is added
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
reset(listener)
// GIVEN that MR2Manager returns null for routing session
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN the selected device changes state
val deviceCallback = captureCallback()
deviceCallback.onDeviceListUpdate(mutableListOf(device))
- fakeExecutor.runAllReady()
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
@@ -317,9 +343,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
return captor.getValue()
}
- fun captureDeviceData(key: String): MediaDeviceData {
+ fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData {
val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
- verify(listener).onMediaDeviceChanged(eq(key), captor.capture())
+ verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture())
return captor.getValue()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index 916fd0fe11b7..7a8e4f7e9b85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -32,7 +32,9 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
@@ -48,6 +50,7 @@ private const val PACKAGE = "PKG"
private const val SESSION_KEY = "SESSION_KEY"
private const val SESSION_ARTIST = "SESSION_ARTIST"
private const val SESSION_TITLE = "SESSION_TITLE"
+private const val USER_ID = 0
private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
private fun <T> anyObject(): T {
@@ -93,7 +96,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
setPlaybackState(playbackBuilder.build())
}
session.setActive(true)
- mediaData = MediaData(true, 0, PACKAGE, null, null, SESSION_TITLE, null,
+ mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
device = null, active = true, resumeAction = null)
}
@@ -118,6 +121,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
verify(executor).executeDelayed(capture(timeoutCaptor), anyLong())
+ verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
}
@Test
@@ -133,6 +137,24 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
}
@Test
+ fun testOnMediaDataLoaded_migratesKeys() {
+ // From not playing
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ clearInvocations(mediaController)
+
+ // To playing
+ val playingState = mock(android.media.session.PlaybackState::class.java)
+ `when`(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
+ `when`(mediaController.playbackState).thenReturn(playingState)
+ mediaTimeoutListener.onMediaDataLoaded("NEWKEY", KEY, mediaData)
+ verify(mediaController).unregisterCallback(anyObject())
+ verify(mediaController).registerCallback(anyObject())
+
+ // Enqueues callback
+ verify(executor).execute(anyObject())
+ }
+
+ @Test
fun testOnPlaybackStateChanged_schedulesTimeout_whenPaused() {
// Assuming we're registered
testOnMediaDataLoaded_registersPlaybackListener()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
index e9a0a40fe8ae..71554608f04b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -69,7 +69,7 @@ public class SeekBarObserverTest : SysuiTestCase() {
fun seekBarGone() {
// WHEN seek bar is disabled
val isEnabled = false
- val data = SeekBarViewModel.Progress(isEnabled, false, null, null)
+ val data = SeekBarViewModel.Progress(isEnabled, false, null, 0)
observer.onChanged(data)
// THEN seek bar shows just a thin line with no text
assertThat(seekBarView.isEnabled()).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index c8ef9fbf06e5..b81ab74458ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -204,6 +204,22 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ fun updateDurationNoMetadata() {
+ // GIVEN that the metadata is null
+ whenever(mockController.getMetadata()).thenReturn(null)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
+ // WHEN the controller is updated
+ viewModel.updateController(mockController)
+ // THEN the seek bar is disabled
+ assertThat(viewModel.progress.value!!.enabled).isFalse()
+ }
+
+ @Test
fun updateElapsedTime() {
// GIVEN that the PlaybackState contains the current position
val position = 200L
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
index 2d9fa1bbd5ca..583d0692565f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.onehanded;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -52,12 +52,12 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mOneHandedAnimationController = new OneHandedAnimationController(mContext,
+ mOneHandedAnimationController = new OneHandedAnimationController(
new OneHandedSurfaceTransactionHelper(mContext));
}
@Test
- public void testGetAnimator_withSameBounds_returnTranslateAnimator() {
+ public void testGetAnimator_withSameBounds_returnAnimator() {
final Rect originalBounds = new Rect(0, 0, TEST_BOUNDS_WIDTH, TEST_BOUNDS_HEIGHT);
final Rect destinationBounds = originalBounds;
destinationBounds.offset(0, 300);
@@ -65,7 +65,6 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase {
mOneHandedAnimationController
.getAnimator(mMockLeash, originalBounds, destinationBounds);
- assertEquals("Expected translate animation",
- OneHandedAnimationController.ANIM_TYPE_TRANSLATE, animator.getAnimationType());
+ assertNotNull(animator);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 28dad14f221a..3231b2852e7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -29,11 +29,11 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.DisplayInfo;
+import android.view.Display;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.IWindowContainerToken;
@@ -41,14 +41,15 @@ import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -58,8 +59,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
static final int DISPLAY_HEIGHT = 1000;
DisplayAreaInfo mDisplayAreaInfo;
- DisplayInfo mDisplayInfo;
- Handler mUpdateHandler;
+ Display mDisplay;
OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
WindowContainerToken mToken;
@@ -76,24 +76,19 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
DisplayController mMockDisplayController;
@Mock
SurfaceControl mMockLeash;
+ @Spy
+ Handler mUpdateHandler;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
- mMockDisplayController,
- mMockAnimationController,
- mMockSurfaceTransactionHelper);
- mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
mToken = new WindowContainerToken(mMockRealToken);
mLeash = new SurfaceControl();
+ mDisplay = mContext.getDisplay();
mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, FEATURE_ONE_HANDED);
mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT;
- mDisplayInfo = new DisplayInfo();
- mDisplayInfo.logicalWidth = DISPLAY_WIDTH;
- mDisplayInfo.logicalHeight = DISPLAY_HEIGHT;
-
when(mMockAnimationController.getAnimator(any(), any(), any())).thenReturn(null);
+ when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockSurfaceTransactionHelper.translate(any(), any(), anyFloat())).thenReturn(
mMockSurfaceTransactionHelper);
when(mMockSurfaceTransactionHelper.crop(any(), any(), any())).thenReturn(
@@ -106,8 +101,11 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
when(mMockAnimator.setTransitionDirection(anyInt())).thenReturn(mFakeAnimator);
when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
- when(mMockDisplayController.getDisplay(anyInt())).thenReturn(null);
+ mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
+ mMockDisplayController,
+ mMockAnimationController);
+ mUpdateHandler = mDisplayAreaOrganizer.getUpdateHandler();
}
@Test
@@ -135,35 +133,152 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo);
- assertThat(mDisplayAreaOrganizer.mDisplayAreaInfo).isEqualTo(newDisplayAreaInfo);
+ assertThat(mDisplayAreaOrganizer.mDisplayAreaMap.containsKey(mDisplayAreaInfo)).isTrue();
}
+ @Ignore("b/160848002")
@Test
public void testScheduleOffset() {
final int xOffSet = 0;
final int yOffSet = 100;
+ TestableLooper.get(this).processAllMessages();
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mDisplayAreaOrganizer.scheduleOffset(xOffSet, yOffSet);
assertThat(mUpdateHandler.hasMessages(
- OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isNotNull();
+ OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isEqualTo(true);
}
+ @Ignore("b/160848002")
@Test
- public void testResetImmediately() {
- // To prevent mNativeObject of Surface is null in the test flow
+ public void testRotation_portraitToLandscape() {
when(mMockLeash.isValid()).thenReturn(false);
- final Rect newBounds = new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
- final DisplayAreaInfo newDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
- FEATURE_ONE_HANDED);
- newDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ // Rotate 0 -> 90
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90);
- mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mMockLeash);
- mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo);
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 0 -> 270
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 180 -> 90
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90);
assertThat(mUpdateHandler.hasMessages(
- OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isNotNull();
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 180 -> 270
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
}
+ @Ignore("b/160848002")
+ @Test
+ public void testRotation_landscapeToPortrait() {
+ when(mMockLeash.isValid()).thenReturn(false);
+ // Rotate 90 -> 0
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 90 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 270 -> 0
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 270 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+ }
+
+ @Ignore("b/160848002")
+ @Test
+ public void testRotation_portraitToPortrait() {
+ when(mMockLeash.isValid()).thenReturn(false);
+ // Rotate 0 -> 0
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 0 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 180 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 180 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+ }
+
+ @Ignore("b/160848002")
+ @Test
+ public void testRotation_landscapeToLandscape() {
+ when(mMockLeash.isValid()).thenReturn(false);
+ // Rotate 90 -> 90
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 90 -> 270
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 270 -> 270
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 270 -> 90
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
new file mode 100644
index 000000000000..3b284b14c36f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.onehanded;
+
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.statusbar.phone.NavigationModeController;
+import com.android.wm.shell.common.DisplayController;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class OneHandedGestureHandlerTest extends OneHandedTestCase {
+ Instrumentation mInstrumentation;
+ OneHandedTouchHandler mTouchHandler;
+ OneHandedGestureHandler mGestureHandler;
+ OneHandedManagerImpl mOneHandedManagerImpl;
+ @Mock
+ DisplayController mMockDisplayController;
+ @Mock
+ OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
+ @Mock
+ SysUiState mMockSysUiState;
+ @Mock
+ NavigationModeController mMockNavigationModeController;
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mTouchHandler = Mockito.spy(new OneHandedTouchHandler());
+ mGestureHandler = Mockito.spy(new OneHandedGestureHandler(
+ mContext, mMockDisplayController, mMockNavigationModeController));
+ mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(),
+ mMockDisplayController,
+ mMockDisplayAreaOrganizer,
+ mTouchHandler,
+ mGestureHandler,
+ mMockSysUiState);
+ }
+
+ @Test
+ public void testOneHandedManager_registerForDisplayAreaOrganizer() {
+ verify(mMockDisplayAreaOrganizer, times(1)).registerTransitionCallback(mGestureHandler);
+ }
+
+ @Test
+ public void testOneHandedManager_setGestureEventListener() {
+ verify(mGestureHandler).setGestureEventListener(any());
+
+ assertThat(mGestureHandler.mGestureEventCallback).isNotNull();
+ }
+
+ @Test
+ public void testReceiveNewConfig_whenSetOneHandedEnabled() {
+ // 1st called at init
+ verify(mGestureHandler).onOneHandedEnabled(true);
+ mOneHandedManagerImpl.setOneHandedEnabled(true);
+ // 2nd called by setOneHandedEnabled()
+ verify(mGestureHandler, times(2)).onOneHandedEnabled(true);
+ }
+
+ @Test
+ public void testOneHandedDisabled_shouldDisposeInputChannel() {
+ mOneHandedManagerImpl.setOneHandedEnabled(false);
+
+ assertThat(mGestureHandler.mInputMonitor).isNull();
+ assertThat(mGestureHandler.mInputEventReceiver).isNull();
+ }
+
+ @Test
+ public void testChangeNavBarTo2Button_shouldDisposeInputChannel() {
+ // 1st called at init
+ verify(mGestureHandler).onOneHandedEnabled(true);
+ mOneHandedManagerImpl.setOneHandedEnabled(true);
+ // 2nd called by setOneHandedEnabled()
+ verify(mGestureHandler, times(2)).onOneHandedEnabled(true);
+
+ mGestureHandler.onNavigationModeChanged(NAV_BAR_MODE_2BUTTON);
+
+ assertThat(mGestureHandler.mInputMonitor).isNull();
+ assertThat(mGestureHandler.mInputEventReceiver).isNull();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
index e2a4b10951c7..55bec54eacb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
@@ -28,11 +28,12 @@ import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.Display;
import androidx.test.filters.SmallTest;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
@@ -45,6 +46,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedManagerImplTest extends OneHandedTestCase {
+ Display mDisplay;
OneHandedManagerImpl mOneHandedManagerImpl;
OneHandedTimeoutHandler mTimeoutHandler;
@@ -53,35 +55,36 @@ public class OneHandedManagerImplTest extends OneHandedTestCase {
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
- OneHandedSurfaceTransactionHelper mMockSurfaceTransactionHelper;
- @Mock
OneHandedTouchHandler mMockTouchHandler;
@Mock
+ OneHandedGestureHandler mMockGestureHandler;
+ @Mock
SysUiState mMockSysUiState;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mDisplay = mContext.getDisplay();
mOneHandedManagerImpl = new OneHandedManagerImpl(getContext(),
mMockDisplayController,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
+ mMockGestureHandler,
mMockSysUiState);
mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
+ when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
}
-
@Test
public void testDefaultShouldNotInOneHanded() {
final OneHandedSurfaceTransactionHelper transactionHelper =
new OneHandedSurfaceTransactionHelper(mContext);
final OneHandedAnimationController animationController = new OneHandedAnimationController(
- mContext, transactionHelper);
+ transactionHelper);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
- mContext, mMockDisplayController, animationController,
- mMockSurfaceTransactionHelper);
+ mContext, mMockDisplayController, animationController);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
index 6db2e1b7e330..3a4ba6a213dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
@@ -30,7 +30,8 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.wm.DisplayController;
+import com.android.systemui.statusbar.phone.NavigationModeController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
@@ -45,10 +46,13 @@ import org.mockito.MockitoAnnotations;
public class OneHandedTouchHandlerTest extends OneHandedTestCase {
Instrumentation mInstrumentation;
OneHandedTouchHandler mTouchHandler;
+ OneHandedGestureHandler mGestureHandler;
OneHandedManagerImpl mOneHandedManagerImpl;
@Mock
DisplayController mMockDisplayController;
@Mock
+ NavigationModeController mMockNavigationModeController;
+ @Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
SysUiState mMockSysUiState;
@@ -58,10 +62,13 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase {
MockitoAnnotations.initMocks(this);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mTouchHandler = Mockito.spy(new OneHandedTouchHandler());
+ mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
+ mMockNavigationModeController);
mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(),
mMockDisplayController,
mMockDisplayAreaOrganizer,
mTouchHandler,
+ mGestureHandler,
mMockSysUiState);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
index 7a234a4d89a7..ffedb07b8db4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
@@ -20,6 +20,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,6 +43,9 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedUITest extends OneHandedTestCase {
+ private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+
+ boolean mIsSupportOneHandedMode;
CommandQueue mCommandQueue;
KeyguardUpdateMonitor mKeyguardUpdateMonitor;
OneHandedUI mOneHandedUI;
@@ -58,6 +62,7 @@ public class OneHandedUITest extends OneHandedTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mIsSupportOneHandedMode = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
mCommandQueue = new CommandQueue(mContext);
mScreenLifecycle = new ScreenLifecycle();
mOneHandedUI = new OneHandedUI(mContext,
@@ -72,6 +77,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Test
public void testStartOneHanded() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
mOneHandedUI.startOneHanded();
verify(mMockOneHandedManagerImpl, times(1)).startOneHanded();
@@ -79,6 +88,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Test
public void testStopOneHanded() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
mOneHandedUI.stopOneHanded();
verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
@@ -86,6 +99,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Test
public void testRegisterSettingsObserver_forEnabled() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
final String key = Settings.Secure.ONE_HANDED_MODE_ENABLED;
verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -93,6 +110,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Test
public void testRegisterSettingsObserver_forTimeout() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
final String key = Settings.Secure.ONE_HANDED_MODE_TIMEOUT;
verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -100,6 +121,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Test
public void testRegisterSettingsObserver_forTapAppExit() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
final String key = Settings.Secure.TAPS_APP_TO_EXIT;
verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -107,6 +132,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Test
public void tesSettingsObserver_updateTapAppToExit() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.TAPS_APP_TO_EXIT, 1);
@@ -115,6 +144,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Test
public void tesSettingsObserver_updateEnabled() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
@@ -123,6 +156,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Test
public void tesSettingsObserver_updateTimeout() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
@@ -134,6 +171,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Ignore("Clarifying do not receive callback")
@Test
public void testKeyguardBouncerShowing_shouldStopOneHanded() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
@@ -141,6 +182,10 @@ public class OneHandedUITest extends OneHandedTestCase {
@Test
public void testScreenTurningOff_shouldStopOneHanded() {
+ // Bypass test if device not support one-handed mode
+ if (!mIsSupportOneHandedMode) {
+ return;
+ }
mScreenLifecycle.dispatchScreenTurningOff();
verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
index b7a2633d0d36..c9c111198f4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -61,8 +61,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mPipAnimationController = new PipAnimationController(
- mContext, new PipSurfaceTransactionHelper(mContext,
- mock(ConfigurationController.class)));
+ new PipSurfaceTransactionHelper(mContext, mock(ConfigurationController.class)));
mLeash = new SurfaceControl.Builder()
.setContainerLayer()
.setName("FakeLeash")
@@ -82,7 +81,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
@Test
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, new Rect(), new Rect());
+ .getAnimator(mLeash, new Rect(), new Rect(), null);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -94,12 +93,12 @@ public class PipAnimationControllerTest extends SysuiTestCase {
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue1);
+ .getAnimator(mLeash, startValue, endValue1, null);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue2);
+ .getAnimator(mLeash, startValue, endValue2, null);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -129,7 +128,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue1);
+ .getAnimator(mLeash, startValue, endValue1, null);
animator.updateEndValue(endValue2);
@@ -141,7 +140,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue);
+ .getAnimator(mLeash, startValue, endValue, null);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index f404f0489e01..e9d2b73182e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -33,7 +33,7 @@ import android.view.Gravity;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
@@ -270,6 +270,21 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
}
@Test
+ public void onSaveReentryBounds_restoreLastSize() {
+ final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+ oldSize.scale(1.25f);
+ mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize);
+
+ final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+ assertEquals(oldSize.width(), newSize.width());
+ assertEquals(oldSize.height(), newSize.height());
+ }
+
+ @Test
public void onResetReentryBounds_useDefaultBounds() {
final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
@@ -299,6 +314,22 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
assertBoundsInclusionWithMargin("restoreLastPosition", newBounds, actualBounds);
}
+ @Test
+ public void onSaveReentryBounds_componentMismatch_restoreLastSize() {
+ final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+ oldSize.scale(1.25f);
+ mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize);
+
+ mPipBoundsHandler.onResetReentryBounds(mTestComponentName2);
+ final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+ assertEquals(oldSize.width(), newSize.width());
+ assertEquals(oldSize.height(), newSize.height());
+ }
+
private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
final Rect expectedWithMargin = new Rect(expected);
expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
index 3155e57d8ab3..17b2e3225200 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.pip.phone;
+import static android.view.MotionEvent.ACTION_BUTTON_PRESS;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
@@ -49,13 +50,17 @@ public class PipTouchStateTest extends SysuiTestCase {
private PipTouchState mTouchState;
private CountDownLatch mDoubleTapCallbackTriggeredLatch;
+ private CountDownLatch mHoverExitCallbackTriggeredLatch;
@Before
public void setUp() throws Exception {
mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1);
+ mHoverExitCallbackTriggeredLatch = new CountDownLatch(1);
mTouchState = new PipTouchState(ViewConfiguration.get(getContext()),
Handler.createAsync(Looper.myLooper()), () -> {
mDoubleTapCallbackTriggeredLatch.countDown();
+ }, () -> {
+ mHoverExitCallbackTriggeredLatch.countDown();
});
assertFalse(mTouchState.isDoubleTap());
assertFalse(mTouchState.isWaitingForDoubleTap());
@@ -120,6 +125,35 @@ public class PipTouchStateTest extends SysuiTestCase {
assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1);
}
+ @Test
+ public void testHoverExitTimeout_timeoutCallbackCalled() throws Exception {
+ mTouchState.scheduleHoverExitTimeoutCallback();
+
+ // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
+ Thread.sleep(50);
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 0);
+ }
+
+ @Test
+ public void testHoverExitTimeout_timeoutCallbackNotCalled() throws Exception {
+ mTouchState.scheduleHoverExitTimeoutCallback();
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
+ }
+
+ @Test
+ public void testHoverExitTimeout_timeoutCallbackNotCalled_ifButtonPress() throws Exception {
+ mTouchState.scheduleHoverExitTimeoutCallback();
+ mTouchState.onTouchEvent(createMotionEvent(ACTION_BUTTON_PRESS, SystemClock.uptimeMillis(),
+ 0, 0));
+
+ // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
+ Thread.sleep(50);
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
+ }
+
private MotionEvent createMotionEvent(int action, long eventTime, float x, float y) {
return MotionEvent.obtain(0, eventTime, action, x, y, 0);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
new file mode 100644
index 000000000000..dcee5a716ceb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PrivacyChipBuilderTest : SysuiTestCase() {
+
+ companion object {
+ val TEST_UID = 1
+ }
+
+ @Test
+ fun testGenerateAppsList() {
+ val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+ "Bar", TEST_UID))
+ val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
+ "Bar", TEST_UID))
+ val foo0 = PrivacyItem(Privacy.TYPE_MICROPHONE, PrivacyApplication(
+ "Foo", TEST_UID))
+ val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+ "Baz", TEST_UID))
+
+ val items = listOf(bar2, foo0, baz1, bar3)
+
+ val textBuilder = PrivacyChipBuilder(context, items)
+
+ val list = textBuilder.appsAndTypes
+ assertEquals(3, list.size)
+ val appsList = list.map { it.first }
+ val typesList = list.map { it.second }
+ // List is sorted by number of types and then by types
+ assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName })
+ assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0])
+ assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
+ assertEquals(listOf(Privacy.TYPE_MICROPHONE), typesList[2])
+ }
+
+ @Test
+ fun testOrder() {
+ // We want location to always go last, so it will go in the "+ other apps"
+ val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication("Camera", TEST_UID))
+ val appMicrophone =
+ PrivacyItem(PrivacyType.TYPE_MICROPHONE,
+ PrivacyApplication("Microphone", TEST_UID))
+ val appLocation =
+ PrivacyItem(PrivacyType.TYPE_LOCATION,
+ PrivacyApplication("Location", TEST_UID))
+
+ val items = listOf(appLocation, appMicrophone, appCamera)
+ val textBuilder = PrivacyChipBuilder(context, items)
+ val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
+ assertEquals(listOf("Camera", "Microphone", "Location"), appList)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
new file mode 100644
index 000000000000..dddc35072315
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.DeviceConfig
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.hamcrest.Matchers.hasItem
+import org.hamcrest.Matchers.not
+import org.hamcrest.Matchers.nullValue
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThat
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyList
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class PrivacyItemControllerTest : SysuiTestCase() {
+
+ companion object {
+ val CURRENT_USER_ID = ActivityManager.getCurrentUser()
+ val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
+ const val SYSTEM_UID = 1000
+ const val TEST_PACKAGE_NAME = "test"
+ const val DEVICE_SERVICES_STRING = "Device services"
+ const val TAG = "PrivacyItemControllerTest"
+ fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+ fun <T> any(): T = Mockito.any<T>()
+ }
+
+ @Mock
+ private lateinit var appOpsController: AppOpsController
+ @Mock
+ private lateinit var callback: PrivacyItemController.Callback
+ @Mock
+ private lateinit var userManager: UserManager
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var dumpManager: DumpManager
+ @Captor
+ private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
+ @Captor
+ private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
+
+ private lateinit var privacyItemController: PrivacyItemController
+ private lateinit var executor: FakeExecutor
+ private lateinit var deviceConfigProxy: DeviceConfigProxy
+
+ fun PrivacyItemController(context: Context): PrivacyItemController {
+ return PrivacyItemController(
+ context,
+ appOpsController,
+ executor,
+ executor,
+ broadcastDispatcher,
+ deviceConfigProxy,
+ userManager,
+ dumpManager
+ )
+ }
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ executor = FakeExecutor(FakeSystemClock())
+ deviceConfigProxy = DeviceConfigProxyFake()
+
+ appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
+
+ deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+ "true", false)
+
+ doReturn(listOf(object : UserInfo() {
+ init {
+ id = CURRENT_USER_ID
+ }
+ })).`when`(userManager).getProfiles(anyInt())
+
+ privacyItemController = PrivacyItemController(mContext)
+ }
+
+ @Test
+ fun testSetListeningTrueByAddingCallback() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
+ any())
+ verify(callback).onPrivacyItemsChanged(anyList())
+ }
+
+ @Test
+ fun testSetListeningFalseByRemovingLastCallback() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController, never()).removeCallback(any(),
+ any())
+ privacyItemController.removeCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
+ any())
+ verify(callback).onPrivacyItemsChanged(emptyList())
+ }
+
+ @Test
+ fun testDistinctItems() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
+ AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+ assertEquals(1, argCaptor.value.size)
+ }
+
+ @Test
+ fun testRegisterReceiver_allUsers() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(broadcastDispatcher, atLeastOnce()).registerReceiver(
+ eq(privacyItemController.userSwitcherReceiver), any(), eq(null), eq(UserHandle.ALL))
+ verify(broadcastDispatcher, never())
+ .unregisterReceiver(eq(privacyItemController.userSwitcherReceiver))
+ }
+
+ @Test
+ fun testReceiver_ACTION_USER_FOREGROUND() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_USER_SWITCHED))
+ executor.runAllReady()
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE))
+ executor.runAllReady()
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE))
+ executor.runAllReady()
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testAddMultipleCallbacks() {
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(anyList())
+
+ privacyItemController.addCallback(otherCallback)
+ executor.runAllReady()
+ verify(otherCallback).onPrivacyItemsChanged(anyList())
+ // Adding a callback should not unnecessarily call previous ones
+ verifyNoMoreInteractions(callback)
+ }
+
+ @Test
+ fun testMultipleCallbacksAreUpdated() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ privacyItemController.addCallback(otherCallback)
+ executor.runAllReady()
+ reset(callback)
+ reset(otherCallback)
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(anyList())
+ verify(otherCallback).onPrivacyItemsChanged(anyList())
+ }
+
+ @Test
+ fun testRemoveCallback() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ privacyItemController.addCallback(otherCallback)
+ executor.runAllReady()
+ executor.runAllReady()
+ reset(callback)
+ reset(otherCallback)
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ privacyItemController.removeCallback(callback)
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ executor.runAllReady()
+ verify(callback, never()).onPrivacyItemsChanged(anyList())
+ verify(otherCallback).onPrivacyItemsChanged(anyList())
+ }
+
+ @Test
+ fun testListShouldNotHaveNull() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ executor.runAllReady()
+
+ verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+ assertEquals(1, argCaptor.value.size)
+ assertThat(argCaptor.value, not(hasItem(nullValue())))
+ }
+
+ @Test
+ fun testListShouldBeCopy() {
+ val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication("", TEST_UID)))
+ privacyItemController.privacyList = list
+ val privacyList = privacyItemController.privacyList
+ assertEquals(list, privacyList)
+ assertTrue(list !== privacyList)
+ }
+
+ @Test
+ fun testNotListeningWhenIndicatorsDisabled() {
+ deviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+ "false",
+ false
+ )
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
+ any())
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index cbb0711f78f8..cb3a04862eb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -51,6 +51,7 @@ import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.util.animation.DisappearParameters;
import com.android.systemui.util.animation.UniqueObjectHostView;
import org.junit.Before;
@@ -110,6 +111,7 @@ public class QSPanelTest extends SysuiTestCase {
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
when(mMediaHost.getHostView()).thenReturn(new UniqueObjectHostView(getContext()));
+ when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
mUiEventLogger = new UiEventLoggerFake();
mTestableLooper.runWithLooper(() -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index ea5449b4448e..417b19f0cfc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -367,13 +367,46 @@ public class QSSecurityFooterTest extends SysuiTestCase {
}
@Test
- public void testGetManagementMessage() {
- assertEquals(null, mFooter.getManagementMessage(false, MANAGING_ORGANIZATION));
+ public void testGetManagementMessage_noManagement() {
+ assertEquals(null, mFooter.getManagementMessage(
+ /* isDeviceManaged= */ false,
+ MANAGING_ORGANIZATION,
+ /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
+ MANAGING_ORGANIZATION));
+ }
+
+ @Test
+ public void testGetManagementMessage_deviceOwner() {
assertEquals(mContext.getString(R.string.monitoring_description_named_management,
MANAGING_ORGANIZATION),
- mFooter.getManagementMessage(true, MANAGING_ORGANIZATION));
+ mFooter.getManagementMessage(
+ /* isDeviceManaged= */ true,
+ MANAGING_ORGANIZATION,
+ /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
+ /* workProfileOrganizationName= */ null));
+ assertEquals(mContext.getString(R.string.monitoring_description_management),
+ mFooter.getManagementMessage(
+ /* isDeviceManaged= */ true,
+ /* organizationName= */ null,
+ /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
+ /* workProfileOrganizationName= */ null));
+ }
+
+ @Test
+ public void testGetManagementMessage_profileOwnerOfOrganizationOwnedDevice() {
+ assertEquals(mContext.getString(R.string.monitoring_description_named_management,
+ MANAGING_ORGANIZATION),
+ mFooter.getManagementMessage(
+ /* isDeviceManaged= */ false,
+ /* organizationName= */ null,
+ /* isProfileOwnerOfOrganizationOwnedDevice= */ true,
+ MANAGING_ORGANIZATION));
assertEquals(mContext.getString(R.string.monitoring_description_management),
- mFooter.getManagementMessage(true, null));
+ mFooter.getManagementMessage(
+ /* isDeviceManaged= */ false,
+ /* organizationName= */ null,
+ /* isProfileOwnerOfOrganizationOwnedDevice= */ true,
+ /* workProfileOrganizationName= */ null));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 5d4ef550b36c..bdb7166f5db1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -41,14 +41,17 @@ import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.CollectionUtils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -331,7 +334,15 @@ public class QSTileHostTest extends SysuiTestCase {
private class TestTile extends QSTileImpl<QSTile.State> {
protected TestTile(QSHost host) {
- super(host);
+ super(
+ host,
+ mLooper.getLooper(),
+ new Handler(mLooper.getLooper()),
+ mock(MetricsLogger.class),
+ mock(StatusBarStateController.class),
+ mock(ActivityStarter.class),
+ mQSLogger
+ );
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 953198c42d66..c2579dd46e78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -23,17 +23,23 @@ import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
+import android.os.Handler
import android.service.quicksettings.IQSTileService
import android.service.quicksettings.Tile
import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
import android.view.IWindowManager
-import androidx.test.runner.AndroidJUnit4
+import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
+import com.android.systemui.qs.logging.QSLogger
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,7 +52,8 @@ import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
class CustomTileTest : SysuiTestCase() {
companion object {
@@ -56,36 +63,53 @@ class CustomTileTest : SysuiTestCase() {
val TILE_SPEC = CustomTile.toSpec(componentName)
}
- @Mock private lateinit var mTileHost: QSHost
- @Mock private lateinit var mTileService: IQSTileService
- @Mock private lateinit var mTileServices: TileServices
- @Mock private lateinit var mTileServiceManager: TileServiceManager
- @Mock private lateinit var mWindowService: IWindowManager
- @Mock private lateinit var mPackageManager: PackageManager
- @Mock private lateinit var mApplicationInfo: ApplicationInfo
- @Mock private lateinit var mServiceInfo: ServiceInfo
+ @Mock private lateinit var tileHost: QSHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var tileService: IQSTileService
+ @Mock private lateinit var tileServices: TileServices
+ @Mock private lateinit var tileServiceManager: TileServiceManager
+ @Mock private lateinit var windowService: IWindowManager
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var applicationInfo: ApplicationInfo
+ @Mock private lateinit var serviceInfo: ServiceInfo
private lateinit var customTile: CustomTile
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var customTileBuilder: CustomTile.Builder
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
- mContext.addMockSystemService("window", mWindowService)
- mContext.setMockPackageManager(mPackageManager)
- `when`(mTileHost.tileServices).thenReturn(mTileServices)
- `when`(mTileHost.context).thenReturn(mContext)
- `when`(mTileServices.getTileWrapper(any(CustomTile::class.java)))
- .thenReturn(mTileServiceManager)
- `when`(mTileServiceManager.tileService).thenReturn(mTileService)
- `when`(mPackageManager.getApplicationInfo(anyString(), anyInt()))
- .thenReturn(mApplicationInfo)
-
- `when`(mPackageManager.getServiceInfo(any(ComponentName::class.java), anyInt()))
- .thenReturn(mServiceInfo)
- mServiceInfo.applicationInfo = mApplicationInfo
-
- customTile = CustomTile.create(mTileHost, TILE_SPEC, mContext)
+ testableLooper = TestableLooper.get(this)
+
+ mContext.addMockSystemService("window", windowService)
+ mContext.setMockPackageManager(packageManager)
+ `when`(tileHost.tileServices).thenReturn(tileServices)
+ `when`(tileHost.context).thenReturn(mContext)
+ `when`(tileServices.getTileWrapper(any(CustomTile::class.java)))
+ .thenReturn(tileServiceManager)
+ `when`(tileServiceManager.tileService).thenReturn(tileService)
+ `when`(packageManager.getApplicationInfo(anyString(), anyInt()))
+ .thenReturn(applicationInfo)
+
+ `when`(packageManager.getServiceInfo(any(ComponentName::class.java), anyInt()))
+ .thenReturn(serviceInfo)
+ serviceInfo.applicationInfo = applicationInfo
+
+ customTileBuilder = CustomTile.Builder(
+ { tileHost },
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+ )
+
+ customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
}
@Test
@@ -93,18 +117,18 @@ class CustomTileTest : SysuiTestCase() {
assertEquals(0, customTile.user)
val userContext = mock(Context::class.java)
- `when`(userContext.packageManager).thenReturn(mPackageManager)
+ `when`(userContext.packageManager).thenReturn(packageManager)
`when`(userContext.userId).thenReturn(10)
- val tile = CustomTile.create(mTileHost, TILE_SPEC, userContext)
+ val tile = CustomTile.create(customTileBuilder, TILE_SPEC, userContext)
assertEquals(10, tile.user)
}
@Test
fun testToggleableTileHasBooleanState() {
- `when`(mTileServiceManager.isToggleableTile).thenReturn(true)
- customTile = CustomTile.create(mTileHost, TILE_SPEC, mContext)
+ `when`(tileServiceManager.isToggleableTile).thenReturn(true)
+ customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
assertTrue(customTile.state is QSTile.BooleanState)
assertTrue(customTile.newTileState() is QSTile.BooleanState)
@@ -118,8 +142,8 @@ class CustomTileTest : SysuiTestCase() {
@Test
fun testValueUpdatedInBooleanTile() {
- `when`(mTileServiceManager.isToggleableTile).thenReturn(true)
- customTile = CustomTile.create(mTileHost, TILE_SPEC, mContext)
+ `when`(tileServiceManager.isToggleableTile).thenReturn(true)
+ customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
customTile.qsTile.icon = mock(Icon::class.java)
`when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
.thenReturn(mock(Drawable::class.java))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 438de99015a4..103e5586f395 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -35,17 +35,14 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static java.lang.Thread.sleep;
-
import android.content.Intent;
import android.metrics.LogMaker;
+import android.os.Handler;
+import android.os.Looper;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -57,7 +54,6 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
@@ -69,7 +65,6 @@ import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -91,9 +86,14 @@ public class QSTileImplTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private TileImpl mTile;
+ @Mock
private QSTileHost mHost;
+ @Mock
private MetricsLogger mMetricsLogger;
+ @Mock
private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ActivityStarter mActivityStarter;
private UiEventLoggerFake mUiEventLoggerFake;
private InstanceId mInstanceId = InstanceId.fakeInstanceId(5);
@@ -104,21 +104,16 @@ public class QSTileImplTest extends SysuiTestCase {
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
- mDependency.injectMockDependency(ActivityStarter.class);
- mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mUiEventLoggerFake = new UiEventLoggerFake();
- mStatusBarStateController =
- mDependency.injectMockDependency(StatusBarStateController.class);
- mHost = mock(QSTileHost.class);
when(mHost.indexOf(SPEC)).thenReturn(POSITION);
when(mHost.getContext()).thenReturn(mContext.getBaseContext());
- when(mHost.getQSLogger()).thenReturn(mQsLogger);
when(mHost.getUiEventLogger()).thenReturn(mUiEventLoggerFake);
when(mHost.getNewInstanceId()).thenReturn(mInstanceId);
- mTile = spy(new TileImpl(mHost));
- mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
+ Handler mainHandler = new Handler(mTestableLooper.getLooper());
+
+ mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler,
+ mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger);
mTile.setTileSpec(SPEC);
}
@@ -223,40 +218,33 @@ public class QSTileImplTest extends SysuiTestCase {
verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
}
- @Test
- @Ignore("flaky")
- public void testStaleTimeout() throws InterruptedException {
- when(mTile.getStaleTimeout()).thenReturn(5l);
- clearInvocations(mTile);
-
- mTile.handleRefreshState(null);
- mTestableLooper.processAllMessages();
- verify(mTile, never()).handleStale();
-
- sleep(10);
- mTestableLooper.processAllMessages();
- verify(mTile).handleStale();
- }
+ //TODO(b/161799397) Bring back testStaleTimeout when we can use FakeExecutor
@Test
public void testStaleListening() {
mTile.handleStale();
mTestableLooper.processAllMessages();
- verify(mTile).handleSetListening(eq(true));
+ verify(mQsLogger).logTileChangeListening(SPEC, true);
mTile.handleRefreshState(null);
mTestableLooper.processAllMessages();
- verify(mTile).handleSetListening(eq(false));
+ verify(mQsLogger).logTileChangeListening(SPEC, false);
}
@Test
public void testHandleDestroyClearsHandlerQueue() {
- when(mTile.getStaleTimeout()).thenReturn(0L);
mTile.handleRefreshState(null); // this will add a delayed H.STALE message
mTile.handleDestroy();
- mTestableLooper.processAllMessages();
- verify(mTile, never()).handleStale();
+ assertFalse(mTile.mHandler.hasMessages(QSTileImpl.H.STALE));
+ }
+
+ @Test
+ public void testHandleDestroyLifecycle() {
+ assertNotEquals(DESTROYED, mTile.getLifecycle().getCurrentState());
+ mTile.handleDestroy();
+
+ assertEquals(DESTROYED, mTile.getLifecycle().getCurrentState());
}
@Test
@@ -351,8 +339,17 @@ public class QSTileImplTest extends SysuiTestCase {
}
private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
- protected TileImpl(QSHost host) {
- super(host);
+ protected TileImpl(
+ QSHost host,
+ Looper backgroundLooper,
+ Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
getState().state = Tile.STATE_ACTIVE;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index 31992875df07..f70106a64968 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -17,13 +17,17 @@
package com.android.systemui.qs.tiles
import android.content.Context
+import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
-import com.android.systemui.Dependency
+import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.statusbar.policy.BatteryController
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -47,6 +51,14 @@ class BatterySaverTileTest : SysuiTestCase() {
@Mock
private lateinit var qsHost: QSHost
@Mock
+ private lateinit var metricsLogger: MetricsLogger
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var qsLogger: QSLogger
+ @Mock
private lateinit var batteryController: BatteryController
private lateinit var testableLooper: TestableLooper
private lateinit var tile: BatterySaverTile
@@ -55,11 +67,18 @@ class BatterySaverTileTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
`when`(qsHost.userContext).thenReturn(userContext)
`when`(userContext.userId).thenReturn(USER)
- tile = BatterySaverTile(qsHost, batteryController)
+ tile = BatterySaverTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ batteryController)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 853b2dbbc485..8ece62281f77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.when;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.media.projection.MediaProjectionInfo;
+import android.os.Handler;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -34,10 +35,12 @@ import android.testing.TestableLooper;
import androidx.lifecycle.LifecycleOwner;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Dependency;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,6 +74,12 @@ public class CastTileTest extends SysuiTestCase {
private QSTileHost mHost;
@Mock
NetworkController.SignalCallback mCallback;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private QSLogger mQSLogger;
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -80,16 +89,20 @@ public class CastTileTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
- mController = mDependency.injectMockDependency(CastController.class);
- mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
- mKeyguard = mDependency.injectMockDependency(KeyguardStateController.class);
- mNetworkController = mDependency.injectMockDependency(NetworkController.class);
-
when(mHost.getContext()).thenReturn(mContext);
- mCastTile = new CastTile(mHost, mController, mKeyguard, mNetworkController,
- mActivityStarter);
+ mCastTile = new CastTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mKeyguard,
+ mNetworkController
+ );
// We are not setting the mocks to listening, so we trigger a first refresh state to
// set the initial state
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index e5024595d97e..2d276bb876f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -23,19 +23,22 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Handler;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.Dependency;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import org.junit.Before;
import org.junit.Test;
@@ -44,18 +47,24 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper()
@SmallTest
public class ScreenRecordTileTest extends SysuiTestCase {
@Mock
private RecordingController mController;
@Mock
- private ActivityStarter mActivityStarter;
- @Mock
private QSTileHost mHost;
@Mock
- private UiEventLogger mUiEventLogger;
+ private KeyguardDismissUtil mKeyguardDismissUtil;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSLogger mQSLogger;
private TestableLooper mTestableLooper;
private ScreenRecordTile mTile;
@@ -65,13 +74,20 @@ public class ScreenRecordTileTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
- mController = mDependency.injectMockDependency(RecordingController.class);
- mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
when(mHost.getContext()).thenReturn(mContext);
- mTile = new ScreenRecordTile(mHost, mController, mActivityStarter, mUiEventLogger);
+ mTile = new ScreenRecordTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mKeyguardDismissUtil
+ );
}
// Test that the tile is inactive and labeled correctly when the controller is neither starting
@@ -89,6 +105,7 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mContext.getString(R.string.quick_settings_screen_record_start)));
mTile.handleClick();
+ mTestableLooper.processAllMessages();
verify(mController, times(1)).getPromptIntent();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
index 6d6a4d8f6b7d..f48b3fc51e82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
@@ -25,6 +25,8 @@ import android.view.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.util.UserIcons
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.statusbar.policy.UserSwitcherController
@@ -50,10 +52,10 @@ class UserDetailViewAdapterTest : SysuiTestCase() {
@Mock private lateinit var mOtherView: View
@Mock private lateinit var mInflatedUserDetailItemView: UserDetailItemView
@Mock private lateinit var mUserInfo: UserInfo
- @Mock private lateinit var mPicture: Bitmap
@Mock private lateinit var mLayoutInflater: LayoutInflater
private lateinit var adapter: UserDetailView.Adapter
private lateinit var uiEventLogger: UiEventLoggerFake
+ private lateinit var mPicture: Bitmap
@Before
fun setUp() {
@@ -64,6 +66,7 @@ class UserDetailViewAdapterTest : SysuiTestCase() {
`when`(mLayoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
.thenReturn(mInflatedUserDetailItemView)
adapter = UserDetailView.Adapter(mContext, mUserSwitcherController, uiEventLogger)
+ mPicture = UserIcons.convertToBitmap(mContext.getDrawable(R.drawable.ic_avatar_user))
}
private fun clickableTest(
@@ -141,4 +144,4 @@ class UserDetailViewAdapterTest : SysuiTestCase() {
false /* isAddUser */,
false /* isRestricted */,
true /* isSwitchToEnabled */)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index b877c7fa6859..11ef3e33f9d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -22,12 +22,14 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import android.app.PendingIntent;
+import android.content.Intent;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import org.junit.Before;
import org.junit.Test;
@@ -45,14 +47,18 @@ import org.mockito.MockitoAnnotations;
public class RecordingControllerTest extends SysuiTestCase {
@Mock
- RecordingController.RecordingStateChangeCallback mCallback;
+ private RecordingController.RecordingStateChangeCallback mCallback;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+
+ private RecordingController mController;
- RecordingController mController;
+ private static final int USER_ID = 10;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mController = new RecordingController(mContext);
+ mController = new RecordingController(mBroadcastDispatcher);
mController.addCallback(mCallback);
}
@@ -121,4 +127,27 @@ public class RecordingControllerTest extends SysuiTestCase {
assertFalse(mController.isRecording());
verify(mCallback).onRecordingEnd();
}
+
+ // Test that switching users will stop an ongoing recording
+ @Test
+ public void testUserChange() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ // If we are recording
+ PendingIntent startIntent = Mockito.mock(PendingIntent.class);
+ PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
+ mController.startCountdown(0, 0, startIntent, stopIntent);
+ mController.updateState(true);
+
+ // and user is changed
+ Intent intent = new Intent(Intent.ACTION_USER_SWITCHED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, USER_ID);
+ mController.mUserChangeReceiver.onReceive(mContext, intent);
+
+ // Ensure that the recording was stopped
+ verify(mCallback).onRecordingEnd();
+ assertFalse(mController.isRecording());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 283a47ca3622..4c9e141c45cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -32,6 +32,9 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import org.junit.Before;
import org.junit.Test;
@@ -58,6 +61,14 @@ public class RecordingServiceTest extends SysuiTestCase {
private Notification mNotification;
@Mock
private Executor mExecutor;
+ @Mock
+ private CurrentUserContextTracker mUserContextTracker;
+ private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() {
+ public void executeWhenUnlocked(ActivityStarter.OnDismissAction action,
+ boolean requiresShadeOpen) {
+ action.onDismiss();
+ }
+ };
private RecordingService mRecordingService;
@@ -65,7 +76,7 @@ public class RecordingServiceTest extends SysuiTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mUiEventLogger,
- mNotificationManager));
+ mNotificationManager, mUserContextTracker, mKeyguardDismissUtil));
// Return actual context info
doReturn(mContext).when(mRecordingService).getApplicationContext();
@@ -80,6 +91,8 @@ public class RecordingServiceTest extends SysuiTestCase {
doNothing().when(mRecordingService).startForeground(anyInt(), any());
doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder();
+
+ doReturn(mContext).when(mUserContextTracker).getCurrentUserContext();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
new file mode 100644
index 000000000000..4aaafbdaec1d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class ActionProxyReceiverTest extends SysuiTestCase {
+
+ @Mock
+ private StatusBar mMockStatusBar;
+ @Mock
+ private ActivityManagerWrapper mMockActivityManagerWrapper;
+ @Mock
+ private Future mMockFuture;
+ @Mock
+ private ScreenshotSmartActions mMockScreenshotSmartActions;
+ @Mock
+ private PendingIntent mMockPendingIntent;
+
+ private Intent mIntent;
+
+ @Before
+ public void setup() throws InterruptedException, ExecutionException, TimeoutException {
+ MockitoAnnotations.initMocks(this);
+ mIntent = new Intent(mContext, ActionProxyReceiver.class)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+
+ when(mMockActivityManagerWrapper.closeSystemWindows(anyString())).thenReturn(mMockFuture);
+ when(mMockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(null);
+ }
+
+ @Test
+ public void testPendingIntentSentWithoutStatusBar() throws PendingIntent.CanceledException {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(false);
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
+ verify(mMockStatusBar, never()).executeRunnableDismissingKeyguard(
+ any(Runnable.class), any(Runnable.class), anyBoolean(), anyBoolean(), anyBoolean());
+ verify(mMockPendingIntent).send(
+ eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+ }
+
+ @Test
+ public void testPendingIntentSentWithStatusBar() throws PendingIntent.CanceledException {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+ // ensure that the pending intent call is passed through
+ doAnswer((Answer<Object>) invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mMockStatusBar).executeRunnableDismissingKeyguard(
+ any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
+ verify(mMockStatusBar).executeRunnableDismissingKeyguard(
+ any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
+ verify(mMockPendingIntent).send(
+ eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+ }
+
+ @Test
+ public void testSmartActionsNotNotifiedByDefault() {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockScreenshotSmartActions, never())
+ .notifyScreenshotAction(any(Context.class), anyString(), anyString(), anyBoolean());
+ }
+
+ @Test
+ public void testSmartActionsNotifiedIfEnabled() {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+ mIntent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
+ String testId = "testID";
+ mIntent.putExtra(EXTRA_ID, testId);
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+ mContext, testId, ACTION_TYPE_SHARE, false);
+ }
+
+ private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) {
+ if (withStatusBar) {
+ return new ActionProxyReceiver(
+ Optional.of(mMockStatusBar), mMockActivityManagerWrapper,
+ mMockScreenshotSmartActions);
+ } else {
+ return new ActionProxyReceiver(
+ Optional.empty(), mMockActivityManagerWrapper, mMockScreenshotSmartActions);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
new file mode 100644
index 000000000000..b9249131c191
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class DeleteScreenshotReceiverTest extends SysuiTestCase {
+
+ @Mock
+ private ScreenshotSmartActions mMockScreenshotSmartActions;
+ @Mock
+ private Executor mMockExecutor;
+
+ private DeleteScreenshotReceiver mDeleteScreenshotReceiver;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mDeleteScreenshotReceiver =
+ new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mMockExecutor);
+ }
+
+ @Test
+ public void testNoUriProvided() {
+ Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
+
+ mDeleteScreenshotReceiver.onReceive(mContext, intent);
+
+ verify(mMockExecutor, never()).execute(any(Runnable.class));
+ verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
+ any(Context.class), any(String.class), any(String.class), anyBoolean());
+ }
+
+ @Test
+ public void testFileDeleted() {
+ DeleteScreenshotReceiver deleteScreenshotReceiver =
+ new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mFakeExecutor);
+ ContentResolver contentResolver = mContext.getContentResolver();
+ final Uri testUri = contentResolver.insert(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, getFakeContentValues());
+ assertNotNull(testUri);
+
+ try {
+ Cursor cursor =
+ contentResolver.query(testUri, null, null, null, null);
+ assertEquals(1, cursor.getCount());
+ Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class)
+ .putExtra(SCREENSHOT_URI_ID, testUri.toString());
+
+ deleteScreenshotReceiver.onReceive(mContext, intent);
+ int runCount = mFakeExecutor.runAllReady();
+
+ assertEquals(1, runCount);
+ cursor =
+ contentResolver.query(testUri, null, null, null, null);
+ assertEquals(0, cursor.getCount());
+ } finally {
+ contentResolver.delete(testUri, null, null);
+ }
+
+ // ensure smart actions not called by default
+ verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
+ any(Context.class), any(String.class), any(String.class), anyBoolean());
+ }
+
+ @Test
+ public void testNotifyScreenshotAction() {
+ Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
+ String uriString = "testUri";
+ String testId = "testID";
+ intent.putExtra(SCREENSHOT_URI_ID, uriString);
+ intent.putExtra(EXTRA_ID, testId);
+ intent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
+
+ mDeleteScreenshotReceiver.onReceive(mContext, intent);
+
+ verify(mMockExecutor).execute(any(Runnable.class));
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+ mContext, testId, ACTION_TYPE_DELETE, false);
+ }
+
+ private static ContentValues getFakeContentValues() {
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
+ + File.separator + Environment.DIRECTORY_SCREENSHOTS);
+ values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test_screenshot");
+ values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
+ values.put(MediaStore.MediaColumns.DATE_ADDED, 0);
+ values.put(MediaStore.MediaColumns.DATE_MODIFIED, 0);
+ return values;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index d3b33992d017..184329ec6e5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -61,12 +61,14 @@ import java.util.concurrent.TimeUnit;
*/
public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
private ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
+ private ScreenshotSmartActions mScreenshotSmartActions;
private Handler mHandler;
@Before
public void setup() {
mSmartActionsProvider = mock(
ScreenshotNotificationSmartActionsProvider.class);
+ mScreenshotSmartActions = new ScreenshotSmartActions();
mHandler = mock(Handler.class);
}
@@ -82,7 +84,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
when(smartActionsProvider.getActions(any(), any(), any(), any(), any()))
.thenThrow(RuntimeException.class);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://authority/data"), bitmap, smartActionsProvider,
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
assertNotNull(smartActionsFuture);
@@ -100,7 +102,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
int timeoutMs = 1000;
when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
RuntimeException.class);
- List<Notification.Action> actions = ScreenshotSmartActions.getSmartActions(
+ List<Notification.Action> actions = mScreenshotSmartActions.getSmartActions(
"", smartActionsFuture, timeoutMs, mSmartActionsProvider);
assertEquals(Collections.emptyList(), actions);
}
@@ -111,7 +113,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
throws Exception {
doThrow(RuntimeException.class).when(mSmartActionsProvider).notifyOp(any(), any(), any(),
anyLong());
- ScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
+ mScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
}
// Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked
@@ -122,7 +124,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
Bitmap bitmap = mock(Bitmap.class);
when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any());
@@ -136,7 +138,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
public void testScreenshotNotificationSmartActionsProviderInvokedOnce() {
Bitmap bitmap = mock(Bitmap.class);
when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true,
UserHandle.getUserHandleForUid(UserHandle.myUserId()));
verify(mSmartActionsProvider, times(1)).getActions(any(), any(), any(), any(), any());
@@ -152,7 +154,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
mContext, null, mHandler);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
+ mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
actionsProvider,
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
assertNotNull(smartActionsFuture);
@@ -172,7 +174,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+ SaveImageInBackgroundTask task =
+ new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
Uri.parse("Screenshot_123.png"));
@@ -198,7 +201,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+ SaveImageInBackgroundTask task =
+ new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
Uri.parse("Screenshot_123.png"));
@@ -224,7 +228,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+ SaveImageInBackgroundTask task =
+ new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
Notification.Action deleteAction = task.createDeleteAction(mContext,
mContext.getResources(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
new file mode 100644
index 000000000000..ce6f0736ec33
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.verify;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SmartActionsReceiverTest extends SysuiTestCase {
+
+ @Mock
+ private ScreenshotSmartActions mMockScreenshotSmartActions;
+ @Mock
+ private PendingIntent mMockPendingIntent;
+
+ private SmartActionsReceiver mSmartActionsReceiver;
+ private Intent mIntent;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mSmartActionsReceiver = new SmartActionsReceiver(mMockScreenshotSmartActions);
+ mIntent = new Intent(mContext, SmartActionsReceiver.class)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+ }
+
+ @Test
+ public void testSmartActionIntent() throws PendingIntent.CanceledException {
+ String testId = "testID";
+ String testActionType = "testActionType";
+ mIntent.putExtra(EXTRA_ID, testId);
+ mIntent.putExtra(EXTRA_ACTION_TYPE, testActionType);
+
+ mSmartActionsReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockPendingIntent).send(
+ eq(mContext), eq(0), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+ mContext, testId, testActionType, true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index e889dad0f610..56df1939be56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -31,13 +31,13 @@ import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationShadeWindowController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.eq
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
@@ -68,6 +68,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Mock private lateinit var shadeAnimation: NotificationShadeDepthController.DepthAnimation
@Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation
@Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation
+ @Mock private lateinit var listener: NotificationShadeDepthController.DepthListener
@Mock private lateinit var dozeParameters: DozeParameters
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
@@ -103,7 +104,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Test
fun setupListeners() {
- verify(dumpManager).registerDumpable(anyString(), safeEq(notificationShadeDepthController))
+ verify(dumpManager).registerDumpable(anyString(), eq(notificationShadeDepthController))
}
@Test
@@ -171,7 +172,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Test
fun updateGlobalDialogVisibility_animatesBlur() {
notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
- verify(globalActionsSpring).animateTo(eq(maxBlur / 2), safeEq(root))
+ verify(globalActionsSpring).animateTo(eq(maxBlur / 2), eq(root))
}
@Test
@@ -191,8 +192,10 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Test
fun updateBlurCallback_setsBlurAndZoom() {
+ notificationShadeDepthController.addListener(listener)
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(wallpaperManager).setWallpaperZoomOut(any(), anyFloat())
+ verify(listener).onWallpaperZoomOutChanged(anyFloat())
verify(blurUtils).applyBlur(any(), anyInt())
}
@@ -245,7 +248,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(notificationShadeWindowController).setBackgroundBlurRadius(0)
- verify(blurUtils).applyBlur(safeEq(viewRootImpl), eq(0))
+ verify(blurUtils).applyBlur(eq(viewRootImpl), eq(0))
}
@Test
@@ -267,8 +270,4 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
verify(shadeSpring, never()).animateTo(anyInt(), any())
verify(shadeAnimation, never()).animateTo(anyInt(), any())
}
-
- private fun <T : Any> safeEq(value: T): T {
- return eq(value) ?: value
- }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
index e6287e7063d3..7eeae67c9fdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
@@ -38,8 +38,8 @@ import android.widget.RemoteViews;
import androidx.palette.graphics.Palette;
import androidx.test.runner.AndroidJUnit4;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.tests.R;
import org.junit.After;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index aefea57155bc..8a49326add25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -61,6 +61,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -200,7 +201,9 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
() -> mNotificationRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class)
+ mock(ForegroundServiceDismissalFeatureController.class),
+ mock(HeadsUpManager.class),
+ mock(StatusBarStateController.class)
);
mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 363fe95aae18..359faba48f08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -1273,8 +1273,8 @@ public class NotifCollectionTest extends SysuiTestCase {
verify(mInterceptor3, never()).shouldInterceptDismissal(clearable);
}
- @Test(expected = IllegalStateException.class)
- public void testClearNotificationThrowsIfMissing() {
+ @Test
+ public void testClearNotificationDoesntThrowIfMissing() {
// GIVEN that enough time has passed that we're beyond the forgiveness window
mClock.advanceTime(5001);
@@ -1287,7 +1287,8 @@ public class NotifCollectionTest extends SysuiTestCase {
container.getSbn(),
new RankingMap(new Ranking[]{ container.getRanking() }));
- // THEN an exception is thrown
+ // THEN the event is ignored
+ verify(mCollectionListener, never()).onEntryRemoved(any(NotificationEntry.class), anyInt());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index 314b19140e7a..960ea79f36b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -16,6 +16,11 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -43,6 +48,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -68,13 +74,13 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
@Mock private AppOpsController mAppOpsController;
@Mock private NotifPipeline mNotifPipeline;
- private NotificationEntry mEntry;
- private Notification mNotification;
+ private NotificationEntryBuilder mEntryBuilder;
private AppOpsCoordinator mAppOpsCoordinator;
private NotifFilter mForegroundFilter;
private NotifCollectionListener mNotifCollectionListener;
private AppOpsController.Callback mAppOpsCallback;
private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
+ private NotifSection mFgsSection;
private FakeSystemClock mClock = new FakeSystemClock();
private FakeExecutor mExecutor = new FakeExecutor(mClock);
@@ -90,11 +96,8 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
mAppOpsController,
mExecutor);
- mNotification = new Notification();
- mEntry = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .setNotification(mNotification)
- .build();
+ mEntryBuilder = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID));
mAppOpsCoordinator.attach(mNotifPipeline);
@@ -122,11 +125,14 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
ArgumentCaptor.forClass(AppOpsController.Callback.class);
verify(mAppOpsController).addCallback(any(int[].class), appOpsCaptor.capture());
mAppOpsCallback = appOpsCaptor.getValue();
+
+ mFgsSection = mAppOpsCoordinator.getSection();
}
@Test
public void filterTest_disclosureUnnecessary() {
- StatusBarNotification sbn = mEntry.getSbn();
+ NotificationEntry entry = mEntryBuilder.build();
+ StatusBarNotification sbn = entry.getSbn();
// GIVEN the notification is a disclosure notification
when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(true);
@@ -136,84 +142,91 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
.thenReturn(false);
// THEN filter out the notification
- assertTrue(mForegroundFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mForegroundFilter.shouldFilterOut(entry, 0));
}
@Test
public void filterTest_systemAlertNotificationUnnecessary() {
- StatusBarNotification sbn = mEntry.getSbn();
-
- // GIVEN the notification is a system alert notification + not a disclosure notification
- when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(true);
- when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false);
-
// GIVEN the alert notification isn't needed for this user
final Bundle extras = new Bundle();
extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS,
new String[]{TEST_PKG});
- mNotification.extras = extras;
+ mEntryBuilder.modifyNotification(mContext)
+ .setExtras(extras);
+ NotificationEntry entry = mEntryBuilder.build();
+ StatusBarNotification sbn = entry.getSbn();
when(mForegroundServiceController.isSystemAlertWarningNeeded(sbn.getUserId(), TEST_PKG))
.thenReturn(false);
+ // GIVEN the notification is a system alert notification + not a disclosure notification
+ when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(true);
+ when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false);
+
+
// THEN filter out the notification
- assertTrue(mForegroundFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mForegroundFilter.shouldFilterOut(entry, 0));
}
@Test
public void filterTest_doNotFilter() {
- StatusBarNotification sbn = mEntry.getSbn();
+ NotificationEntry entry = mEntryBuilder.build();
+ StatusBarNotification sbn = entry.getSbn();
// GIVEN the notification isn't a system alert notification nor a disclosure notification
when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(false);
when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false);
// THEN don't filter out the notification
- assertFalse(mForegroundFilter.shouldFilterOut(mEntry, 0));
+ assertFalse(mForegroundFilter.shouldFilterOut(entry, 0));
}
@Test
public void extendLifetimeText_notForeground() {
// GIVEN the notification doesn't represent a foreground service
- mNotification.flags = 0;
+ mEntryBuilder.modifyNotification(mContext)
+ .setFlag(FLAG_FOREGROUND_SERVICE, false);
// THEN don't extend the lifetime
assertFalse(mForegroundNotifLifetimeExtender
- .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
+ .shouldExtendLifetime(mEntryBuilder.build(),
+ NotificationListenerService.REASON_CLICK));
}
@Test
public void extendLifetimeText_foregroundNotifRecentlyPosted() {
// GIVEN the notification represents a foreground service that was just posted
- mNotification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- mEntry = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
+ Notification notification = new Notification.Builder(mContext, "test_channel")
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ NotificationEntry entry = mEntryBuilder
.setSbn(new StatusBarNotification(TEST_PKG, TEST_PKG, NOTIF_USER_ID, "",
- NOTIF_USER_ID, NOTIF_USER_ID, mNotification,
+ NOTIF_USER_ID, NOTIF_USER_ID, notification,
new UserHandle(NOTIF_USER_ID), "", System.currentTimeMillis()))
- .setNotification(mNotification)
+ .setNotification(notification)
.build();
// THEN extend the lifetime
assertTrue(mForegroundNotifLifetimeExtender
- .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
+ .shouldExtendLifetime(entry, NotificationListenerService.REASON_CLICK));
}
@Test
public void extendLifetimeText_foregroundNotifOld() {
// GIVEN the notification represents a foreground service that was posted 10 seconds ago
- mNotification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- mEntry = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
+ Notification notification = new Notification.Builder(mContext, "test_channel")
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ NotificationEntry entry = mEntryBuilder
.setSbn(new StatusBarNotification(TEST_PKG, TEST_PKG, NOTIF_USER_ID, "",
- NOTIF_USER_ID, NOTIF_USER_ID, mNotification,
+ NOTIF_USER_ID, NOTIF_USER_ID, notification,
new UserHandle(NOTIF_USER_ID), "",
System.currentTimeMillis() - 10000))
- .setNotification(mNotification)
+ .setNotification(notification)
.build();
// THEN don't extend the lifetime because the extended time exceeds MIN_FGS_TIME_MS
assertFalse(mForegroundNotifLifetimeExtender
- .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
+ .shouldExtendLifetime(entry, NotificationListenerService.REASON_CLICK));
}
@Test
@@ -345,4 +358,41 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
// THEN the entry's active app ops is updated to empty
assertTrue(entry.mActiveAppOps.isEmpty());
}
+
+ @Test
+ public void testIncludeFGSInSection_importanceDefault() {
+ // GIVEN the notification represents a colorized foreground service with > min importance
+ mEntryBuilder
+ .setFlag(mContext, FLAG_FOREGROUND_SERVICE, true)
+ .setImportance(IMPORTANCE_DEFAULT)
+ .modifyNotification(mContext).setColorized(true);
+
+ // THEN the entry is in the fgs section
+ assertTrue(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
+
+ @Test
+ public void testDiscludeFGSInSection_importanceMin() {
+ // GIVEN the notification represents a colorized foreground service with min importance
+ mEntryBuilder
+ .setFlag(mContext, FLAG_FOREGROUND_SERVICE, true)
+ .setImportance(IMPORTANCE_MIN)
+ .modifyNotification(mContext).setColorized(true);
+
+ // THEN the entry is NOT in the fgs section
+ assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
+
+ @Test
+ public void testDiscludeNonFGSInSection() {
+ // GIVEN the notification represents a colorized notification with high importance that
+ // is NOT a foreground service
+ mEntryBuilder
+ .setImportance(IMPORTANCE_HIGH)
+ .setFlag(mContext, FLAG_FOREGROUND_SERVICE, false)
+ .modifyNotification(mContext).setColorized(false);
+
+ // THEN the entry is NOT in the fgs section
+ assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index dfc627e14d8c..be5c8a846afb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -25,6 +25,9 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -40,38 +43,52 @@ import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class ConversationCoordinatorTest : SysuiTestCase() {
-
- private var coordinator: ConversationCoordinator = ConversationCoordinator()
-
// captured listeners and pluggables:
- private var promoter: NotifPromoter? = null
+ private lateinit var promoter: NotifPromoter
+ private lateinit var peopleSection: NotifSection
@Mock
- private val pipeline: NotifPipeline? = null
+ private lateinit var pipeline: NotifPipeline
+ @Mock
+ private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
@Mock
- private val channel: NotificationChannel? = null
- private var entry: NotificationEntry? = null
+ private lateinit var channel: NotificationChannel
+ private lateinit var entry: NotificationEntry
+
+ private lateinit var coordinator: ConversationCoordinator
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(channel!!.isImportantConversation).thenReturn(true)
+ coordinator = ConversationCoordinator(peopleNotificationIdentifier)
+ whenever(channel.isImportantConversation).thenReturn(true)
- coordinator.attach(pipeline!!)
+ coordinator.attach(pipeline)
// capture arguments:
val notifPromoterCaptor = ArgumentCaptor.forClass(NotifPromoter::class.java)
verify(pipeline).addPromoter(notifPromoterCaptor.capture())
promoter = notifPromoterCaptor.value
+ peopleSection = coordinator.getSection()
+
entry = NotificationEntryBuilder().setChannel(channel).build()
}
@Test
- fun testPromotesCurrentHUN() {
-
+ fun testPromotesImportantConversations() {
// only promote important conversations
- assertTrue(promoter!!.shouldPromoteToTopLevel(entry))
- assertFalse(promoter!!.shouldPromoteToTopLevel(NotificationEntryBuilder().build()))
+ assertTrue(promoter.shouldPromoteToTopLevel(entry))
+ assertFalse(promoter.shouldPromoteToTopLevel(NotificationEntryBuilder().build()))
+ }
+
+ @Test
+ fun testInPeopleSection() {
+ whenever(peopleNotificationIdentifier.getPeopleNotificationType(
+ entry.sbn, entry.ranking)).thenReturn(TYPE_PERSON)
+
+ // only put people notifications in this section
+ assertTrue(peopleSection.isInSection(entry))
+ assertFalse(peopleSection.isInSection(NotificationEntryBuilder().build()))
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 85acbe6d440b..5f10f38b2ee8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -36,6 +36,8 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import org.junit.Before;
import org.junit.Test;
@@ -50,6 +52,7 @@ import org.mockito.MockitoAnnotations;
public class RankingCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
+ @Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private NotifPipeline mNotifPipeline;
@Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
@@ -58,16 +61,23 @@ public class RankingCoordinatorTest extends SysuiTestCase {
private NotifFilter mCapturedSuspendedFilter;
private NotifFilter mCapturedDozingFilter;
+ private NotifSection mAlertingSection;
+ private NotifSection mSilentSection;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- RankingCoordinator rankingCoordinator = new RankingCoordinator(mStatusBarStateController);
+ RankingCoordinator rankingCoordinator =
+ new RankingCoordinator(mStatusBarStateController, mHighPriorityProvider);
mEntry = new NotificationEntryBuilder().build();
rankingCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
+
+ mAlertingSection = rankingCoordinator.getAlertingSection();
+ mSilentSection = rankingCoordinator.getSilentSection();
}
@Test
@@ -130,6 +140,26 @@ public class RankingCoordinatorTest extends SysuiTestCase {
assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
}
+ @Test
+ public void testIncludeInSectionAlerting() {
+ // GIVEN the entry is high priority
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
+
+ // THEN entry is in the alerting section
+ assertTrue(mAlertingSection.isInSection(mEntry));
+ assertFalse(mSilentSection.isInSection(mEntry));
+ }
+
+ @Test
+ public void testIncludeInSectionSilent() {
+ // GIVEN the entry isn't high priority
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+
+ // THEN entry is in the silent section
+ assertFalse(mAlertingSection.isInSection(mEntry));
+ assertTrue(mSilentSection.isInSection(mEntry));
+ }
+
private RankingBuilder getRankingForUnfilteredNotif() {
return new RankingBuilder()
.setKey(mEntry.getKey())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 25da74137a90..a2f8c1cb0ad3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -50,7 +50,9 @@ import android.widget.TextView;
import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
@@ -59,7 +61,6 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
-import com.android.systemui.tests.R;
import org.junit.Assert;
import org.junit.Before;
@@ -110,6 +111,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
() -> smartReplyConstants,
() -> smartReplyController,
mConversationNotificationProcessor,
+ mock(MediaFeatureFlag.class),
mock(Executor.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 5640f08264a6..601df2cb4fc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
@@ -86,6 +87,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -120,8 +122,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
@Mock private NotificationGutsManager mGutsManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
- @Mock private ExpandableNotificationRowComponent.Builder
- mExpandableNotificationRowComponentBuilder;
+ @Mock(answer = Answers.RETURNS_SELF)
+ private ExpandableNotificationRowComponent.Builder mExpandableNotificationRowComponentBuilder;
@Mock private ExpandableNotificationRowComponent mExpandableNotificationRowComponent;
@Mock private FalsingManager mFalsingManager;
@Mock private KeyguardBypassController mKeyguardBypassController;
@@ -182,7 +184,9 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
() -> mRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class)
+ mock(ForegroundServiceDismissalFeatureController.class),
+ mock(HeadsUpManager.class),
+ mock(StatusBarStateController.class)
);
NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
@@ -197,6 +201,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
() -> mock(SmartReplyConstants.class),
() -> mock(SmartReplyController.class),
mock(ConversationNotificationProcessor.class),
+ mock(MediaFeatureFlag.class),
mBgExecutor);
mRowContentBindStage = new RowContentBindStage(
binder,
@@ -209,21 +214,9 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
when(mExpandableNotificationRowComponentBuilder
.expandableNotificationRow(viewCaptor.capture()))
.thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .notificationEntry(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .onDismissRunnable(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .rowContentBindStage(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .onExpandClickListener(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
-
when(mExpandableNotificationRowComponentBuilder.build())
.thenReturn(mExpandableNotificationRowComponent);
+
when(mExpandableNotificationRowComponent.getExpandableNotificationRowController())
.thenAnswer((Answer<ExpandableNotificationRowController>) invocation ->
new ExpandableNotificationRowController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 362914d6f848..8ccbb2ebb0db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -43,9 +43,11 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import com.android.systemui.R;
import com.android.systemui.TestableDependency;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubblesTestActivity;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -68,7 +70,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
-import com.android.systemui.tests.R;
import org.mockito.ArgumentCaptor;
@@ -133,6 +134,7 @@ public class NotificationTestHelper {
() -> mock(SmartReplyConstants.class),
() -> mock(SmartReplyController.class),
mock(ConversationNotificationProcessor.class),
+ mock(MediaFeatureFlag.class),
mock(Executor.class));
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index 45f7c5a6fdc0..a147c8d0f121 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -24,10 +24,10 @@ import android.widget.RemoteViews;
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.tests.R;
import org.junit.Assert;
import org.junit.Before;
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 243503d1d8a6..7ca24789a29b 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
@@ -380,7 +380,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
setupMockStack(
PEOPLE_HEADER,
- ALERTING.headsUp(),
+ ALERTING,
PERSON,
ALERTING_HEADER,
GENTLE_HEADER,
@@ -403,9 +403,9 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
enablePeopleFiltering();
setupMockStack(
- PERSON.headsUp(),
+ PERSON,
INCOMING_HEADER,
- ALERTING.headsUp(),
+ ALERTING,
PEOPLE_HEADER,
PERSON
);
@@ -425,7 +425,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
enablePeopleFiltering();
setupMockStack(
- PERSON.headsUp(),
+ PERSON,
PEOPLE_HEADER,
PERSON
);
@@ -443,8 +443,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
enablePeopleFiltering();
setupMockStack(
- ALERTING.headsUp(),
- PERSON.headsUp()
+ ALERTING,
+ PERSON
);
mSectionsManager.updateSectionBoundaries();
verifyMockStack(
@@ -461,7 +461,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
setupMockStack(
INCOMING_HEADER,
- ALERTING.headsUp(),
+ ALERTING,
PEOPLE_HEADER,
FSN,
PERSON,
@@ -502,9 +502,9 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
public void testMediaControls_AddWhenEnterKeyguardWithHeadsUp() {
enableMediaControls();
- // GIVEN a stack that doesn't include media controls but includes HEADS_UP
+ // GIVEN a stack that doesn't include media
setupMockStack(
- ALERTING.headsUp(),
+ ALERTING,
ALERTING,
GENTLE_HEADER,
GENTLE);
@@ -584,6 +584,27 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
);
}
+ @Test
+ public void testIgnoreGoneView() {
+ enablePeopleFiltering();
+
+ setupMockStack(
+ PERSON.gone(),
+ ALERTING,
+ GENTLE
+ );
+
+ mSectionsManager.updateSectionBoundaries();
+
+ verifyMockStack(
+ ChildType.ALERTING_HEADER,
+ ChildType.PERSON,
+ ChildType.ALERTING,
+ ChildType.GENTLE_HEADER,
+ ChildType.GENTLE
+ );
+ }
+
private void enablePeopleFiltering() {
when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
}
@@ -619,16 +640,16 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
child = mSectionsManager.getSilentHeaderView();
break;
case FSN:
- child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
break;
case PERSON:
- child = mockNotification(BUCKET_PEOPLE, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
break;
case ALERTING:
- child = mockNotification(BUCKET_ALERTING, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
break;
case GENTLE:
- child = mockNotification(BUCKET_SILENT, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_SILENT, entry.mIsGone);
break;
case OTHER:
child = mock(View.class);
@@ -643,7 +664,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
}
}
- private View mockNotification(int bucket, boolean headsUp) {
+ private View mockNotification(int bucket, boolean isGone) {
ExpandableNotificationRow notifRow =
mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
@@ -659,8 +680,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
return null;
}).when(mockEntry).setBucket(anyInt());
- when(notifRow.isHeadsUp()).thenReturn(headsUp);
- when(mockEntry.isRowHeadsUp()).thenReturn(headsUp);
+ when(notifRow.getVisibility()).thenReturn(isGone ? View.GONE : View.VISIBLE);
return notifRow;
}
@@ -767,16 +787,16 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
child = mSectionsManager.getSilentHeaderView();
break;
case FSN:
- child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
break;
case PERSON:
- child = mockNotification(BUCKET_PEOPLE, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
break;
case ALERTING:
- child = mockNotification(BUCKET_ALERTING, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
break;
case GENTLE:
- child = mockNotification(BUCKET_SILENT, entry.mIsHeadsUp);
+ child = mockNotification(BUCKET_SILENT, entry.mIsGone);
break;
case OTHER:
child = mock(View.class);
@@ -796,36 +816,25 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
private static final StackEntry ALERTING_HEADER = new StackEntry(ChildType.ALERTING_HEADER);
private static final StackEntry GENTLE_HEADER = new StackEntry(ChildType.GENTLE_HEADER);
private static final StackEntry FSN = new StackEntry(ChildType.FSN);
- private static final StackEntry.Hunnable PERSON = new StackEntry.Hunnable(ChildType.PERSON);
- private static final StackEntry.Hunnable ALERTING = new StackEntry.Hunnable(ChildType.ALERTING);
+ private static final StackEntry PERSON = new StackEntry(ChildType.PERSON);
+ private static final StackEntry ALERTING = new StackEntry(ChildType.ALERTING);
private static final StackEntry GENTLE = new StackEntry(ChildType.GENTLE);
private static class StackEntry {
final ChildType mChildType;
- final boolean mIsHeadsUp;
+ final boolean mIsGone;
StackEntry(ChildType childType) {
this(childType, false);
}
- StackEntry(ChildType childType, boolean isHeadsUp) {
+ StackEntry(ChildType childType, boolean isGone) {
mChildType = childType;
- mIsHeadsUp = isHeadsUp;
+ mIsGone = isGone;
}
- static class Hunnable extends StackEntry {
-
- Hunnable(ChildType childType) {
- super(childType, false);
- }
-
- Hunnable(ChildType childType, boolean isHeadsUp) {
- super(childType, isHeadsUp);
- }
-
- public Hunnable headsUp() {
- return new Hunnable(mChildType, true);
- }
+ public StackEntry gone() {
+ return new StackEntry(mChildType, true);
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index b286f9486e13..6d411333b220 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -55,6 +55,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -92,6 +93,7 @@ import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.leak.LeakDetector;
@@ -190,7 +192,9 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
() -> mock(NotificationRowBinder.class),
() -> mRemoteInputManager,
mock(LeakDetector.class),
- mock(ForegroundServiceDismissalFeatureController.class)
+ mock(ForegroundServiceDismissalFeatureController.class),
+ mock(HeadsUpManager.class),
+ mock(StatusBarStateController.class)
);
mEntryManager.setUpWithPresenter(mock(NotificationPresenter.class));
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index debc840394b5..fa253e62ef0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -16,13 +16,18 @@
package com.android.systemui.statusbar.phone;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
+import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -30,6 +35,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
import org.junit.Assert;
@@ -50,6 +56,7 @@ public class DozeParametersTest extends SysuiTestCase {
@Mock private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
@Mock private PowerManager mPowerManager;
@Mock private TunerService mTunerService;
+ @Mock private BatteryController mBatteryController;
@Before
public void setup() {
@@ -59,11 +66,12 @@ public class DozeParametersTest extends SysuiTestCase {
mAmbientDisplayConfiguration,
mAlwaysOnDisplayPolicy,
mPowerManager,
+ mBatteryController,
mTunerService
);
}
@Test
- public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
+ public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
mDozeParameters.setControlScreenOffAnimation(true);
reset(mPowerManager);
mDozeParameters.setControlScreenOffAnimation(false);
@@ -71,7 +79,7 @@ public class DozeParametersTest extends SysuiTestCase {
}
@Test
- public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
+ public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
mDozeParameters.setControlScreenOffAnimation(false);
reset(mPowerManager);
mDozeParameters.setControlScreenOffAnimation(true);
@@ -79,11 +87,28 @@ public class DozeParametersTest extends SysuiTestCase {
}
@Test
- public void test_getWallpaperAodDuration_when_shouldControlScreenOff() {
+ public void testGetWallpaperAodDuration_when_shouldControlScreenOff() {
mDozeParameters.setControlScreenOffAnimation(true);
Assert.assertEquals(
"wallpaper hides faster when controlling screen off",
mDozeParameters.getWallpaperAodDuration(),
DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY);
}
+
+ @Test
+ public void testGetAlwaysOn() {
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+
+ assertThat(mDozeParameters.getAlwaysOn()).isTrue();
+ }
+
+ @Test
+ public void testGetAlwaysOn_whenBatterySaver() {
+ when(mBatteryController.isAodPowerSave()).thenReturn(true);
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+
+ assertThat(mDozeParameters.getAlwaysOn()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
index 6433376cd2a9..b5060ee416f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -190,13 +191,13 @@ public class NavigationBarContextTest extends SysuiTestCase {
kbd2.setDarkIntensity(0f);
// Update icon returns the drawable intensity to half
- doReturn(kbd1).when(button).getNewDrawable();
- button.updateIcon();
+ doReturn(kbd1).when(button).getNewDrawable(anyInt(), anyInt());
+ button.updateIcon(0, 0);
assertEquals(TEST_DARK_INTENSITY, kbd1.getDarkIntensity(), DARK_INTENSITY_ERR);
// Return old dark intensity on new drawable after update icon
- doReturn(kbd2).when(button).getNewDrawable();
- button.updateIcon();
+ doReturn(kbd2).when(button).getNewDrawable(anyInt(), anyInt());
+ button.updateIcon(0, 0);
assertEquals(TEST_DARK_INTENSITY, kbd2.getDarkIntensity(), DARK_INTENSITY_ERR);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
index d6b38ff4936f..f21235c12cde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
@@ -59,8 +59,8 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
final View view = new View(mContext);
mRotationButton = mock(RotationButton.class);
- mRotationButtonController = spy(
- new RotationButtonController(mContext, RES_UNDEF, mRotationButton));
+ mRotationButtonController = spy(new RotationButtonController(mContext, 0, 0,
+ mRotationButton));
final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
doReturn(view).when(mRotationButton).getCurrentView();
doReturn(true).when(mRotationButton).acceptRotationProposal();
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 c2d218140803..b0b66b87d421 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
@@ -180,6 +180,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private ConversationNotificationManager mConversationNotificationManager;
@Mock
+ private BiometricUnlockController mBiometricUnlockController;
+ @Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
@@ -238,7 +240,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController,
mFlingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
- mStatusBarKeyguardViewManager);
+ mBiometricUnlockController, mStatusBarKeyguardViewManager);
mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager,
mNotificationShelf, mNotificationAreaController, mScrimController);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
index ca6c16f91ad8..8c37cf1514fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static com.google.common.truth.Truth.assertThat;
@@ -41,6 +42,7 @@ import com.android.internal.colorextraction.ColorExtractor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -63,6 +65,7 @@ public class NotificationShadeWindowControllerTest extends SysuiTestCase {
@Mock private IActivityManager mActivityManager;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
+ @Mock private KeyguardViewMediator mKeyguardViewMediator;
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock ColorExtractor.GradientColors mGradientColors;
@@ -79,8 +82,8 @@ public class NotificationShadeWindowControllerTest extends SysuiTestCase {
mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController, mColorExtractor,
- mDumpManager);
+ mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+ mColorExtractor, mDumpManager);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
@@ -120,6 +123,17 @@ public class NotificationShadeWindowControllerTest extends SysuiTestCase {
}
@Test
+ public void attach_visibleWithWallpaper() {
+ clearInvocations(mWindowManager);
+ when(mKeyguardViewMediator.isShowingAndNotOccluded()).thenReturn(true);
+ mNotificationShadeWindowController.attach();
+
+ verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
+ verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+ assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) != 0).isTrue();
+ }
+
+ @Test
public void setBackgroundBlurRadius_expandedWithBlurs() {
mNotificationShadeWindowController.setBackgroundBlurRadius(10);
verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index f83fbd478bf3..eca48c8c2ee1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -59,6 +59,11 @@ public class BatteryControllerTest extends SysuiTestCase {
}
@Test
+ public void testBatteryInitialized() {
+ Assert.assertTrue(mBatteryController.mHasReceivedBattery);
+ }
+
+ @Test
public void testIndependentAODBatterySaver_true() {
PowerSaveState state = new PowerSaveState.Builder()
.setBatterySaverEnabled(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
index fa711f1cf9a2..a16fb5e8dc17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
@@ -23,10 +23,12 @@ import static org.mockito.Mockito.verify;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.Lifecycle.Event;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -76,6 +78,34 @@ public class CallbackControllerTest extends SysuiTestCase {
verify(controller).removeCallback(eq(callback));
}
+ @Test
+ public void testCallbackIsRemovedOnDestroy() {
+ SimpleLifecycleOwner owner = new SimpleLifecycleOwner();
+
+ Object callback = new Object();
+ Controller controller = mock(Controller.class);
+ controller.observe(owner, callback);
+
+ owner.setState(Lifecycle.State.RESUMED);
+ verify(controller).addCallback(callback);
+
+ owner.setState(Lifecycle.State.DESTROYED);
+ verify(controller).removeCallback(callback);
+ }
+
+ private static class SimpleLifecycleOwner implements LifecycleOwner {
+ LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
+ }
+
+ public void setState(Lifecycle.State state) {
+ mLifecycle.setCurrentState(state);
+ }
+ }
+
private static class Controller implements CallbackController<Object> {
@Override
public void addCallback(Object listener) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
index 016e402c69f4..db50163ce6a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
@@ -17,6 +17,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.policy.CastController.Callback;
import org.junit.Before;
@@ -51,7 +52,7 @@ public class CastControllerImplTest extends SysuiTestCase {
mContext.addMockSystemService(MediaProjectionManager.class, mMediaProjectionManager);
when(mMediaProjectionManager.getActiveProjectionInfo()).thenReturn(mProjection);
- mController = new CastControllerImpl(mContext);
+ mController = new CastControllerImpl(mContext, mock(DumpManager.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index 5ce209b1f249..0c2361a9f6b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -15,14 +15,16 @@
package com.android.systemui.statusbar.policy;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.Intent;
import android.location.LocationManager;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -31,12 +33,15 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.appops.AppOpsController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
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
@@ -46,14 +51,21 @@ public class LocationControllerImplTest extends SysuiTestCase {
private LocationControllerImpl mLocationController;
private TestableLooper mTestableLooper;
+ @Mock private AppOpsController mAppOpsController;
+
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
mTestableLooper = TestableLooper.get(this);
mLocationController = spy(new LocationControllerImpl(mContext,
+ mAppOpsController,
mTestableLooper.getLooper(),
- mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
mock(BroadcastDispatcher.class),
mock(BootCompleteCache.class)));
+
+ mTestableLooper.processAllMessages();
}
@Test
@@ -67,12 +79,12 @@ public class LocationControllerImplTest extends SysuiTestCase {
mLocationController.addCallback(callback);
mLocationController.addCallback(mock(LocationChangeCallback.class));
- when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(false);
- mLocationController.onReceive(mContext, new Intent(
- LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION));
- when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(true);
- mLocationController.onReceive(mContext, new Intent(
- LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION));
+ doReturn(false).when(mLocationController).areActiveHighPowerLocationRequests();
+ mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0,
+ "", false);
+ doReturn(true).when(mLocationController).areActiveHighPowerLocationRequests();
+ mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0,
+ "", true);
mTestableLooper.processAllMessages();
}
@@ -107,11 +119,22 @@ public class LocationControllerImplTest extends SysuiTestCase {
LocationChangeCallback callback = mock(LocationChangeCallback.class);
mLocationController.addCallback(callback);
+
+ mTestableLooper.processAllMessages();
+
mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION));
mTestableLooper.processAllMessages();
verify(callback, times(2)).onLocationSettingsChanged(anyBoolean());
+
+ doReturn(true).when(mLocationController).areActiveHighPowerLocationRequests();
+ mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0,
+ "", true);
+
+ mTestableLooper.processAllMessages();
+
+ verify(callback, times(1)).onLocationActiveChanged(anyBoolean());
}
@Test
@@ -124,6 +147,8 @@ public class LocationControllerImplTest extends SysuiTestCase {
verify(callback).onLocationSettingsChanged(anyBoolean());
mLocationController.removeCallback(callback);
+ mTestableLooper.processAllMessages();
+
mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION));
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
new file mode 100644
index 000000000000..8c9248222014
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of {@link ThreadFactory} that returns {@link FakeExecutor} where it can.
+ */
+public class FakeThreadFactory implements ThreadFactory {
+ private final FakeExecutor mFakeExecutor;
+
+ public FakeThreadFactory(FakeExecutor fakeExecutor) {
+ mFakeExecutor = fakeExecutor;
+ }
+
+ @Override
+ public Executor buildExecutorOnNewThread(String threadName) {
+ return mFakeExecutor;
+ }
+
+ @Override
+ public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
+ return mFakeExecutor;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
index 9149599f2c7c..0d8dd2c0f140 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
@@ -23,15 +23,16 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.SensorEventListener;
-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.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeThreadFactory;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -39,20 +40,20 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class AsyncSensorManagerTest extends SysuiTestCase {
private AsyncSensorManager mAsyncSensorManager;
private SensorEventListener mListener;
private FakeSensorManager.FakeProximitySensor mSensor;
private PluginManager mPluginManager;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() throws Exception {
mPluginManager = mock(PluginManager.class);
FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext);
mAsyncSensorManager = new AsyncSensorManager(
- fakeSensorManager, mPluginManager, new Handler());
+ fakeSensorManager, new FakeThreadFactory(mFakeExecutor), mPluginManager);
mSensor = fakeSensorManager.getFakeProximitySensor();
mListener = mock(SensorEventListener.class);
}
@@ -99,6 +100,6 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
}
public void waitUntilRequestsCompleted() {
- TestableLooper.get(this).processAllMessages();
+ mFakeExecutor.runAllReady();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
index 8ba7d62ba843..d3a35a735f6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
@@ -20,12 +20,14 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.os.Handler;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeThreadFactory;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -33,21 +35,20 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class ThresholdSensorImplTest extends SysuiTestCase {
private ThresholdSensorImpl mThresholdSensor;
private FakeSensorManager mSensorManager;
private AsyncSensorManager mAsyncSensorManager;
private FakeSensorManager.FakeProximitySensor mFakeProximitySensor;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() throws Exception {
- allowTestableLooperAsMainThread();
mSensorManager = new FakeSensorManager(getContext());
mAsyncSensorManager = new AsyncSensorManager(
- mSensorManager, null, new Handler());
+ mSensorManager, new FakeThreadFactory(mFakeExecutor), null);
mFakeProximitySensor = mSensorManager.getFakeProximitySensor();
ThresholdSensorImpl.Builder thresholdSensorBuilder = new ThresholdSensorImpl.Builder(
@@ -60,6 +61,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testSingleListener() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -81,6 +83,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testMultiListener() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listenerA = new TestableListener();
TestableListener listenerB = new TestableListener();
@@ -114,6 +117,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testDuplicateListener() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listenerA = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -138,6 +142,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
}
@Test
public void testUnregister() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -157,6 +162,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testPauseAndResume() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -199,6 +205,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testAlertListeners() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listenerA = new TestableListener();
TestableListener listenerB = new TestableListener();
@@ -230,6 +237,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testHysteresis() {
+ Assert.setTestThread(Thread.currentThread());
float lowValue = 10f;
float highValue = 100f;
FakeSensorManager.FakeGenericSensor sensor = mSensorManager.getFakeLightSensor();
@@ -278,6 +286,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testAlertAfterPause() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
mThresholdSensor.register(listener);
@@ -307,7 +316,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
}
private void waitForSensorManager() {
- TestableLooper.get(this).processAllMessages();
+ mFakeExecutor.runAllReady();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
new file mode 100644
index 000000000000..8cb5f3e65a5e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FakeSettings implements SecureSettings, GlobalSettings, SystemSettings {
+ private final Map<SettingsKey, String> mValues = new HashMap<>();
+ private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
+ new HashMap<>();
+
+ public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
+
+ public FakeSettings() {
+ }
+
+ public FakeSettings(String initialKey, String initialValue) {
+ putString(initialKey, initialValue);
+ }
+
+ public FakeSettings(Map<String, String> initialValues) {
+ for (Map.Entry<String, String> kv : initialValues.entrySet()) {
+ putString(kv.getKey(), kv.getValue());
+ }
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return null;
+ }
+
+ @Override
+ public void registerContentObserverForUser(String name, ContentObserver settingsObserver,
+ int userHandle) {
+ SettingsKey key = new SettingsKey(userHandle, name);
+ mContentObservers.putIfAbsent(key, new ArrayList<>());
+ List<ContentObserver> observers = mContentObservers.get(key);
+ observers.add(settingsObserver);
+ }
+
+ @Override
+ public void unregisterContentObserver(ContentObserver settingsObserver) {
+ for (SettingsKey key : mContentObservers.keySet()) {
+ List<ContentObserver> observers = mContentObservers.get(key);
+ observers.remove(settingsObserver);
+ }
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Uri.withAppendedPath(CONTENT_URI, name);
+ }
+
+ @Override
+ public int getUserId() {
+ return UserHandle.USER_CURRENT;
+ }
+
+ @Override
+ public String getString(String name) {
+ return getStringForUser(name, getUserId());
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return mValues.get(new SettingsKey(userHandle, name));
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ return putStringForUser(name, value, null, false, getUserId(), overrideableByRestore);
+ }
+
+ @Override
+ public boolean putString(String name, String value) {
+ return putString(name, value, false);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return putStringForUser(name, value, null, false, userHandle, false);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ SettingsKey key = new SettingsKey(userHandle, name);
+ mValues.put(key, value);
+
+ Uri uri = getUriFor(name);
+ for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) {
+ observer.dispatchChange(false, List.of(uri), userHandle);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ return putString(name, value);
+ }
+
+ private static class SettingsKey extends Pair<Integer, String> {
+ SettingsKey(Integer first, String second) {
+ super(first, second);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
new file mode 100644
index 000000000000..0d560f237a07
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.database.ContentObserver;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collection;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class FakeSettingsTest extends SysuiTestCase {
+ @Mock
+ ContentObserver mContentObserver;
+
+ private FakeSettings mFakeSettings;
+
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mFakeSettings = new FakeSettings();
+ }
+
+ /**
+ * Test FakeExecutor that receives non-delayed items to execute.
+ */
+ @Test
+ public void testPutAndGet() throws Settings.SettingNotFoundException {
+ mFakeSettings.putInt("foobar", 1);
+ assertThat(mFakeSettings.getInt("foobar")).isEqualTo(1);
+ }
+
+ @Test
+ public void testInitialize() {
+ mFakeSettings = new FakeSettings("abra", "cadabra");
+ assertThat(mFakeSettings.getString("abra")).isEqualTo("cadabra");
+ }
+
+ @Test
+ public void testInitializeWithMap() {
+ mFakeSettings = new FakeSettings(Map.of("one fish", "two fish", "red fish", "blue fish"));
+ assertThat(mFakeSettings.getString("red fish")).isEqualTo("blue fish");
+ assertThat(mFakeSettings.getString("one fish")).isEqualTo("two fish");
+ }
+
+ @Test
+ public void testRegisterContentObserver() {
+ mFakeSettings.registerContentObserver("cat", mContentObserver);
+
+ mFakeSettings.putString("cat", "hat");
+
+ verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt());
+ }
+
+ @Test
+ public void testUnregisterContentObserver() {
+ mFakeSettings.registerContentObserver("cat", mContentObserver);
+ mFakeSettings.unregisterContentObserver(mContentObserver);
+
+ mFakeSettings.putString("cat", "hat");
+
+ verify(mContentObserver, never()).dispatchChange(
+ anyBoolean(), any(Collection.class), anyInt());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index 8ec4cb8b927b..50c1e73f6aac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -25,6 +25,8 @@ import java.io.PrintWriter;
public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCallback>
implements BatteryController {
+ private boolean mWirelessCharging;
+
public FakeBatteryController(LeakCheck test) {
super(test, "battery");
}
@@ -58,4 +60,13 @@ public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCal
public boolean isAodPowerSave() {
return false;
}
+
+ @Override
+ public boolean isWirelessCharging() {
+ return mWirelessCharging;
+ }
+
+ public void setWirelessCharging(boolean wirelessCharging) {
+ mWirelessCharging = wirelessCharging;
+ }
}
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
index 754584e70fad..6ddb122936e7 100644
--- a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -1,24 +1,6 @@
// Signature format: 2.0
package android.net {
- public final class TetheredClient implements android.os.Parcelable {
- ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
- method public int describeContents();
- method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
- method @NonNull public android.net.MacAddress getMacAddress();
- method public int getTetheringType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
- }
-
- public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.net.LinkAddress getAddress();
- method @Nullable public String getHostname();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
- }
-
public final class TetheringConstants {
field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
@@ -38,69 +20,15 @@ package android.net {
method @NonNull public String[] getTetheringErroredIfaces();
method public boolean isTetheringSupported();
method public boolean isTetheringSupported(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
method @Deprecated public int setUsbTethering(boolean);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
method @Deprecated public int tether(@NonNull String);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
method @Deprecated public int untether(@NonNull String);
- field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
- field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
- field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
- field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
- field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
- field public static final int TETHERING_BLUETOOTH = 2; // 0x2
- field public static final int TETHERING_ETHERNET = 5; // 0x5
- field public static final int TETHERING_INVALID = -1; // 0xffffffff
- field public static final int TETHERING_NCM = 4; // 0x4
- field public static final int TETHERING_USB = 1; // 0x1
- field public static final int TETHERING_WIFI = 0; // 0x0
- field public static final int TETHERING_WIFI_P2P = 3; // 0x3
- field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
- field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
- field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
- field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
- field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
- field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
- field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
- field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
- field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
- field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
- field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
- field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
- field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
- field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
- field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
- field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
- field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
- field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
- field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
- field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
- }
-
- public static interface TetheringManager.OnTetheringEntitlementResultListener {
- method public void onTetheringEntitlementResult(int);
- }
-
- public static interface TetheringManager.StartTetheringCallback {
- method public default void onTetheringFailed(int);
- method public default void onTetheringStarted();
}
public static interface TetheringManager.TetheringEventCallback {
- method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
- method public default void onError(@NonNull String, int);
- method public default void onOffloadStatusChanged(int);
method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
- method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheringSupported(boolean);
- method public default void onUpstreamChanged(@Nullable android.net.Network);
}
public static class TetheringManager.TetheringInterfaceRegexps {
@@ -109,21 +37,5 @@ package android.net {
method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
}
- public static class TetheringManager.TetheringRequest {
- method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
- method @Nullable public android.net.LinkAddress getLocalIpv4Address();
- method public boolean getShouldShowEntitlementUi();
- method public int getTetheringType();
- method public boolean isExemptFromEntitlementCheck();
- }
-
- public static class TetheringManager.TetheringRequest.Builder {
- ctor public TetheringManager.TetheringRequest.Builder(int);
- method @NonNull public android.net.TetheringManager.TetheringRequest build();
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
- }
-
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 48be0d96425c..645b00001330 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -16,8 +16,6 @@
package android.net;
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -36,7 +34,6 @@ import java.util.Objects;
* @hide
*/
@SystemApi
-@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public final class TetheredClient implements Parcelable {
@NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 7483bac11662..db8436859281 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -55,7 +55,6 @@ import java.util.function.Supplier;
* @hide
*/
@SystemApi
-@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public class TetheringManager {
private static final String TAG = TetheringManager.class.getSimpleName();
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
index 2d3108a0bff1..591861f5b837 100644
--- a/packages/Tethering/jarjar-rules.txt
+++ b/packages/Tethering/jarjar-rules.txt
@@ -3,7 +3,7 @@
# If there are files in that filegroup that are not covered below, the classes in the
# module will be overwritten by the ones in the framework.
rule com.android.internal.util.** com.android.networkstack.tethering.util.@1
-rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
+rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
index 593d04a06b93..a0198cc9c126 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
@@ -273,8 +273,9 @@ public class TetheringNotificationUpdater {
mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
0 /* requestCode */,
new Intent(Settings.ACTION_TETHER_SETTINGS)
- .setPackage(getSettingsPackageName(mContext.getPackageManager())),
- Intent.FLAG_ACTIVITY_NEW_TASK | PendingIntent.FLAG_IMMUTABLE,
+ .setPackage(getSettingsPackageName(mContext.getPackageManager()))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ PendingIntent.FLAG_IMMUTABLE,
null /* options */);
showNotification(R.drawable.stat_sys_tether_general, title, message,
@@ -317,8 +318,9 @@ public class TetheringNotificationUpdater {
mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
0 /* requestCode */,
new Intent(Settings.ACTION_TETHER_SETTINGS)
- .setPackage(getSettingsPackageName(mContext.getPackageManager())),
- Intent.FLAG_ACTIVITY_NEW_TASK | PendingIntent.FLAG_IMMUTABLE,
+ .setPackage(getSettingsPackageName(mContext.getPackageManager()))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ PendingIntent.FLAG_IMMUTABLE,
null /* options */);
showNotification(R.drawable.stat_sys_tether_general, title, message,
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 74df11370e50..9bb01ae5df1d 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -42,6 +42,7 @@ import android.net.dhcp.DhcpPacket;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.system.Os;
import android.util.Log;
@@ -101,17 +102,21 @@ public class EthernetTetheringTest {
private UiAutomation mUiAutomation =
InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ private boolean mRunTests;
@Before
public void setUp() throws Exception {
- mHandlerThread = new HandlerThread(getClass().getSimpleName());
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
- mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
// Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
// tethered client callbacks.
mUiAutomation.adoptShellPermissionIdentity(
MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED);
+ mRunTests = mTm.isTetheringSupported() && mEm != null;
+ assumeTrue(mRunTests);
+
+ mHandlerThread = new HandlerThread(getClass().getSimpleName());
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
}
private void cleanUp() throws Exception {
@@ -135,7 +140,7 @@ public class EthernetTetheringTest {
@After
public void tearDown() throws Exception {
try {
- cleanUp();
+ if (mRunTests) cleanUp();
} finally {
mUiAutomation.dropShellPermissionIdentity();
}
@@ -224,9 +229,19 @@ public class EthernetTetheringTest {
}
+ private boolean isAdbOverNetwork() {
+ // If adb TCP port opened, this test may running by adb over network.
+ return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1)
+ || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1);
+ }
+
@Test
public void testPhysicalEthernet() throws Exception {
assumeTrue(mEm.isAvailable());
+ // Do not run this test if adb is over network and ethernet is connected.
+ // It is likely the adb run over ethernet, the adb would break when ethernet is switching
+ // from client mode to server mode. See b/160389275.
+ assumeFalse(isAdbOverNetwork());
// Get an interface to use.
final String iface = mTetheredInterfaceRequester.getInterface();
diff --git a/packages/Tethering/tests/privileged/Android.bp b/packages/Tethering/tests/privileged/Android.bp
new file mode 100644
index 000000000000..a0fb24603a93
--- /dev/null
+++ b/packages/Tethering/tests/privileged/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+ name: "TetheringPrivilegedTests",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ certificate: "networkstack",
+ platform_apis: true,
+ test_suites: [
+ "general-tests",
+ "mts",
+ ],
+ compile_multilib: "both",
+}
diff --git a/packages/Tethering/tests/privileged/AndroidManifest.xml b/packages/Tethering/tests/privileged/AndroidManifest.xml
new file mode 100644
index 000000000000..49eba15d13d4
--- /dev/null
+++ b/packages/Tethering/tests/privileged/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.networkstack.tethering.tests.privileged"
+ android:sharedUserId="android.uid.networkstack">
+
+ <!-- Note: do not add any privileged or signature permissions that are granted
+ to the network stack and its shared uid apps. Otherwise, the test APK will
+ install, but when the device is rebooted, it will bootloop because this
+ test APK is not in the privileged permission allow list -->
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.networkstack.tethering.tests.privileged"
+ android:label="Tethering privileged tests">
+ </instrumentation>
+</manifest>
diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt
index 1ea56cdf1a3d..ec2d2b02004e 100644
--- a/packages/Tethering/tests/unit/jarjar-rules.txt
+++ b/packages/Tethering/tests/unit/jarjar-rules.txt
@@ -8,4 +8,4 @@ rule com.android.internal.util.State* com.android.networkstack.tethering.util.St
rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
-rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
+rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml
index 14e6e91e3a25..a75fb2d47bbd 100644
--- a/packages/WAPPushManager/AndroidManifest.xml
+++ b/packages/WAPPushManager/AndroidManifest.xml
@@ -23,6 +23,8 @@
<permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND"
android:protectionLevel="signatureOrSystem" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
<original-package android:name="com.android.smspush" />
<application
android:allowClearUserData="false">
diff --git a/packages/overlays/IconPackKaiAndroidOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackKaiAndroidOverlay/res/drawable/ic_screenshot.xml
index 8359e2012ff9..4184a1ec06fb 100644
--- a/packages/overlays/IconPackKaiAndroidOverlay/res/drawable/ic_screenshot.xml
+++ b/packages/overlays/IconPackKaiAndroidOverlay/res/drawable/ic_screenshot.xml
@@ -13,8 +13,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector android:height="24dp" android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M16.75,1h-9.5C6.01,1,5,2.01,5,3.25v17.5C5,21.99,6.01,23,7.25,23h9.5c1.24,0,2.25-1.01,2.25-2.25V3.25 C19,2.01,17.99,1,16.75,1z M7.25,2.5h9.5c0.41,0,0.75,0.34,0.75,0.75v1h-11v-1C6.5,2.84,6.84,2.5,7.25,2.5z M17.5,5.75v12.5h-11 V5.75H17.5z M16.75,21.5h-9.5c-0.41,0-0.75-0.34-0.75-0.75v-1h11v1C17.5,21.16,17.16,21.5,16.75,21.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M9.5,11V8.5H12V7H8.75C8.34,7,8,7.34,8,7.75V11H9.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12,17h3.25c0.41,0,0.75-0.34,0.75-0.75V13h-1.5v2.5H12V17z"/>
-</vector> \ No newline at end of file
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.75,1h-9.5C6.01,1 5,2.01 5,3.25v17.5C5,21.99 6.01,23 7.25,23h9.5c1.24,0 2.25,-1.01 2.25,-2.25V3.25C19,2.01 17.99,1 16.75,1zM7.25,2.5h9.5c0.41,0 0.75,0.34 0.75,0.75v1h-11v-1C6.5,2.84 6.84,2.5 7.25,2.5zM17.5,5.75v12.5h-11V5.75H17.5zM16.75,21.5h-9.5c-0.41,0 -0.75,-0.34 -0.75,-0.75v-1h11v1C17.5,21.16 17.16,21.5 16.75,21.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.5,11V8.5H12V7H8.75C8.34,7 8,7.34 8,7.75V11H9.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17h3.25c0.41,0 0.75,-0.34 0.75,-0.75V13h-1.5v2.5H12V17z"/>
+</vector>
diff --git a/packages/overlays/IconPackKaiLauncherOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackKaiLauncherOverlay/res/drawable/ic_screenshot.xml
new file mode 100644
index 000000000000..4184a1ec06fb
--- /dev/null
+++ b/packages/overlays/IconPackKaiLauncherOverlay/res/drawable/ic_screenshot.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.75,1h-9.5C6.01,1 5,2.01 5,3.25v17.5C5,21.99 6.01,23 7.25,23h9.5c1.24,0 2.25,-1.01 2.25,-2.25V3.25C19,2.01 17.99,1 16.75,1zM7.25,2.5h9.5c0.41,0 0.75,0.34 0.75,0.75v1h-11v-1C6.5,2.84 6.84,2.5 7.25,2.5zM17.5,5.75v12.5h-11V5.75H17.5zM16.75,21.5h-9.5c-0.41,0 -0.75,-0.34 -0.75,-0.75v-1h11v1C17.5,21.16 17.16,21.5 16.75,21.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.5,11V8.5H12V7H8.75C8.34,7 8,7.34 8,7.75V11H9.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17h3.25c0.41,0 0.75,-0.34 0.75,-0.75V13h-1.5v2.5H12V17z"/>
+</vector>
diff --git a/packages/overlays/IconPackKaiLauncherOverlay/res/drawable/ic_select.xml b/packages/overlays/IconPackKaiLauncherOverlay/res/drawable/ic_select.xml
index e7624840cecb..f949a0cbd859 100644
--- a/packages/overlays/IconPackKaiLauncherOverlay/res/drawable/ic_select.xml
+++ b/packages/overlays/IconPackKaiLauncherOverlay/res/drawable/ic_select.xml
@@ -13,9 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M 11.25 1 H 12.75 V 4 H 11.25 V 1 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 15.69 4.06 H 18.69 V 5.56 H 15.69 V 4.06 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 6.06 3.31 H 7.56 V 6.31 H 6.06 V 3.31 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M15.5,12.5l-1.25,0V8.12C14.25,6.95,13.3,6,12.12,6S10,6.95,10,8.12v7.9L8.03,15.5c-0.39-0.1-1.23-0.36-2.56,0.97 c-0.29,0.29-0.29,0.75-0.01,1.05l3.79,3.98c0,0,0,0,0,0.01c1.37,1.41,3.28,1.51,4.04,1.49l2.2,0c2.12,0.06,5.25-1.01,5.25-5.25 C20.75,13.19,17.23,12.46,15.5,12.5z M15.5,21.5l-2.25,0c-0.44,0.01-1.93-0.02-2.92-1.03l-3.27-3.43c0.17-0.1,0.38-0.13,0.58-0.08 l2.91,0.78c0.47,0.12,0.94-0.23,0.94-0.72V8.12c0-0.34,0.28-0.62,0.62-0.62s0.62,0.28,0.62,0.62v5.12c0,0.41,0.33,0.75,0.75,0.75 l2.05,0c1.26-0.03,3.7,0.37,3.7,3.75C19.25,21.14,16.78,21.53,15.5,21.5z"/>
-</vector> \ No newline at end of file
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M15.38,12.5h-1.25V8.12C14.13,6.95 13.18,6 12,6S9.88,6.95 9.88,8.12v7.9L7.91,15.5c-0.39,-0.1 -1.23,-0.36 -2.56,0.97c-0.29,0.29 -0.29,0.75 -0.01,1.05l3.79,3.98c0,0 0,0 0,0.01c1.37,1.41 3.28,1.51 4.04,1.49h2.2c2.12,0.06 5.25,-1.01 5.25,-5.25C20.63,13.19 17.11,12.46 15.38,12.5zM15.38,21.5h-2.25c-0.44,0.01 -1.93,-0.02 -2.92,-1.03l-3.27,-3.43c0.17,-0.1 0.38,-0.13 0.58,-0.08l2.91,0.78c0.47,0.12 0.94,-0.23 0.94,-0.72v-8.9c0,-0.34 0.28,-0.62 0.62,-0.62s0.62,0.28 0.62,0.62v5.12c0,0.41 0.33,0.75 0.75,0.75h2.05c1.26,-0.03 3.7,0.37 3.7,3.75C19.13,21.14 16.66,21.53 15.38,21.5zM3,8.25c0.41,0 0.75,0.34 0.75,0.75S3.41,9.75 3,9.75S2.25,9.41 2.25,9S2.59,8.25 3,8.25zM6,8.25c0.41,0 0.75,0.34 0.75,0.75S6.41,9.75 6,9.75C5.59,9.75 5.25,9.41 5.25,9S5.59,8.25 6,8.25zM18,8.25c0.41,0 0.75,0.34 0.75,0.75S18.41,9.75 18,9.75S17.25,9.41 17.25,9S17.59,8.25 18,8.25zM21,8.25c0.41,0 0.75,0.34 0.75,0.75S21.41,9.75 21,9.75S20.25,9.41 20.25,9S20.59,8.25 21,8.25zM12,2.25c0.41,0 0.75,0.34 0.75,0.75S12.41,3.75 12,3.75S11.25,3.41 11.25,3S11.59,2.25 12,2.25zM15,2.25c0.41,0 0.75,0.34 0.75,0.75S15.41,3.75 15,3.75S14.25,3.41 14.25,3S14.59,2.25 15,2.25zM3,2.25c0.41,0 0.75,0.34 0.75,0.75S3.41,3.75 3,3.75S2.25,3.41 2.25,3S2.59,2.25 3,2.25zM6,2.25c0.41,0 0.75,0.34 0.75,0.75S6.41,3.75 6,3.75C5.59,3.75 5.25,3.41 5.25,3S5.59,2.25 6,2.25zM9,2.25c0.41,0 0.75,0.34 0.75,0.75S9.41,3.75 9,3.75S8.25,3.41 8.25,3S8.59,2.25 9,2.25zM18,2.25c0.41,0 0.75,0.34 0.75,0.75S18.41,3.75 18,3.75S17.25,3.41 17.25,3S17.59,2.25 18,2.25zM21,2.25c0.41,0 0.75,0.34 0.75,0.75S21.41,3.75 21,3.75S20.25,3.41 20.25,3S20.59,2.25 21,2.25zM3,5.25c0.41,0 0.75,0.34 0.75,0.75c0,0.41 -0.34,0.75 -0.75,0.75S2.25,6.41 2.25,6C2.25,5.59 2.59,5.25 3,5.25zM21,5.25c0.41,0 0.75,0.34 0.75,0.75c0,0.41 -0.34,0.75 -0.75,0.75S20.25,6.41 20.25,6C20.25,5.59 20.59,5.25 21,5.25z"/>
+</vector>
diff --git a/packages/overlays/IconPackKaiSystemUIOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackKaiSystemUIOverlay/res/drawable/ic_screenshot.xml
new file mode 100644
index 000000000000..4184a1ec06fb
--- /dev/null
+++ b/packages/overlays/IconPackKaiSystemUIOverlay/res/drawable/ic_screenshot.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.75,1h-9.5C6.01,1 5,2.01 5,3.25v17.5C5,21.99 6.01,23 7.25,23h9.5c1.24,0 2.25,-1.01 2.25,-2.25V3.25C19,2.01 17.99,1 16.75,1zM7.25,2.5h9.5c0.41,0 0.75,0.34 0.75,0.75v1h-11v-1C6.5,2.84 6.84,2.5 7.25,2.5zM17.5,5.75v12.5h-11V5.75H17.5zM16.75,21.5h-9.5c-0.41,0 -0.75,-0.34 -0.75,-0.75v-1h11v1C17.5,21.16 17.16,21.5 16.75,21.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.5,11V8.5H12V7H8.75C8.34,7 8,7.34 8,7.75V11H9.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17h3.25c0.41,0 0.75,-0.34 0.75,-0.75V13h-1.5v2.5H12V17z"/>
+</vector>
diff --git a/packages/overlays/IconPackSamAndroidOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackSamAndroidOverlay/res/drawable/ic_screenshot.xml
index 404a8e5ef055..b5d4555a22f9 100644
--- a/packages/overlays/IconPackSamAndroidOverlay/res/drawable/ic_screenshot.xml
+++ b/packages/overlays/IconPackSamAndroidOverlay/res/drawable/ic_screenshot.xml
@@ -13,8 +13,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector android:height="24dp" android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M8.75,11c0.41,0,0.75-0.34,0.75-0.75V8.5h1.75C11.66,8.5,12,8.16,12,7.75C12,7.34,11.66,7,11.25,7H9C8.45,7,8,7.45,8,8 v2.25C8,10.66,8.34,11,8.75,11z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12.75,17H15c0.55,0,1-0.45,1-1v-2.25c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v1.75h-1.75 c-0.41,0-0.75,0.34-0.75,0.75C12,16.66,12.34,17,12.75,17z"/>
- <path android:fillColor="@android:color/white" android:pathData="M16,1H8C6.34,1,5,2.34,5,4v16c0,1.65,1.35,3,3,3h8c1.65,0,3-1.35,3-3V4C19,2.34,17.66,1,16,1z M17,18H7V6h10V18z"/>
-</vector> \ No newline at end of file
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8.75,11c0.41,0 0.75,-0.34 0.75,-0.75V8.5h1.75C11.66,8.5 12,8.16 12,7.75C12,7.34 11.66,7 11.25,7H9C8.45,7 8,7.45 8,8v2.25C8,10.66 8.34,11 8.75,11z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.75,17H15c0.55,0 1,-0.45 1,-1v-2.25c0,-0.41 -0.34,-0.75 -0.75,-0.75s-0.75,0.34 -0.75,0.75v1.75h-1.75c-0.41,0 -0.75,0.34 -0.75,0.75C12,16.66 12.34,17 12.75,17z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,1H8C6.34,1 5,2.34 5,4v16c0,1.65 1.35,3 3,3h8c1.65,0 3,-1.35 3,-3V4C19,2.34 17.66,1 16,1zM17,18H7V6h10V18z"/>
+</vector>
diff --git a/packages/overlays/IconPackSamLauncherOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackSamLauncherOverlay/res/drawable/ic_screenshot.xml
new file mode 100644
index 000000000000..b5d4555a22f9
--- /dev/null
+++ b/packages/overlays/IconPackSamLauncherOverlay/res/drawable/ic_screenshot.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8.75,11c0.41,0 0.75,-0.34 0.75,-0.75V8.5h1.75C11.66,8.5 12,8.16 12,7.75C12,7.34 11.66,7 11.25,7H9C8.45,7 8,7.45 8,8v2.25C8,10.66 8.34,11 8.75,11z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.75,17H15c0.55,0 1,-0.45 1,-1v-2.25c0,-0.41 -0.34,-0.75 -0.75,-0.75s-0.75,0.34 -0.75,0.75v1.75h-1.75c-0.41,0 -0.75,0.34 -0.75,0.75C12,16.66 12.34,17 12.75,17z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,1H8C6.34,1 5,2.34 5,4v16c0,1.65 1.35,3 3,3h8c1.65,0 3,-1.35 3,-3V4C19,2.34 17.66,1 16,1zM17,18H7V6h10V18z"/>
+</vector>
diff --git a/packages/overlays/IconPackSamLauncherOverlay/res/drawable/ic_select.xml b/packages/overlays/IconPackSamLauncherOverlay/res/drawable/ic_select.xml
index 7fc05a9873ab..86a15e6958f4 100644
--- a/packages/overlays/IconPackSamLauncherOverlay/res/drawable/ic_select.xml
+++ b/packages/overlays/IconPackSamLauncherOverlay/res/drawable/ic_select.xml
@@ -13,9 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M17.59,5.83l0.71-0.71c0.39-0.39,0.39-1.02,0-1.41l0,0c-0.39-0.39-1.02-0.39-1.41,0l-0.71,0.71 c-0.39,0.39-0.39,1.02,0,1.41C16.56,6.21,17.2,6.21,17.59,5.83z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12,4c0.55,0,1-0.45,1-1V2c0-0.55-0.45-1-1-1s-1,0.45-1,1v1C11,3.55,11.45,4,12,4z"/>
- <path android:fillColor="@android:color/white" android:pathData="M6.42,5.83c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41L7.12,3.71c-0.39-0.39-1.02-0.39-1.41,0l0,0 c-0.39,0.39-0.39,1.02,0,1.41L6.42,5.83z"/>
- <path android:fillColor="@android:color/white" android:pathData="M17.95,14.43l-3.23-1.61c-0.42-0.21-0.88-0.32-1.34-0.32H13v-5C13,6.67,12.33,6,11.5,6C10.67,6,10,6.67,10,7.5v9.12 c0,0.32-0.29,0.55-0.6,0.49l-2.84-0.6c-0.37-0.08-0.76,0.04-1.03,0.31c-0.43,0.44-0.43,1.14,0.01,1.58l3.71,3.71 C9.81,22.68,10.58,23,11.37,23h4.82c1.49,0,2.76-1.1,2.97-2.58l0.41-2.89C19.76,16.26,19.1,15.01,17.95,14.43z"/>
-</vector> \ No newline at end of file
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18.45,14.43l-3.23,-1.61c-0.42,-0.21 -0.88,-0.32 -1.34,-0.32H13.5v-5C13.5,6.67 12.83,6 12,6s-1.5,0.67 -1.5,1.5v9.12c0,0.32 -0.29,0.55 -0.6,0.49l-2.84,-0.6c-0.37,-0.08 -0.76,0.04 -1.03,0.31C5.6,17.26 5.6,17.96 6.04,18.4l3.71,3.71c0.56,0.57 1.33,0.89 2.12,0.89h4.82c1.49,0 2.76,-1.1 2.97,-2.58l0.41,-2.89C20.26,16.26 19.6,15.01 18.45,14.43zM3,8.25c0.41,0 0.75,0.34 0.75,0.75S3.41,9.75 3,9.75S2.25,9.41 2.25,9S2.59,8.25 3,8.25zM6,8.25c0.41,0 0.75,0.34 0.75,0.75S6.41,9.75 6,9.75C5.59,9.75 5.25,9.41 5.25,9S5.59,8.25 6,8.25zM18,8.25c0.41,0 0.75,0.34 0.75,0.75S18.41,9.75 18,9.75S17.25,9.41 17.25,9S17.59,8.25 18,8.25zM21,8.25c0.41,0 0.75,0.34 0.75,0.75S21.41,9.75 21,9.75S20.25,9.41 20.25,9S20.59,8.25 21,8.25zM12,2.25c0.41,0 0.75,0.34 0.75,0.75S12.41,3.75 12,3.75S11.25,3.41 11.25,3S11.59,2.25 12,2.25zM15,2.25c0.41,0 0.75,0.34 0.75,0.75S15.41,3.75 15,3.75S14.25,3.41 14.25,3S14.59,2.25 15,2.25zM3,2.25c0.41,0 0.75,0.34 0.75,0.75S3.41,3.75 3,3.75S2.25,3.41 2.25,3S2.59,2.25 3,2.25zM6,2.25c0.41,0 0.75,0.34 0.75,0.75S6.41,3.75 6,3.75C5.59,3.75 5.25,3.41 5.25,3S5.59,2.25 6,2.25zM9,2.25c0.41,0 0.75,0.34 0.75,0.75S9.41,3.75 9,3.75S8.25,3.41 8.25,3S8.59,2.25 9,2.25zM18,2.25c0.41,0 0.75,0.34 0.75,0.75S18.41,3.75 18,3.75S17.25,3.41 17.25,3S17.59,2.25 18,2.25zM21,2.25c0.41,0 0.75,0.34 0.75,0.75S21.41,3.75 21,3.75S20.25,3.41 20.25,3S20.59,2.25 21,2.25zM3,5.25c0.41,0 0.75,0.34 0.75,0.75c0,0.41 -0.34,0.75 -0.75,0.75S2.25,6.41 2.25,6C2.25,5.59 2.59,5.25 3,5.25zM21,5.25c0.41,0 0.75,0.34 0.75,0.75c0,0.41 -0.34,0.75 -0.75,0.75S20.25,6.41 20.25,6C20.25,5.59 20.59,5.25 21,5.25z"/>
+</vector>
diff --git a/packages/overlays/IconPackSamSystemUIOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackSamSystemUIOverlay/res/drawable/ic_screenshot.xml
new file mode 100644
index 000000000000..b5d4555a22f9
--- /dev/null
+++ b/packages/overlays/IconPackSamSystemUIOverlay/res/drawable/ic_screenshot.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8.75,11c0.41,0 0.75,-0.34 0.75,-0.75V8.5h1.75C11.66,8.5 12,8.16 12,7.75C12,7.34 11.66,7 11.25,7H9C8.45,7 8,7.45 8,8v2.25C8,10.66 8.34,11 8.75,11z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.75,17H15c0.55,0 1,-0.45 1,-1v-2.25c0,-0.41 -0.34,-0.75 -0.75,-0.75s-0.75,0.34 -0.75,0.75v1.75h-1.75c-0.41,0 -0.75,0.34 -0.75,0.75C12,16.66 12.34,17 12.75,17z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,1H8C6.34,1 5,2.34 5,4v16c0,1.65 1.35,3 3,3h8c1.65,0 3,-1.35 3,-3V4C19,2.34 17.66,1 16,1zM17,18H7V6h10V18z"/>
+</vector>
diff --git a/packages/overlays/IconPackVictorAndroidOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackVictorAndroidOverlay/res/drawable/ic_screenshot.xml
index b1fb07d0e0c4..cbd22c6bfeed 100644
--- a/packages/overlays/IconPackVictorAndroidOverlay/res/drawable/ic_screenshot.xml
+++ b/packages/overlays/IconPackVictorAndroidOverlay/res/drawable/ic_screenshot.xml
@@ -13,8 +13,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector android:height="24dp" android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M17.59,1H6.41L5,2.41v19.17L6.41,23h11.17L19,21.59V2.41L17.59,1z M17.5,2.5v1.75h-11V2.5H17.5z M17.5,5.75v12.5h-11V5.75 H17.5z M6.5,21.5v-1.75h11v1.75H6.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 9.5 11 L 9.5 8.5 L 12 8.5 L 12 7 L 8 7 L 8 11 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 17 L 16 17 L 16 13 L 14.5 13 L 14.5 15.5 L 12 15.5 Z"/>
-</vector> \ No newline at end of file
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.59,1H6.41L5,2.41v19.17L6.41,23h11.17L19,21.59V2.41L17.59,1zM17.5,2.5v1.75h-11V2.5H17.5zM17.5,5.75v12.5h-11V5.75H17.5zM6.5,21.5v-1.75h11v1.75H6.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.5,11l0,-2.5l2.5,0l0,-1.5l-4,0l0,4z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17l4,0l0,-4l-1.5,0l0,2.5l-2.5,0z"/>
+</vector>
diff --git a/packages/overlays/IconPackVictorLauncherOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackVictorLauncherOverlay/res/drawable/ic_screenshot.xml
new file mode 100644
index 000000000000..cbd22c6bfeed
--- /dev/null
+++ b/packages/overlays/IconPackVictorLauncherOverlay/res/drawable/ic_screenshot.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.59,1H6.41L5,2.41v19.17L6.41,23h11.17L19,21.59V2.41L17.59,1zM17.5,2.5v1.75h-11V2.5H17.5zM17.5,5.75v12.5h-11V5.75H17.5zM6.5,21.5v-1.75h11v1.75H6.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.5,11l0,-2.5l2.5,0l0,-1.5l-4,0l0,4z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17l4,0l0,-4l-1.5,0l0,2.5l-2.5,0z"/>
+</vector>
diff --git a/packages/overlays/IconPackVictorLauncherOverlay/res/drawable/ic_select.xml b/packages/overlays/IconPackVictorLauncherOverlay/res/drawable/ic_select.xml
index 75ee344ba116..05597dd66107 100644
--- a/packages/overlays/IconPackVictorLauncherOverlay/res/drawable/ic_select.xml
+++ b/packages/overlays/IconPackVictorLauncherOverlay/res/drawable/ic_select.xml
@@ -13,9 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M16.19,12.5h-1.94V8.12C14.25,6.95,13.3,6,12.12,6S10,6.95,10,8.12v7.9l-3.22-0.86l-1.82,1.82L10.68,23h8.6l1.57-7.96 L16.19,12.5z M18.04,21.5h-6.72l-4.27-4.49l0.18-0.18l4.28,1.14V8.12c0-0.34,0.28-0.62,0.62-0.62s0.62,0.28,0.62,0.62V14h3.06 l3.35,1.83L18.04,21.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 11.25 1 H 12.75 V 4 H 11.25 V 1 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 15.69 4.06 H 18.69 V 5.56 H 15.69 V 4.06 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 6.06 3.31 H 7.56 V 6.31 H 6.06 V 3.31 Z"/>
-</vector> \ No newline at end of file
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.25,2.25h1.5v1.5h-1.5V2.25zM18.75,3.75h-1.5v-1.5h1.5V3.75zM15.75,3.75h-1.5v-1.5h1.5V3.75zM12.75,3.75h-1.5v-1.5h1.5V3.75zM9.75,3.75h-1.5v-1.5h1.5V3.75zM6.75,3.75h-1.5v-1.5h1.5V3.75zM2.25,2.25h1.5v1.5h-1.5V2.25zM2.25,5.25h1.5v1.5h-1.5V5.25zM2.25,8.25h1.5v1.5h-1.5V8.25zM18.75,9.75h-1.5v-1.5h1.5V9.75zM6.75,9.75h-1.5v-1.5h1.5V9.75zM20.25,8.25h1.5v1.5h-1.5V8.25zM20.25,5.25h1.5v1.5h-1.5V5.25zM16.07,12.5h-1.94V8.12C14.13,6.95 13.18,6 12,6S9.88,6.95 9.88,8.12v7.9l-3.22,-0.86l-1.82,1.82L10.56,23h8.6l1.57,-7.96L16.07,12.5zM17.92,21.5H11.2l-4.27,-4.49l0.18,-0.18l4.28,1.14V8.12c0,-0.34 0.28,-0.62 0.62,-0.62s0.62,0.28 0.62,0.62V14h3.06l3.35,1.83L17.92,21.5z"/>
+</vector>
diff --git a/packages/overlays/IconPackVictorSystemUIOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackVictorSystemUIOverlay/res/drawable/ic_screenshot.xml
new file mode 100644
index 000000000000..cbd22c6bfeed
--- /dev/null
+++ b/packages/overlays/IconPackVictorSystemUIOverlay/res/drawable/ic_screenshot.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.59,1H6.41L5,2.41v19.17L6.41,23h11.17L19,21.59V2.41L17.59,1zM17.5,2.5v1.75h-11V2.5H17.5zM17.5,5.75v12.5h-11V5.75H17.5zM6.5,21.5v-1.75h11v1.75H6.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.5,11l0,-2.5l2.5,0l0,-1.5l-4,0l0,4z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17l4,0l0,-4l-1.5,0l0,2.5l-2.5,0z"/>
+</vector>
diff --git a/packages/services/PacProcessor/AndroidManifest.xml b/packages/services/PacProcessor/AndroidManifest.xml
index 6740c169ff21..ad1326194a4d 100644
--- a/packages/services/PacProcessor/AndroidManifest.xml
+++ b/packages/services/PacProcessor/AndroidManifest.xml
@@ -5,7 +5,9 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
- android:label="@string/app_name">
+ android:label="@string/app_name"
+ android:defaultToDeviceProtectedStorage="true"
+ android:directBootAware="true">
<service android:name=".PacService"
android:exported="true">
diff --git a/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl b/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
index 4e54aba5c3bf..1bbc90d604f9 100644
--- a/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
+++ b/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
@@ -21,7 +21,4 @@ interface IProxyService
String resolvePacFile(String host, String url);
oneway void setPacFile(String scriptContents);
-
- oneway void startPacSystem();
- oneway void stopPacSystem();
}
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
index 7aea721617b9..5a7de9f70b49 100644
--- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
@@ -88,15 +88,5 @@ public class PacService extends Service {
}
mLibpac.setCurrentProxyScript(script);
}
-
- @Override
- public void startPacSystem() throws RemoteException {
- //TODO: remove
- }
-
- @Override
- public void stopPacSystem() throws RemoteException {
- //TODO: remove
- }
}
}
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index a734df7fc5b4..5c3f2d83fb00 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -18,10 +18,10 @@ LOCAL_SHARED_LIBRARIES := \
libjnigraphics
LOCAL_HEADER_LIBRARIES := \
+ jni_headers \
libbase_headers
LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
frameworks/rs
LOCAL_CFLAGS += -Wno-unused-parameter
diff --git a/services/Android.bp b/services/Android.bp
index 00676e332656..f0144ac1c695 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -125,7 +125,6 @@ droidstubs {
" --hide InternalClasses" + // com.android.* classes are okay in this interface
// TODO: remove the --hide options below
" --hide-package com.google.android.startop.iorap" +
- " --hide ReferencesHidden" +
" --hide DeprecationMismatch" +
" --hide HiddenTypedefConstant",
visibility: ["//visibility:private"],
@@ -155,7 +154,14 @@ droidstubs {
java_library {
name: "android_system_server_stubs_current",
+ defaults: ["android_stubs_dists_default"],
srcs: [":services-stubs.sources"],
installable: false,
static_libs: ["android_module_lib_stubs_current"],
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
+ dist: {
+ dir: "apistubs/android/system-server",
+ },
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index c82dff22e328..9ad808a685a6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -79,6 +79,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -89,6 +90,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.Set;
/**
@@ -205,7 +207,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
/**
* @return The magnification controller
*/
- @NonNull MagnificationController getMagnificationController();
+ @NonNull
+ FullScreenMagnificationController getFullScreenMagnificationController();
/**
* Called back to notify system that the client has changed
@@ -295,6 +298,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mEventTypes = info.eventTypes;
mFeedbackType = info.feedbackType;
String[] packageNames = info.packageNames;
+ mPackageNames.clear();
if (packageNames != null) {
mPackageNames.addAll(Arrays.asList(packageNames));
}
@@ -830,7 +834,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
final long identity = Binder.clearCallingIdentity();
try {
- return mSystemSupport.getMagnificationController().getScale(displayId);
+ return mSystemSupport.getFullScreenMagnificationController().getScale(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -843,8 +847,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
if (!hasRightsToCurrentUserLocked()) {
return region;
}
- MagnificationController magnificationController =
- mSystemSupport.getMagnificationController();
+ FullScreenMagnificationController magnificationController =
+ mSystemSupport.getFullScreenMagnificationController();
boolean registeredJustForThisCall =
registerMagnificationIfNeeded(displayId, magnificationController);
final long identity = Binder.clearCallingIdentity();
@@ -866,8 +870,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
if (!hasRightsToCurrentUserLocked()) {
return 0.0f;
}
- MagnificationController magnificationController =
- mSystemSupport.getMagnificationController();
+ FullScreenMagnificationController magnificationController =
+ mSystemSupport.getFullScreenMagnificationController();
boolean registeredJustForThisCall =
registerMagnificationIfNeeded(displayId, magnificationController);
final long identity = Binder.clearCallingIdentity();
@@ -888,8 +892,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
if (!hasRightsToCurrentUserLocked()) {
return 0.0f;
}
- MagnificationController magnificationController =
- mSystemSupport.getMagnificationController();
+ FullScreenMagnificationController magnificationController =
+ mSystemSupport.getFullScreenMagnificationController();
boolean registeredJustForThisCall =
registerMagnificationIfNeeded(displayId, magnificationController);
final long identity = Binder.clearCallingIdentity();
@@ -905,7 +909,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
private boolean registerMagnificationIfNeeded(int displayId,
- MagnificationController magnificationController) {
+ FullScreenMagnificationController magnificationController) {
if (!magnificationController.isRegistered(displayId)
&& mSecurityPolicy.canControlMagnification(this)) {
magnificationController.register(displayId);
@@ -926,8 +930,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
final long identity = Binder.clearCallingIdentity();
try {
- MagnificationController magnificationController =
- mSystemSupport.getMagnificationController();
+ FullScreenMagnificationController magnificationController =
+ mSystemSupport.getFullScreenMagnificationController();
return (magnificationController.reset(displayId, animate)
|| !magnificationController.isMagnifying(displayId));
} finally {
@@ -947,8 +951,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
final long identity = Binder.clearCallingIdentity();
try {
- MagnificationController magnificationController =
- mSystemSupport.getMagnificationController();
+ FullScreenMagnificationController magnificationController =
+ mSystemSupport.getFullScreenMagnificationController();
if (!magnificationController.isRegistered(displayId)) {
magnificationController.register(displayId);
}
@@ -1175,7 +1179,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
/* ignore */
}
if (mService != null) {
- mService.unlinkToDeath(this, 0);
+ try {
+ mService.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Slog.e(LOG_TAG, "Failed unregistering death link");
+ }
mService = null;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 2c43b15883b3..8b40f610b4b3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -34,6 +34,7 @@ import android.view.accessibility.AccessibilityEvent;
import com.android.server.LocalServices;
import com.android.server.accessibility.gestures.TouchExplorer;
+import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
import com.android.server.policy.WindowManagerPolicy;
@@ -533,12 +534,13 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
& FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
MagnificationGestureHandler magnificationGestureHandler;
if (mAms.getMagnificationMode(displayId)
- == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW && triggerable) {
+ == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
magnificationGestureHandler = new WindowMagnificationGestureHandler(displayContext,
- mAms.getWindowMagnificationMgr(), mAms::onMagnificationScaleChanged, displayId);
+ mAms.getWindowMagnificationMgr(), mAms::onMagnificationScaleChanged,
+ detectControlGestures, triggerable, displayId);
} else {
magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext,
- mAms.getMagnificationController(), mAms::onMagnificationScaleChanged,
+ mAms.getFullScreenMagnificationController(), mAms::onMagnificationScaleChanged,
detectControlGestures, triggerable, displayId);
}
return magnificationGestureHandler;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9f9ceb0d43d8..2cd4c6939fa9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -56,6 +56,8 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.IFingerprintService;
@@ -116,6 +118,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -193,6 +196,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final SimpleStringSplitter mStringColonSplitter =
new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
+ private final Rect mTempRect = new Rect();
+ private final Rect mTempRect1 = new Rect();
+
private final PackageManager mPackageManager;
private final PowerManager mPowerManager;
@@ -212,7 +218,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// Lazily initialized - access through getSystemActionPerfomer()
private SystemActionPerformer mSystemActionPerformer;
- private MagnificationController mMagnificationController;
+ private FullScreenMagnificationController mFullScreenMagnificationController;
private InteractionBridge mInteractionBridge;
@@ -250,6 +256,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
//TODO: Remove this hack
private boolean mInitialized;
+ private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
private AccessibilityUserState getCurrentUserStateLocked() {
@@ -1085,6 +1092,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
/**
+ * Gets a point within the accessibility focused node where we can send down
+ * and up events to perform a click.
+ *
+ * @param outPoint The click point to populate.
+ * @return Whether accessibility a click point was found and set.
+ */
+ // TODO: (multi-display) Make sure this works for multiple displays.
+ public boolean getAccessibilityFocusClickPointInScreen(Point outPoint) {
+ return getInteractionBridge().getAccessibilityFocusClickPointInScreenNotLocked(outPoint);
+ }
+
+ /**
* Perform an accessibility action on the view that currently has accessibility focus.
* Has no effect if no item has accessibility focus, if the item with accessibility
* focus does not expose the specified action, or if the action fails.
@@ -1098,6 +1117,32 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return getInteractionBridge().performActionOnAccessibilityFocusedItemNotLocked(action);
}
+ /**
+ * Returns true if accessibility focus is confined to the active window.
+ */
+ public boolean accessibilityFocusOnlyInActiveWindow() {
+ synchronized (mLock) {
+ return mA11yWindowManager.isTrackingWindowsLocked();
+ }
+ }
+
+ /**
+ * Gets the bounds of a window.
+ *
+ * @param outBounds The output to which to write the bounds.
+ */
+ boolean getWindowBounds(int windowId, Rect outBounds) {
+ IBinder token;
+ synchronized (mLock) {
+ token = getWindowToken(windowId, mCurrentUserId);
+ }
+ mWindowManagerService.getWindowFrame(token, outBounds);
+ if (!outBounds.isEmpty()) {
+ return true;
+ }
+ return false;
+ }
+
public int getActiveWindowId() {
return mA11yWindowManager.getActiveWindowId(mCurrentUserId);
}
@@ -1876,9 +1921,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
for (int i = 0; !observingWindows && (i < boundServiceCount); i++) {
AccessibilityServiceConnection boundService = boundServices.get(i);
if (boundService.canRetrieveInteractiveWindowsLocked()) {
+ userState.setAccessibilityFocusOnlyInActiveWindow(false);
observingWindows = true;
}
}
+ userState.setAccessibilityFocusOnlyInActiveWindow(true);
// Gets all valid displays and start tracking windows of each display if there is at least
// one bound service that can retrieve window content.
@@ -2191,13 +2238,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
- if (mMagnificationController != null) {
- mMagnificationController.setUserId(userState.mUserId);
+ if (mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.setUserId(userState.mUserId);
}
if (mUiAutomationManager.suppressingAccessibilityServicesLocked()
- && mMagnificationController != null) {
- mMagnificationController.unregisterAll();
+ && mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.unregisterAll();
return;
}
@@ -2209,7 +2256,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|| userState.isShortcutMagnificationEnabledLocked()) {
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
- getMagnificationController().register(display.getDisplayId());
+ getFullScreenMagnificationController().register(display.getDisplayId());
}
return;
}
@@ -2219,9 +2266,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final Display display = displays.get(i);
final int displayId = display.getDisplayId();
if (userHasListeningMagnificationServicesLocked(userState, displayId)) {
- getMagnificationController().register(displayId);
- } else if (mMagnificationController != null) {
- mMagnificationController.unregister(displayId);
+ getFullScreenMagnificationController().register(displayId);
+ } else if (mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.unregister(displayId);
}
}
}
@@ -2568,7 +2615,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
// In case user assigned magnification to the given shortcut.
if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) {
- final boolean enabled = !getMagnificationController().isMagnifying(displayId);
+ final boolean enabled = !getFullScreenMagnificationController().isMagnifying(displayId);
logAccessibilityShortcutActivated(MAGNIFICATION_COMPONENT_NAME, shortcutType, enabled);
sendAccessibilityButtonToInputFilter(displayId);
return;
@@ -2928,13 +2975,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
- public MagnificationController getMagnificationController() {
+ public FullScreenMagnificationController getFullScreenMagnificationController() {
synchronized (mLock) {
- if (mMagnificationController == null) {
- mMagnificationController = new MagnificationController(mContext, this, mLock);
- mMagnificationController.setUserId(mCurrentUserId);
+ if (mFullScreenMagnificationController == null) {
+ mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
+ this, mLock);
+ mFullScreenMagnificationController.setUserId(mCurrentUserId);
}
- return mMagnificationController;
+ return mFullScreenMagnificationController;
}
}
@@ -2995,6 +3043,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
/**
+ * Gets a point within the accessibility focused node where we can send down and up events
+ * to perform a click.
+ *
+ * @param outPoint The click point to populate.
+ * @return Whether accessibility a click point was found and set.
+ */
+ // TODO: (multi-display) Make sure this works for multiple displays.
+ boolean getAccessibilityFocusClickPointInScreen(Point outPoint) {
+ return getInteractionBridge()
+ .getAccessibilityFocusClickPointInScreenNotLocked(outPoint);
+ }
+
+ /**
* Perform an accessibility action on the view that currently has accessibility focus.
* Has no effect if no item has accessibility focus, if the item with accessibility
* focus does not expose the specified action, or if the action fails.
@@ -3012,6 +3073,43 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return focus.performAction(action.getId());
}
+ public boolean getAccessibilityFocusClickPointInScreenNotLocked(Point outPoint) {
+ AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked();
+ if (focus == null) {
+ return false;
+ }
+
+ synchronized (mLock) {
+ Rect boundsInScreen = mTempRect;
+ focus.getBoundsInScreen(boundsInScreen);
+
+ // Apply magnification if needed.
+ MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
+ if (spec != null && !spec.isNop()) {
+ boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY);
+ boundsInScreen.scale(1 / spec.scale);
+ }
+
+ // Clip to the window bounds.
+ Rect windowBounds = mTempRect1;
+ getWindowBounds(focus.getWindowId(), windowBounds);
+ if (!boundsInScreen.intersect(windowBounds)) {
+ return false;
+ }
+
+ // Clip to the screen bounds.
+ Point screenSize = mTempPoint;
+ mDefaultDisplay.getRealSize(screenSize);
+ if (!boundsInScreen.intersect(0, 0, screenSize.x, screenSize.y)) {
+ return false;
+ }
+
+ outPoint.set(boundsInScreen.centerX(), boundsInScreen.centerY());
+ }
+
+ return true;
+ }
+
private AccessibilityNodeInfo getAccessibilityFocusNotLocked() {
final int focusedWindowId;
synchronized (mLock) {
@@ -3126,8 +3224,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
}
- if (mMagnificationController != null) {
- mMagnificationController.onDisplayRemoved(displayId);
+ if (mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.onDisplayRemoved(displayId);
}
mA11yWindowManager.stopTrackingWindows(displayId);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index fea2e7b841e0..e48d11d17f40 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -120,7 +120,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.removeServiceLocked(this);
- mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
+ mSystemSupport.getFullScreenMagnificationController().resetAllIfNeeded(mId);
mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1,
userState.mUserId);
resetLocked();
@@ -312,7 +312,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
userState.serviceDisconnectedLocked(this);
}
resetLocked();
- mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
+ mSystemSupport.getFullScreenMagnificationController().resetAllIfNeeded(mId);
mSystemSupport.onClientChangeLocked(false);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index e54b0f9fb413..f865aa7d6e37 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -105,6 +105,7 @@ class AccessibilityUserState {
private boolean mIsDisplayMagnificationEnabled;
private boolean mIsFilterKeyEventsEnabled;
private boolean mIsPerformGesturesEnabled;
+ private boolean mAccessibilityFocusOnlyInActiveWindow;
private boolean mIsTextHighContrastEnabled;
private boolean mIsTouchExplorationEnabled;
private boolean mServiceHandlesDoubleTap;
@@ -742,6 +743,13 @@ class AccessibilityUserState {
mIsPerformGesturesEnabled = enabled;
}
+ public boolean isAccessibilityFocusOnlyInActiveWindow() {
+ return mAccessibilityFocusOnlyInActiveWindow;
+ }
+
+ public void setAccessibilityFocusOnlyInActiveWindow(boolean enabled) {
+ mAccessibilityFocusOnlyInActiveWindow = enabled;
+ }
public ComponentName getServiceChangingSoftKeyboardModeLocked() {
return mServiceChangingSoftKeyboardMode;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 468e93a8f683..669bb24e0e77 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -747,6 +747,13 @@ public class AccessibilityWindowManager {
* Dumps all {@link AccessibilityWindowInfo}s here.
*/
void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ pw.append("Global Info [ ");
+ pw.println("Top focused display Id = " + mTopFocusedDisplayId);
+ pw.println(" Active Window Id = " + mActiveWindowId);
+ pw.println(" Top Focused Window Id = " + mTopFocusedWindowId);
+ pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId
+ + " ]");
+ pw.println();
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int j = 0; j < windowCount; j++) {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index 667364c9c901..070626be9f80 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -21,8 +21,11 @@ import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_I
import static com.android.server.accessibility.gestures.TouchState.MAX_POINTER_COUNT;
import android.content.Context;
+import android.graphics.Point;
import android.util.Slog;
import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -37,19 +40,27 @@ import com.android.server.policy.WindowManagerPolicy;
*/
class EventDispatcher {
private static final String LOG_TAG = "EventDispatcher";
+ private static final int CLICK_LOCATION_NONE = 0;
+ private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
+ private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2;
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 long pressing pointer id if coordinate remapping is needed for double tap and hold
+ private int mLongPressingPointerId = -1;
+
+ // The long pressing pointer X if coordinate remapping is needed for double tap and hold.
+ private int mLongPressingPointerDeltaX;
+
+ // The long pressing pointer Y if coordinate remapping is needed for double tap and hold.
+ private int mLongPressingPointerDeltaY;
+
+ // Temporary point to avoid instantiation.
+ private final Point mTempPoint = new Point();
- // The last injected hover event.
- private MotionEvent mLastInjectedHoverEvent;
private TouchState mState;
EventDispatcher(
@@ -98,8 +109,18 @@ class EventDispatcher {
if (action == MotionEvent.ACTION_DOWN) {
event.setDownTime(event.getEventTime());
} else {
- event.setDownTime(getLastInjectedDownEventTime());
+ event.setDownTime(mState.getLastInjectedDownEventTime());
+ }
+ // If the user is long pressing but the long pressing pointer
+ // was not exactly over the accessibility focused item we need
+ // to remap the location of that pointer so the user does not
+ // have to explicitly touch explore something to be able to
+ // long press it, or even worse to avoid the user long pressing
+ // on the wrong item since click and long press behave differently.
+ if (mLongPressingPointerId >= 0) {
+ event = offsetEvent(event, -mLongPressingPointerDeltaX, -mLongPressingPointerDeltaY);
}
+
if (DEBUG) {
Slog.d(
LOG_TAG,
@@ -116,7 +137,7 @@ class EventDispatcher {
} else {
Slog.e(LOG_TAG, "Error sending event: no receiver specified.");
}
- updateState(event);
+ mState.onInjectedMotionEvent(event);
if (event != prototype) {
event.recycle();
@@ -145,87 +166,15 @@ class EventDispatcher {
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(Integer.bitCount(mState.getInjectedPointersDown()));
builder.append(" [ ");
for (int i = 0; i < MAX_POINTER_COUNT; i++) {
- if ((mInjectedPointersDown & i) != 0) {
+ if (mState.isInjectedPointerDown(i)) {
builder.append(i);
builder.append(" ");
}
@@ -236,6 +185,48 @@ class EventDispatcher {
}
/**
+ * /** Offsets all pointers in the given event by adding the specified X and Y offsets.
+ *
+ * @param event The event to offset.
+ * @param offsetX The X offset.
+ * @param offsetY The Y offset.
+ * @return An event with the offset pointers or the original event if both offsets are zero.
+ */
+ private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) {
+ if (offsetX == 0 && offsetY == 0) {
+ return event;
+ }
+ final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
+ final int pointerCount = event.getPointerCount();
+ PointerProperties[] props = PointerProperties.createArray(pointerCount);
+ PointerCoords[] coords = PointerCoords.createArray(pointerCount);
+ for (int i = 0; i < pointerCount; i++) {
+ event.getPointerProperties(i, props[i]);
+ event.getPointerCoords(i, coords[i]);
+ if (i == remappedIndex) {
+ coords[i].x += offsetX;
+ coords[i].y += offsetY;
+ }
+ }
+ return MotionEvent.obtain(
+ event.getDownTime(),
+ event.getEventTime(),
+ event.getAction(),
+ event.getPointerCount(),
+ props,
+ coords,
+ event.getMetaState(),
+ event.getButtonState(),
+ 1.0f,
+ 1.0f,
+ event.getDeviceId(),
+ event.getEdgeFlags(),
+ event.getSource(),
+ event.getDisplayId(),
+ event.getFlags());
+ }
+
+ /**
* Computes the action for an injected event based on a masked action and a pointer index.
*
* @param actionMasked The masked action.
@@ -247,7 +238,7 @@ class EventDispatcher {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
// Compute the action based on how many down pointers are injected.
- if (getInjectedPointerDownCount() == 0) {
+ if (mState.getInjectedPointerDownCount() == 0) {
return MotionEvent.ACTION_DOWN;
} else {
return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
@@ -255,7 +246,7 @@ class EventDispatcher {
}
case MotionEvent.ACTION_POINTER_UP:
// Compute the action based on how many down pointers are injected.
- if (getInjectedPointerDownCount() == 1) {
+ if (mState.getInjectedPointerDownCount() == 1) {
return MotionEvent.ACTION_UP;
} else {
return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
@@ -280,7 +271,7 @@ class EventDispatcher {
for (int i = 0; i < pointerCount; i++) {
final int pointerId = prototype.getPointerId(i);
// Do not send event for already delivered pointers.
- if (!isInjectedPointerDown(pointerId)) {
+ if (!mState.isInjectedPointerDown(pointerId)) {
pointerIdBits |= (1 << pointerId);
final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
sendMotionEvent(
@@ -306,7 +297,7 @@ class EventDispatcher {
for (int i = 0; i < pointerCount; i++) {
final int pointerId = prototype.getPointerId(i);
// Skip non injected down pointers.
- if (!isInjectedPointerDown(pointerId)) {
+ if (!mState.isInjectedPointerDown(pointerId)) {
continue;
}
final int action = computeInjectionAction(MotionEvent.ACTION_POINTER_UP, i);
@@ -315,4 +306,103 @@ class EventDispatcher {
pointerIdBits &= ~(1 << pointerId);
}
}
+
+ public boolean longPressWithTouchEvents(MotionEvent event, int policyFlags) {
+ final int pointerIndex = event.getActionIndex();
+ final int pointerId = event.getPointerId(pointerIndex);
+ Point clickLocation = mTempPoint;
+ final int result = computeClickLocation(clickLocation);
+ if (result == CLICK_LOCATION_NONE) {
+ return false;
+ }
+ mLongPressingPointerId = pointerId;
+ mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
+ mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
+ sendDownForAllNotInjectedPointers(event, policyFlags);
+ return true;
+ }
+
+ void clear() {
+ mLongPressingPointerId = -1;
+ mLongPressingPointerDeltaX = 0;
+ mLongPressingPointerDeltaY = 0;
+ }
+
+ public void clickWithTouchEvents(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ final int pointerIndex = event.getActionIndex();
+ final int pointerId = event.getPointerId(pointerIndex);
+ Point clickLocation = mTempPoint;
+ final int result = computeClickLocation(clickLocation);
+ if (result == CLICK_LOCATION_NONE) {
+ Slog.e(LOG_TAG, "Unable to compute click location.");
+ // We can't send a click to no location, but the gesture was still
+ // consumed.
+ return;
+ }
+ // Do the click.
+ PointerProperties[] properties = new PointerProperties[1];
+ properties[0] = new PointerProperties();
+ event.getPointerProperties(pointerIndex, properties[0]);
+ PointerCoords[] coords = new PointerCoords[1];
+ coords[0] = new PointerCoords();
+ coords[0].x = clickLocation.x;
+ coords[0].y = clickLocation.y;
+ MotionEvent clickEvent =
+ MotionEvent.obtain(
+ event.getDownTime(),
+ event.getEventTime(),
+ MotionEvent.ACTION_DOWN,
+ 1,
+ properties,
+ coords,
+ 0,
+ 0,
+ 1.0f,
+ 1.0f,
+ event.getDeviceId(),
+ 0,
+ event.getSource(),
+ event.getDisplayId(),
+ event.getFlags());
+ final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
+ sendActionDownAndUp(clickEvent, rawEvent, policyFlags, targetAccessibilityFocus);
+ clickEvent.recycle();
+ }
+
+ private int computeClickLocation(Point outLocation) {
+ if (mState.getLastInjectedHoverEventForClick() != null) {
+ final int lastExplorePointerIndex =
+ mState.getLastInjectedHoverEventForClick().getActionIndex();
+ outLocation.x =
+ (int) mState.getLastInjectedHoverEventForClick().getX(lastExplorePointerIndex);
+ outLocation.y =
+ (int) mState.getLastInjectedHoverEventForClick().getY(lastExplorePointerIndex);
+ if (!mAms.accessibilityFocusOnlyInActiveWindow()
+ || mState.getLastTouchedWindowId() == mAms.getActiveWindowId()) {
+ if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
+ return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
+ } else {
+ return CLICK_LOCATION_LAST_TOUCH_EXPLORED;
+ }
+ }
+ }
+ if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
+ return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
+ }
+ return CLICK_LOCATION_NONE;
+ }
+
+ private void sendActionDownAndUp(
+ MotionEvent prototype,
+ MotionEvent rawEvent,
+ int policyFlags,
+ boolean targetAccessibilityFocus) {
+ // Tap with the pointer that last explored.
+ final int pointerId = prototype.getPointerId(prototype.getActionIndex());
+ final int pointerIdBits = (1 << pointerId);
+ prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
+ sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+ prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
+ sendMotionEvent(prototype, MotionEvent.ACTION_UP, rawEvent, pointerIdBits, policyFlags);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 6d0f069e51ac..e9c70c60a322 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -104,6 +104,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
mHandler = new Handler(context.getMainLooper());
mListener = listener;
mState = state;
+ mMultiFingerGesturesEnabled = false;
// Set up gestures.
// Start with double tap.
mGestures.add(new MultiTap(context, 2, GESTURE_DOUBLE_TAP, this));
@@ -247,7 +248,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
* and hold is dispatched via onGestureCompleted. Otherwise, this method is called when the
* user has performed a double tap and then held down the second tap.
*/
- void onDoubleTapAndHold();
+ void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags);
/**
* When FLAG_SERVICE_HANDLES_DOUBLE_TAP is enabled, this method is not called; double-tap is
@@ -256,7 +257,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
*
* @return true if the event is consumed, else false
*/
- boolean onDoubleTap();
+ boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags);
/**
* Called when the system has decided the event stream is a potential gesture.
@@ -322,7 +323,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
new AccessibilityGestureEvent(gestureId, event.getDisplayId());
mListener.onGestureCompleted(gestureEvent);
} else {
- mListener.onDoubleTap();
+ mListener.onDoubleTap(event, rawEvent, policyFlags);
}
clear();
break;
@@ -332,7 +333,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
new AccessibilityGestureEvent(gestureId, event.getDisplayId());
mListener.onGestureCompleted(gestureEvent);
} else {
- mListener.onDoubleTapAndHold();
+ mListener.onDoubleTapAndHold(event, rawEvent, policyFlags);
}
clear();
break;
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 7b5180d7041b..696702fad730 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -39,7 +39,6 @@ import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_I
import android.accessibilityservice.AccessibilityGestureEvent;
import android.annotation.NonNull;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.Region;
import android.os.Handler;
import android.util.Slog;
@@ -133,8 +132,6 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Handle to the accessibility manager service.
private final AccessibilityManagerService mAms;
- // Temporary point to avoid instantiation.
- private final Point mTempPoint = new Point();
// Context in which this explorer operates.
private final Context mContext;
@@ -301,6 +298,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (eventType == TYPE_VIEW_HOVER_EXIT) {
sendsPendingA11yEventsIfNeeded();
}
+ mState.onReceivedAccessibilityEvent(event);
super.onAccessibilityEvent(event);
}
@@ -332,16 +330,15 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
@Override
- public void onDoubleTapAndHold() {
- // Try to use the standard accessibility API to long click
- if (!mAms.performActionOnAccessibilityFocusedItem(
- AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)) {
- Slog.e(LOG_TAG, "ACTION_LONG_CLICK failed.");
+ public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
+ sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
+ mState.startDelegating();
}
}
@Override
- public boolean onDoubleTap() {
+ public boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
mAms.onTouchInteractionEnd();
// Remove pending event deliveries.
mSendHoverEnterAndMoveDelayed.cancel();
@@ -357,7 +354,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Try to use the standard accessibility API to click
if (!mAms.performActionOnAccessibilityFocusedItem(
AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)) {
- Slog.e(LOG_TAG, "ACTION_CLICK failed.");
+ Slog.e(LOG_TAG, "ACTION_CLICK failed. Dispatching motion events to simulate click.");
+
+ mDispatcher.clickWithTouchEvents(event, rawEvent, policyFlags);
+ return true;
}
return true;
}
@@ -819,6 +819,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Announce the end of a the touch interaction.
mAms.onTouchInteractionEnd();
+ mDispatcher.clear();
mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
} break;
@@ -851,7 +852,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
* @param policyFlags The policy flags associated with the event.
*/
private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
- MotionEvent event = mDispatcher.getLastInjectedHoverEvent();
+ MotionEvent event = mState.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() != ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
if (!mSendTouchExplorationEndDelayed.isPending()) {
@@ -873,7 +874,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
* @param policyFlags The policy flags associated with the event.
*/
private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
- MotionEvent event = mDispatcher.getLastInjectedHoverEvent();
+ MotionEvent event = mState.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() == ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
mDispatcher.sendMotionEvent(
@@ -1198,7 +1199,6 @@ public class TouchExplorer extends BaseEventStreamTransformation
+ ", mDetermineUserIntentTimeout: " + mDetermineUserIntentTimeout
+ ", mDoubleTapSlop: " + mDoubleTapSlop
+ ", mDraggingPointerId: " + mDraggingPointerId
- + ", mTempPoint: " + mTempPoint
+ " }";
}
}
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 d23dbbefd325..7a39bc29e8e5 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -75,6 +75,16 @@ public class TouchState {
private MotionEvent mLastReceivedEvent;
// The accompanying raw event without any transformations.
private MotionEvent mLastReceivedRawEvent;
+ // The id of the last touch explored window.
+ private int mLastTouchedWindowId;
+ // The last injected hover event.
+ private MotionEvent mLastInjectedHoverEvent;
+ // The last injected hover event used for performing clicks.
+ private MotionEvent mLastInjectedHoverEventForClick;
+ // The time of the last injected down.
+ private long mLastInjectedDownEventTime;
+ // Keep track of which pointers sent to the system are down.
+ private int mInjectedPointersDown;
public TouchState() {
mReceivedPointerTracker = new ReceivedPointerTracker();
@@ -88,7 +98,9 @@ public class TouchState {
mLastReceivedEvent.recycle();
mLastReceivedEvent = null;
}
+ mLastTouchedWindowId = -1;
mReceivedPointerTracker.clear();
+ mInjectedPointersDown = 0;
}
/**
@@ -107,6 +119,71 @@ public class TouchState {
mReceivedPointerTracker.onMotionEvent(rawEvent);
}
+ /**
+ * Processes an injected {@link MotionEvent} event.
+ *
+ * @param event The event to process.
+ */
+ void onInjectedMotionEvent(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:
+ if (mLastInjectedHoverEvent != null) {
+ mLastInjectedHoverEvent.recycle();
+ }
+ mLastInjectedHoverEvent = MotionEvent.obtain(event);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ if (mLastInjectedHoverEvent != null) {
+ mLastInjectedHoverEvent.recycle();
+ }
+ mLastInjectedHoverEvent = MotionEvent.obtain(event);
+ if (mLastInjectedHoverEventForClick != null) {
+ mLastInjectedHoverEventForClick.recycle();
+ }
+ mLastInjectedHoverEventForClick = MotionEvent.obtain(event);
+ break;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Injected pointer:\n" + toString());
+ }
+ }
+
+ /** Updates state in response to an accessibility event received from the outside. */
+ public void onReceivedAccessibilityEvent(AccessibilityEvent event) {
+ // If a new window opens or the accessibility focus moves we no longer
+ // want to click/long press on the last touch explored location.
+ switch (event.getEventType()) {
+ case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
+ case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+ if (mLastInjectedHoverEventForClick != null) {
+ mLastInjectedHoverEventForClick.recycle();
+ mLastInjectedHoverEventForClick = null;
+ }
+ mLastTouchedWindowId = -1;
+ break;
+ case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
+ case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
+ mLastTouchedWindowId = event.getWindowId();
+ break;
+ }
+ }
+
public void onInjectedAccessibilityEvent(int type) {
// The below state transitions go here because the related events are often sent on a
// delay.
@@ -236,6 +313,46 @@ public class TouchState {
return mLastReceivedEvent;
}
+ /** @return The the last injected hover event. */
+ public MotionEvent getLastInjectedHoverEvent() {
+ return mLastInjectedHoverEvent;
+ }
+
+ /** @return The time of the last injected down event. */
+ public long getLastInjectedDownEventTime() {
+ return mLastInjectedDownEventTime;
+ }
+
+ public int getLastTouchedWindowId() {
+ return mLastTouchedWindowId;
+ }
+
+ /** @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 used for a click. */
+ public MotionEvent getLastInjectedHoverEventForClick() {
+ return mLastInjectedHoverEventForClick;
+ }
+
/** 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/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index e784056d9e35..44c4bf4836a0 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package com.android.server.accessibility.magnification;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -42,6 +42,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.wm.WindowManagerInternal;
import java.util.Locale;
@@ -56,9 +57,9 @@ import java.util.Locale;
* magnification region. If a value is out of bounds, it will be adjusted to guarantee these
* constraints.
*/
-public class MagnificationController {
+public class FullScreenMagnificationController {
private static final boolean DEBUG = false;
- private static final String LOG_TAG = "MagnificationController";
+ private static final String LOG_TAG = "FullScreenMagnificationController";
public static final float MIN_SCALE = 1.0f;
public static final float MAX_SCALE = 8.0f;
@@ -140,11 +141,12 @@ public class MagnificationController {
/**
* Unregisters magnification callback from window manager. Callbacks to
- * {@link MagnificationController#unregisterCallbackLocked(int, boolean)} after
+ * {@link FullScreenMagnificationController#unregisterCallbackLocked(int, boolean)} after
* unregistered.
*
* @param delete true if this instance should be removed from the SparseArray in
- * MagnificationController after unregistered, for example, display removed.
+ * FullScreenMagnificationController after unregistered, for example,
+ * display removed.
*/
@GuardedBy("mLock")
void unregister(boolean delete) {
@@ -164,7 +166,8 @@ public class MagnificationController {
* called after animation finished.
*
* @param delete true if this instance should be removed from the SparseArray in
- * MagnificationController after unregistered, for example, display removed.
+ * FullScreenMagnificationController after unregistered, for example,
+ * display removed.
*/
@GuardedBy("mLock")
void unregisterPending(boolean delete) {
@@ -257,15 +260,17 @@ public class MagnificationController {
@Override
public void onRotationChanged(int rotation) {
// Treat as context change and reset
- final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded,
- MagnificationController.this, mDisplayId, true);
+ final Message m = PooledLambda.obtainMessage(
+ FullScreenMagnificationController::resetIfNeeded,
+ FullScreenMagnificationController.this, mDisplayId, true);
mControllerCtx.getHandler().sendMessage(m);
}
@Override
public void onUserContextChanged() {
- final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded,
- MagnificationController.this, mDisplayId, true);
+ final Message m = PooledLambda.obtainMessage(
+ FullScreenMagnificationController::resetIfNeeded,
+ FullScreenMagnificationController.this, mDisplayId, true);
mControllerCtx.getHandler().sendMessage(m);
}
@@ -554,25 +559,25 @@ public class MagnificationController {
float getMinOffsetXLocked() {
final float viewportWidth = mMagnificationBounds.width();
final float viewportLeft = mMagnificationBounds.left;
- return (viewportLeft + viewportWidth) -
- (viewportLeft + viewportWidth) * mCurrentMagnificationSpec.scale;
+ return (viewportLeft + viewportWidth)
+ - (viewportLeft + viewportWidth) * mCurrentMagnificationSpec.scale;
}
float getMaxOffsetXLocked() {
- return mMagnificationBounds.left -
- mMagnificationBounds.left * mCurrentMagnificationSpec.scale;
+ return mMagnificationBounds.left
+ - mMagnificationBounds.left * mCurrentMagnificationSpec.scale;
}
float getMinOffsetYLocked() {
final float viewportHeight = mMagnificationBounds.height();
final float viewportTop = mMagnificationBounds.top;
- return (viewportTop + viewportHeight) -
- (viewportTop + viewportHeight) * mCurrentMagnificationSpec.scale;
+ return (viewportTop + viewportHeight)
+ - (viewportTop + viewportHeight) * mCurrentMagnificationSpec.scale;
}
float getMaxOffsetYLocked() {
- return mMagnificationBounds.top -
- mMagnificationBounds.top * mCurrentMagnificationSpec.scale;
+ return mMagnificationBounds.top
+ - mMagnificationBounds.top * mCurrentMagnificationSpec.scale;
}
@Override
@@ -590,9 +595,9 @@ public class MagnificationController {
}
/**
- * MagnificationController Constructor
+ * FullScreenMagnificationController Constructor
*/
- public MagnificationController(@NonNull Context context,
+ public FullScreenMagnificationController(@NonNull Context context,
@NonNull AccessibilityManagerService ams, @NonNull Object lock) {
this(new ControllerContext(context, ams,
LocalServices.getService(WindowManagerInternal.class),
@@ -604,7 +609,7 @@ public class MagnificationController {
* Constructor for tests
*/
@VisibleForTesting
- public MagnificationController(@NonNull ControllerContext ctx, @NonNull Object lock) {
+ public FullScreenMagnificationController(@NonNull ControllerContext ctx, @NonNull Object lock) {
mControllerCtx = ctx;
mLock = lock;
mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
@@ -1088,7 +1093,7 @@ public class MagnificationController {
private void onScreenTurnedOff() {
final Message m = PooledLambda.obtainMessage(
- MagnificationController::resetAllIfNeeded, this, false);
+ FullScreenMagnificationController::resetAllIfNeeded, this, false);
mControllerCtx.getHandler().sendMessage(m);
}
@@ -1253,14 +1258,14 @@ public class MagnificationController {
synchronized (mLock) {
if (mEnabled) {
float fract = animation.getAnimatedFraction();
- mTmpMagnificationSpec.scale = mStartMagnificationSpec.scale +
- (mEndMagnificationSpec.scale - mStartMagnificationSpec.scale) * fract;
- mTmpMagnificationSpec.offsetX = mStartMagnificationSpec.offsetX +
- (mEndMagnificationSpec.offsetX - mStartMagnificationSpec.offsetX)
- * fract;
- mTmpMagnificationSpec.offsetY = mStartMagnificationSpec.offsetY +
- (mEndMagnificationSpec.offsetY - mStartMagnificationSpec.offsetY)
- * fract;
+ mTmpMagnificationSpec.scale = mStartMagnificationSpec.scale
+ + (mEndMagnificationSpec.scale - mStartMagnificationSpec.scale) * fract;
+ mTmpMagnificationSpec.offsetX = mStartMagnificationSpec.offsetX
+ + (mEndMagnificationSpec.offsetX - mStartMagnificationSpec.offsetX)
+ * fract;
+ mTmpMagnificationSpec.offsetY = mStartMagnificationSpec.offsetY
+ + (mEndMagnificationSpec.offsetY - mStartMagnificationSpec.offsetY)
+ * fract;
setMagnificationSpecLocked(mTmpMagnificationSpec);
}
}
@@ -1269,10 +1274,10 @@ public class MagnificationController {
private static class ScreenStateObserver extends BroadcastReceiver {
private final Context mContext;
- private final MagnificationController mController;
+ private final FullScreenMagnificationController mController;
private boolean mRegistered = false;
- public ScreenStateObserver(Context context, MagnificationController controller) {
+ ScreenStateObserver(Context context, FullScreenMagnificationController controller) {
mContext = context;
mController = controller;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 058a293529fb..d50e9d720861 100644
--- a/services/accessibility/java/com/android/server/accessibility/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package com.android.server.accessibility.magnification;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
@@ -59,8 +59,8 @@ import android.view.ViewConfiguration;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.gestures.GestureUtils;
-import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import java.util.ArrayDeque;
import java.util.Queue;
@@ -114,7 +114,7 @@ import java.util.Queue;
* 7. The magnification scale will be persisted in settings and in the cloud.
*/
@SuppressWarnings("WeakerAccess")
-class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler {
+public class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler {
private static final String LOG_TAG = "FullScreenMagnificationGestureHandler";
private static final boolean DEBUG_ALL = false;
@@ -127,9 +127,9 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
// to AccessibilityService.MagnificationController#setScale() has
// different scale range
private static final float MIN_SCALE = 2.0f;
- private static final float MAX_SCALE = MagnificationController.MAX_SCALE;
+ private static final float MAX_SCALE = FullScreenMagnificationController.MAX_SCALE;
- @VisibleForTesting final MagnificationController mMagnificationController;
+ @VisibleForTesting final FullScreenMagnificationController mFullScreenMagnificationController;
@VisibleForTesting final DelegatingState mDelegatingState;
@VisibleForTesting final DetectingState mDetectingState;
@@ -163,7 +163,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
/**
* @param context Context for resolving various magnification-related resources
- * @param magnificationController the {@link MagnificationController}
+ * @param fullScreenMagnificationController the {@link FullScreenMagnificationController}
*
* @param detectTripleTap {@code true} if this detector should detect and respond to triple-tap
* gestures for engaging and disengaging magnification,
@@ -173,9 +173,9 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
* {@code false} if it should ignore such triggers.
* @param displayId The logical display id.
*/
- FullScreenMagnificationGestureHandler(Context context,
- MagnificationController magnificationController,
- MagnificationGestureHandler.ScaleChangedListener listener,
+ public FullScreenMagnificationGestureHandler(Context context,
+ FullScreenMagnificationController fullScreenMagnificationController,
+ ScaleChangedListener listener,
boolean detectTripleTap,
boolean detectShortcutTrigger,
int displayId) {
@@ -185,7 +185,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
"FullScreenMagnificationGestureHandler(detectTripleTap = " + detectTripleTap
+ ", detectShortcutTrigger = " + detectShortcutTrigger + ")");
}
- mMagnificationController = magnificationController;
+ mFullScreenMagnificationController = fullScreenMagnificationController;
mDisplayId = displayId;
mDelegatingState = new DelegatingState();
@@ -265,7 +265,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
mScreenStateReceiver.unregister();
}
// Check if need to reset when MagnificationGestureHandler is the last magnifying service.
- mMagnificationController.resetAllIfNeeded(
+ mFullScreenMagnificationController.resetAllIfNeeded(
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
clearAndTransitionToStateDetecting();
}
@@ -273,7 +273,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
@Override
public void notifyShortcutTriggered() {
if (mDetectShortcutTrigger) {
- boolean wasMagnifying = mMagnificationController.resetIfNeeded(mDisplayId,
+ boolean wasMagnifying = mFullScreenMagnificationController.resetIfNeeded(mDisplayId,
/* animate */ true);
if (wasMagnifying) {
clearAndTransitionToStateDetecting();
@@ -424,7 +424,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
}
public void persistScaleAndTransitionTo(State state) {
- mMagnificationController.persistScale();
+ mFullScreenMagnificationController.persistScale();
clear();
transitionTo(state);
}
@@ -439,7 +439,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX
+ " scrollY: " + distanceY);
}
- mMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX,
+ mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX,
distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
return /* event consumed: */ true;
}
@@ -455,7 +455,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
mScaling = abs(deltaScale) > mScalingThreshold;
return mScaling;
}
- final float initialScale = mMagnificationController.getScale(mDisplayId);
+ final float initialScale = mFullScreenMagnificationController.getScale(mDisplayId);
final float targetScale = initialScale * detector.getScaleFactor();
// Don't allow a gesture to move the user further outside the
@@ -477,7 +477,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
final float pivotX = detector.getFocusX();
final float pivotY = detector.getFocusY();
if (DEBUG_PANNING_SCALING) Slog.i(LOG_TAG, "Scaled content to: " + scale + "x");
- mMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
+ mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
mListener.onMagnificationScaleChanged(mDisplayId, getMode());
return /* handled: */ true;
@@ -537,9 +537,9 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
}
final float eventX = event.getX();
final float eventY = event.getY();
- if (mMagnificationController.magnificationRegionContains(
+ if (mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, eventX, eventY)) {
- mMagnificationController.setCenter(mDisplayId, eventX, eventY,
+ mFullScreenMagnificationController.setCenter(mDisplayId, eventX, eventY,
/* animate */ mLastMoveOutsideMagnifiedRegion,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
mLastMoveOutsideMagnifiedRegion = false;
@@ -687,7 +687,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
mLastDetectingDownEventTime = event.getDownTime();
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
- if (!mMagnificationController.magnificationRegionContains(
+ if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
@@ -705,7 +705,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
// If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
// to ensure reachability of
// STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
- || mMagnificationController.isMagnifying(mDisplayId)) {
+ || mFullScreenMagnificationController.isMagnifying(mDisplayId)) {
afterMultiTapTimeoutTransitionToDelegatingState();
@@ -717,7 +717,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
}
break;
case ACTION_POINTER_DOWN: {
- if (mMagnificationController.isMagnifying(mDisplayId)
+ if (mFullScreenMagnificationController.isMagnifying(mDisplayId)
&& event.getPointerCount() == 2) {
storeSecondPointerDownLocation(event);
mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
@@ -760,7 +760,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
- if (!mMagnificationController.magnificationRegionContains(
+ if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
@@ -811,7 +811,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
// Only log the triple tap event, use numTaps to filter.
if (multitapTriggered && numTaps > 2) {
- final boolean enabled = mMagnificationController.isMagnifying(mDisplayId);
+ final boolean enabled = mFullScreenMagnificationController.isMagnifying(mDisplayId);
logMagnificationTripleTap(enabled);
}
return multitapTriggered;
@@ -947,7 +947,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
clear();
// Toggle zoom
- if (mMagnificationController.isMagnifying(mDisplayId)) {
+ if (mFullScreenMagnificationController.isMagnifying(mDisplayId)) {
zoomOff();
} else {
zoomOn(up.getX(), up.getY());
@@ -955,7 +955,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
}
private boolean isMagnifying() {
- return mMagnificationController.isMagnifying(mDisplayId);
+ return mFullScreenMagnificationController.isMagnifying(mDisplayId);
}
void transitionToViewportDraggingStateAndClear(MotionEvent down) {
@@ -964,7 +964,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
clear();
mViewportDraggingState.mZoomedInBeforeDrag =
- mMagnificationController.isMagnifying(mDisplayId);
+ mFullScreenMagnificationController.isMagnifying(mDisplayId);
// Triple tap and hold also belongs to triple tap event.
final boolean enabled = !mViewportDraggingState.mZoomedInBeforeDrag;
@@ -995,7 +995,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "setShortcutTriggered(" + state + ")");
mShortcutTriggered = state;
- mMagnificationController.setForceShowMagnifiableBounds(mDisplayId, state);
+ mFullScreenMagnificationController.setForceShowMagnifiableBounds(mDisplayId, state);
}
/**
@@ -1028,9 +1028,9 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOn(" + centerX + ", " + centerY + ")");
final float scale = MathUtils.constrain(
- mMagnificationController.getPersistedScale(),
+ mFullScreenMagnificationController.getPersistedScale(),
MIN_SCALE, MAX_SCALE);
- mMagnificationController.setScaleAndCenter(mDisplayId,
+ mFullScreenMagnificationController.setScaleAndCenter(mDisplayId,
scale, centerX, centerY,
/* animate */ true,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -1038,7 +1038,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
private void zoomOff() {
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()");
- mMagnificationController.reset(mDisplayId, /* animate */ true);
+ mFullScreenMagnificationController.reset(mDisplayId, /* animate */ true);
}
private static MotionEvent recycleAndNullify(@Nullable MotionEvent event) {
@@ -1059,7 +1059,7 @@ class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler
+ ", mDetectShortcutTrigger=" + mDetectShortcutTrigger
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
- + ", mMagnificationController=" + mMagnificationController
+ + ", mMagnificationController=" + mFullScreenMagnificationController
+ ", mDisplayId=" + mDisplayId
+ '}';
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index f38c38ff7d26..d6f53d2c225c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -23,7 +23,7 @@ import com.android.server.accessibility.BaseEventStreamTransformation;
*/
public abstract class MagnificationGestureHandler extends BaseEventStreamTransformation {
- protected final MagnificationGestureHandler.ScaleChangedListener mListener;
+ protected final ScaleChangedListener mListener;
protected MagnificationGestureHandler(ScaleChangedListener listener) {
mListener = listener;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java
index 6d16718f39ea..7a4d9e34b657 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java
@@ -37,6 +37,8 @@ class MagnificationGestureMatcher {
public static final int GESTURE_SWIPE = GESTURE_BASE + 2;
public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3;
public static final int GESTURE_SINGLE_TAP_AND_HOLD = GESTURE_BASE + 4;
+ public static final int GESTURE_TRIPLE_TAP = GESTURE_BASE + 5;
+ public static final int GESTURE_TRIPLE_TAP_AND_HOLD = GESTURE_BASE + 6;
@IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = {
GESTURE_TWO_FINGER_DOWN,
@@ -61,6 +63,10 @@ class MagnificationGestureMatcher {
return "GESTURE_SINGLE_TAP";
case GESTURE_SINGLE_TAP_AND_HOLD:
return "GESTURE_SINGLE_TAP_AND_HOLD";
+ case GESTURE_TRIPLE_TAP:
+ return "GESTURE_TRIPLE_TAP";
+ case GESTURE_TRIPLE_TAP_AND_HOLD:
+ return "GESTURE_TRIPLE_TAP_AND_HOLD";
}
return "none";
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index bb696309e3cd..bd25f2bea881 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -44,9 +44,11 @@ import java.util.Queue;
* This class handles window magnification in response to touch events and shortcut.
*
* The behavior is as follows:
-
+ *
* <ol>
- * <li> Window magnification can be "toggled" by tapping shortcut. It is triggered via
+ * <li> 1. Toggle Window magnification by triple-tap gesture shortcut. It is triggered via
+ * {@link #onTripleTap(MotionEvent)}.
+ * <li> 2. Toggle Window magnification by tapping shortcut. It is triggered via
* {@link #notifyShortcutTriggered()}.
* <li> When the window magnifier is visible, pinching with any number of additional fingers
* would adjust the magnification scale .<strong>Note</strong> that this operation is valid only
@@ -72,7 +74,6 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
private static final float MAX_SCALE = WindowMagnificationManager.MAX_SCALE;
private final WindowMagnificationManager mWindowMagnificationMgr;
-
@VisibleForTesting
final DelegatingState mDelegatingState;
@VisibleForTesting
@@ -85,6 +86,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
@VisibleForTesting
State mPreviousState;
+ final boolean mDetectShortcutTrigger;
+
private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
private final int mDisplayId;
@@ -97,7 +100,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
*/
public WindowMagnificationGestureHandler(Context context,
WindowMagnificationManager windowMagnificationMgr,
- MagnificationGestureHandler.ScaleChangedListener listener, int displayId) {
+ ScaleChangedListener listener, boolean detectTripleTap,
+ boolean detectShortcutTrigger, int displayId) {
super(listener);
if (DEBUG_ALL) {
Slog.i(LOG_TAG,
@@ -105,12 +109,13 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
}
mWindowMagnificationMgr = windowMagnificationMgr;
+ mDetectShortcutTrigger = detectShortcutTrigger;
mDisplayId = displayId;
mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context,
(event, rawEvent, policyFlags) -> super.onMotionEvent(
event, rawEvent, policyFlags));
mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate);
- mDetectingState = new DetectingState(context);
+ mDetectingState = new DetectingState(context, detectTripleTap);
mObservePanningScalingState = new PanningScalingGestureState(
new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true,
new PanningScalingHandler.MagnificationDelegate() {
@@ -176,11 +181,10 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
if (DEBUG_ALL) {
Slog.i(LOG_TAG, "notifyShortcutTriggered():");
}
- if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
- disableWindowMagnifier();
- } else {
- enableWindowMagnifier(Float.NaN, Float.NaN);
+ if (!mDetectShortcutTrigger) {
+ return;
}
+ toggleMagnification(Float.NaN, Float.NaN);
}
@Override
@@ -206,6 +210,21 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, false);
}
+ private void toggleMagnification(float centerX, float centerY) {
+ if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
+ disableWindowMagnifier();
+ } else {
+ enableWindowMagnifier(centerX, centerY);
+ }
+ }
+
+ private void onTripleTap(MotionEvent up) {
+ if (DEBUG_DETECTING) {
+ Slog.i(LOG_TAG, "onTripleTap()");
+ }
+ toggleMagnification(up.getX(), up.getY());
+ }
+
void resetToDetectState() {
transitionTo(mDetectingState);
}
@@ -348,8 +367,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
/**
* This class handles motion events in a duration to determine if the user is going to
- * manipulate the window magnifier or want to interact with current UI. The rule of leaving
- * this state is as follows:
+ * manipulate the window magnifier or want to interact with current UI. The rule of leaving
+ * this state is as follows:
* <ol>
* <li> If {@link MagnificationGestureMatcher#GESTURE_TWO_FINGER_DOWN} is detected,
* {@link State} will be transited to {@link PanningScalingGestureState}.</li>
@@ -363,11 +382,28 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
private final MagnificationGesturesObserver mGesturesObserver;
- DetectingState(Context context) {
- mGesturesObserver = new MagnificationGesturesObserver(this, new SimpleSwipe(context),
- new MultiTap(context, 1, MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null),
- new MultiTapAndHold(context, 1,
- MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null),
+ /**
+ * {@code true} if this detector should detect and respond to triple-tap
+ * gestures for engaging and disengaging magnification,
+ * {@code false} if it should ignore such gestures
+ */
+ private final boolean mDetectTripleTap;
+
+ DetectingState(Context context, boolean detectTripleTap) {
+ mDetectTripleTap = detectTripleTap;
+ final MultiTap multiTap = new MultiTap(context, mDetectTripleTap ? 3 : 1,
+ mDetectTripleTap
+ ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
+ : MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null);
+ final MultiTapAndHold multiTapAndHold = new MultiTapAndHold(context,
+ mDetectTripleTap ? 3 : 1,
+ mDetectTripleTap
+ ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
+ : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null);
+ mGesturesObserver = new MagnificationGesturesObserver(this,
+ new SimpleSwipe(context),
+ multiTap,
+ multiTapAndHold,
new TwoFingersDown(context));
}
@@ -395,7 +431,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
@Override
public boolean shouldStopDetection(MotionEvent motionEvent) {
- return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId);
+ return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
+ && !mDetectTripleTap;
}
@Override
@@ -412,6 +449,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN
&& mWindowMagnificationMgr.pointersInWindow(mDisplayId, motionEvent) > 0) {
transitionTo(mObservePanningScalingState);
+ } else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
+ onTripleTap(motionEvent);
} else {
mMotionEventDispatcherDelegate.sendDelayedMotionEvents(delayedEventQueue,
lastDownEventTime);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index b5ea01a281a3..a08c2dd4292d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -17,7 +17,10 @@
package com.android.server.accessibility.magnification;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
@@ -34,7 +37,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
-import com.android.server.accessibility.MagnificationController;
import com.android.server.statusbar.StatusBarManagerInternal;
/**
@@ -50,20 +52,31 @@ public class WindowMagnificationManager implements
private static final String TAG = "WindowMagnificationMgr";
//Ensure the range has consistency with full screen.
- static final float MAX_SCALE = MagnificationController.MAX_SCALE;
- static final float MIN_SCALE = MagnificationController.MIN_SCALE;
+ static final float MAX_SCALE = FullScreenMagnificationController.MAX_SCALE;
+ static final float MIN_SCALE = FullScreenMagnificationController.MIN_SCALE;
- private final Object mLock = new Object();;
+ private final Object mLock = new Object();
private final Context mContext;
@VisibleForTesting
@GuardedBy("mLock")
- @Nullable WindowMagnificationConnectionWrapper mConnectionWrapper;
+ @Nullable
+ WindowMagnificationConnectionWrapper mConnectionWrapper;
@GuardedBy("mLock")
private ConnectionCallback mConnectionCallback;
@GuardedBy("mLock")
private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>();
private int mUserId;
+ @VisibleForTesting
+ protected final BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int displayId = context.getDisplayId();
+ removeMagnificationButton(displayId);
+ disableWindowMagnification(displayId);
+ }
+ };
+
public WindowMagnificationManager(Context context, int userId) {
mContext = context;
mUserId = userId;
@@ -134,8 +147,12 @@ public class WindowMagnificationManager implements
if (connect == isConnected()) {
return false;
}
- if (!connect) {
+ if (connect) {
+ final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mScreenStateReceiver, intentFilter);
+ } else {
disableAllWindowMagnifiers();
+ mContext.unregisterReceiver(mScreenStateReceiver);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
index 19248ca54611..c25dd37bc7d9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -31,9 +31,16 @@ import com.android.server.inputmethod.InputMethodManagerInternal;
import java.util.Optional;
import java.util.function.Consumer;
-
/**
- * Controls the interaction with the IME for the inline suggestion sessions.
+ * Controls the interaction with the IME for the {@link AutofillInlineSuggestionsRequestSession}s.
+ *
+ * <p>The class maintains the inline suggestion session with the autofill service. There is at most
+ * one active inline suggestion session at any given corresponding to one focused view.
+ * New sessions are created only when {@link #onCreateInlineSuggestionsRequestLocked} is called.</p>
+ *
+ * <p>The class manages the interaction between the {@link com.android.server.autofill.Session} and
+ * the inline suggestion session whenever inline suggestions can be provided. All calls to the
+ * inline suggestion session must be made through this controller.</p>
*/
final class AutofillInlineSessionController {
@NonNull
@@ -66,7 +73,6 @@ final class AutofillInlineSessionController {
mUiCallback = callback;
}
-
/**
* Requests the IME to create an {@link InlineSuggestionsRequest} for {@code autofillId}.
*
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 68eeb0a3ca2e..84fbe9a75a18 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -103,6 +103,8 @@ final class AutofillInlineSuggestionsRequestSession {
private boolean mDestroyed = false;
@GuardedBy("mLock")
private boolean mPreviousHasNonPinSuggestionShow;
+ @GuardedBy("mLock")
+ private boolean mImeSessionInvalidated = false;
AutofillInlineSuggestionsRequestSession(
@NonNull InputMethodManagerInternal inputMethodManagerInternal, int userId,
@@ -157,7 +159,7 @@ final class AutofillInlineSuggestionsRequestSession {
Slog.d(TAG,
"onInlineSuggestionsResponseLocked called for:" + inlineFillUi.getAutofillId());
}
- if (mImeRequest == null || mResponseCallback == null) {
+ if (mImeRequest == null || mResponseCallback == null || mImeSessionInvalidated) {
return false;
}
// TODO(b/151123764): each session should only correspond to one field.
@@ -191,6 +193,7 @@ final class AutofillInlineSuggestionsRequestSession {
if (mDestroyed) {
return;
}
+ mImeSessionInvalidated = false;
if (sDebug) Slog.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId);
mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId,
new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras),
@@ -291,6 +294,7 @@ final class AutofillInlineSuggestionsRequestSession {
return;
}
mImeRequestReceived = true;
+ mImeSessionInvalidated = false;
if (request != null && callback != null) {
mImeRequest = request;
@@ -346,6 +350,23 @@ final class AutofillInlineSuggestionsRequestSession {
}
}
+ /**
+ * Handles the IME session status received from the IME.
+ *
+ * <p> Should only be invoked in the {@link #mHandler} thread.
+ */
+ private void handleOnReceiveImeSessionInvalidated() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ mImeSessionInvalidated = true;
+ }
+ }
+
+ /**
+ * Internal implementation of {@link IInlineSuggestionsRequestCallback}.
+ */
private static final class InlineSuggestionsRequestCallbackImpl extends
IInlineSuggestionsRequestCallback.Stub {
@@ -433,6 +454,18 @@ final class AutofillInlineSuggestionsRequestSession {
session, false, false));
}
}
+
+ @BinderThread
+ @Override
+ public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+ if (sDebug) Slog.d(TAG, "onInlineSuggestionsSessionInvalidated() called.");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession
+ ::handleOnReceiveImeSessionInvalidated, session));
+ }
+ }
}
private static boolean match(@Nullable AutofillId autofillId,
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 9b3d075e3f2c..7ab4369b338a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -73,6 +73,7 @@ import android.service.autofill.FieldClassificationUserData;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
+import android.service.autofill.InlinePresentation;
import android.service.autofill.InternalSanitizer;
import android.service.autofill.InternalValidator;
import android.service.autofill.SaveInfo;
@@ -1437,7 +1438,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mClientState = newClientState;
}
final Dataset dataset = (Dataset) result;
- authenticatedResponse.getDatasets().set(datasetIdx, dataset);
+ final Dataset oldDataset = authenticatedResponse.getDatasets().get(datasetIdx);
+ if (!isPinnedDataset(oldDataset)) {
+ authenticatedResponse.getDatasets().set(datasetIdx, dataset);
+ }
autoFill(requestId, datasetIdx, dataset, false);
} else {
Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id "
@@ -1455,6 +1459,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ /**
+ * A dataset can potentially have multiple fields, and it's possible that some of the fields'
+ * has inline presentation and some don't. It's also possible that some of the fields'
+ * inline presentation is pinned and some isn't. So the concept of whether a dataset is
+ * pinned or not is ill-defined. Here we say a dataset is pinned if any of the field has a
+ * pinned inline presentation in the dataset. It's not ideal but hopefully it is sufficient
+ * for most of the cases.
+ */
+ private static boolean isPinnedDataset(@Nullable Dataset dataset) {
+ if (dataset != null && dataset.getFieldIds() != null) {
+ final int numOfFields = dataset.getFieldIds().size();
+ for (int i = 0; i < numOfFields; i++) {
+ final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(i);
+ if (inlinePresentation != null && inlinePresentation.isPinned()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@GuardedBy("mLock")
void setAuthenticationResultForAugmentedAutofillLocked(Bundle data, int authId) {
final Dataset dataset = (data == null) ? null :
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 9c21e8fe5e45..7c7e74285bf5 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,9 +1,12 @@
+# Bug component: 656484
+
+aabhinav@google.com
alsutton@google.com
-anniemeng@google.com
-brufino@google.com
bryanmawhinney@google.com
-ctate@google.com
-jorlow@google.com
+jstemmer@google.com
nathch@google.com
+niagra@google.com
+niamhfw@google.com
+philippov@google.com
rthakohov@google.com
-
+tobiast@google.com
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b13bef2de151..c839ce94bd64 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -24,6 +24,7 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
+import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -1338,14 +1339,15 @@ public class BackupManagerService extends IBackupManager.Stub {
if (!isUserReadyForBackup(userId)) {
return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
}
- return requestBackup(userId, packages, observer, monitor, flags);
+ return requestBackup(userId, packages, observer, monitor, flags, OperationType.BACKUP);
}
@Override
public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags) throws RemoteException {
- return requestBackupForUser(binderGetCallingUserId(), packages,
- observer, monitor, flags);
+ IBackupManagerMonitor monitor, int flags, @OperationType int operationType)
+ throws RemoteException {
+ return requestBackup(binderGetCallingUserId(), packages,
+ observer, monitor, flags, operationType);
}
/**
@@ -1357,13 +1359,15 @@ public class BackupManagerService extends IBackupManager.Stub {
String[] packages,
IBackupObserver observer,
IBackupManagerMonitor monitor,
- int flags) {
+ int flags,
+ @OperationType int operationType) {
UserBackupManagerService userBackupManagerService =
getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
return userBackupManagerService == null
? BackupManager.ERROR_BACKUP_NOT_ALLOWED
- : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
+ : userBackupManagerService.requestBackup(packages, observer, monitor, flags,
+ operationType);
}
@Override
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 2241569afe18..e80a6d9e0907 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -33,7 +33,7 @@ import android.os.ParcelFileDescriptor;
import android.util.Slog;
import com.android.server.LocalServices;
-import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupEligibilityRules;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -134,10 +134,11 @@ public class PackageManagerBackupAgent extends BackupAgent {
init(packageMgr, packages, userId);
}
- public PackageManagerBackupAgent(PackageManager packageMgr, int userId) {
+ public PackageManagerBackupAgent(PackageManager packageMgr, int userId,
+ BackupEligibilityRules backupEligibilityRules) {
init(packageMgr, null, userId);
- evaluateStorablePackages();
+ evaluateStorablePackages(backupEligibilityRules);
}
private void init(PackageManager packageMgr, List<PackageInfo> packages, int userId) {
@@ -153,18 +154,19 @@ public class PackageManagerBackupAgent extends BackupAgent {
// We will need to refresh our understanding of what is eligible for
// backup periodically; this entry point serves that purpose.
- public void evaluateStorablePackages() {
- mAllPackages = getStorableApplications(mPackageManager, mUserId);
+ public void evaluateStorablePackages(BackupEligibilityRules backupEligibilityRules) {
+ mAllPackages = getStorableApplications(mPackageManager, mUserId, backupEligibilityRules);
}
/** Gets all packages installed on user {@code userId} eligible for backup. */
- public static List<PackageInfo> getStorableApplications(PackageManager pm, int userId) {
+ public static List<PackageInfo> getStorableApplications(PackageManager pm, int userId,
+ BackupEligibilityRules backupEligibilityRules) {
List<PackageInfo> pkgs =
pm.getInstalledPackagesAsUser(PackageManager.GET_SIGNING_CERTIFICATES, userId);
int N = pkgs.size();
for (int a = N-1; a >= 0; a--) {
PackageInfo pkg = pkgs.get(a);
- if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, userId)) {
+ if (!backupEligibilityRules.appIsEligibleForBackup(pkg.applicationInfo)) {
pkgs.remove(a);
}
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index b7f775b1f321..ff21a733223c 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -47,6 +47,7 @@ import android.app.IBackupAgent;
import android.app.PendingIntent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupManager;
+import android.app.backup.BackupManager.OperationType;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.FullBackup;
import android.app.backup.IBackupManager;
@@ -67,6 +68,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
@@ -126,7 +128,7 @@ import com.android.server.backup.restore.ActiveRestoreSession;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.android.server.backup.utils.BackupObserverUtils;
import com.android.server.backup.utils.SparseArrayUtils;
@@ -335,6 +337,7 @@ public class UserBackupManagerService {
private final BackupManagerConstants mConstants;
private final BackupWakeLock mWakelock;
private final BackupHandler mBackupHandler;
+ private final BackupEligibilityRules mScheduledBackupEligibility;
private final IBackupManager mBackupManagerBinder;
@@ -535,11 +538,12 @@ public class UserBackupManagerService {
}
@VisibleForTesting
- UserBackupManagerService(Context context) {
+ UserBackupManagerService(Context context, PackageManager packageManager) {
mContext = context;
mUserId = 0;
mRegisterTransportsRequestedTime = 0;
+ mPackageManager = packageManager;
mBaseStateDir = null;
mDataDir = null;
@@ -550,7 +554,6 @@ public class UserBackupManagerService {
mRunInitIntent = null;
mAgentTimeoutParameters = null;
mTransportManager = null;
- mPackageManager = null;
mActivityManagerInternal = null;
mAlarmManager = null;
mConstants = null;
@@ -562,6 +565,7 @@ public class UserBackupManagerService {
mActivityManager = null;
mStorageManager = null;
mBackupManagerBinder = null;
+ mScheduledBackupEligibility = null;
}
private UserBackupManagerService(
@@ -578,6 +582,8 @@ public class UserBackupManagerService {
mPackageManagerBinder = AppGlobals.getPackageManager();
mActivityManager = ActivityManager.getService();
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mScheduledBackupEligibility = getEligibilityRules(mPackageManager, userId,
+ OperationType.BACKUP);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -913,7 +919,13 @@ public class UserBackupManagerService {
* non-lifecycle agent instance, so we manually set up the context topology for it.
*/
public BackupAgent makeMetadataAgent() {
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager, mUserId);
+ return makeMetadataAgentWithEligibilityRules(mScheduledBackupEligibility);
+ }
+
+ public BackupAgent makeMetadataAgentWithEligibilityRules(
+ BackupEligibilityRules backupEligibilityRules) {
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager, mUserId,
+ backupEligibilityRules);
pmAgent.attach(mContext);
pmAgent.onCreate(UserHandle.of(mUserId));
return pmAgent;
@@ -995,7 +1007,8 @@ public class UserBackupManagerService {
boolean changed = false;
ArrayList<FullBackupEntry> schedule = null;
List<PackageInfo> apps =
- PackageManagerBackupAgent.getStorableApplications(mPackageManager, mUserId);
+ PackageManagerBackupAgent.getStorableApplications(mPackageManager, mUserId,
+ mScheduledBackupEligibility);
if (mFullBackupScheduleFile.exists()) {
try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
@@ -1025,9 +1038,9 @@ public class UserBackupManagerService {
foundApps.add(pkgName); // all apps that we've addressed already
try {
PackageInfo pkg = mPackageManager.getPackageInfoAsUser(pkgName, 0, mUserId);
- if (AppBackupUtils.appGetsFullBackup(pkg)
- && AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo,
- mUserId)) {
+ if (mScheduledBackupEligibility.appGetsFullBackup(pkg)
+ && mScheduledBackupEligibility.appIsEligibleForBackup(
+ pkg.applicationInfo)) {
schedule.add(new FullBackupEntry(pkgName, lastBackup));
} else {
if (DEBUG) {
@@ -1046,9 +1059,9 @@ public class UserBackupManagerService {
// New apps can arrive "out of band" via OTA and similar, so we also need to
// scan to make sure that we're tracking all full-backup candidates properly
for (PackageInfo app : apps) {
- if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(app.applicationInfo,
- mUserId)) {
+ if (mScheduledBackupEligibility.appGetsFullBackup(app)
+ && mScheduledBackupEligibility.appIsEligibleForBackup(
+ app.applicationInfo)) {
if (!foundApps.contains(app.packageName)) {
if (MORE_DEBUG) {
Slog.i(
@@ -1079,8 +1092,9 @@ public class UserBackupManagerService {
changed = true;
schedule = new ArrayList<>(apps.size());
for (PackageInfo info : apps) {
- if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
- info.applicationInfo, mUserId)) {
+ if (mScheduledBackupEligibility.appGetsFullBackup(info)
+ && mScheduledBackupEligibility.appIsEligibleForBackup(
+ info.applicationInfo)) {
schedule.add(new FullBackupEntry(info.packageName, 0));
}
}
@@ -1380,9 +1394,9 @@ public class UserBackupManagerService {
PackageInfo app =
mPackageManager.getPackageInfoAsUser(
packageName, /* flags */ 0, mUserId);
- if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo, mUserId)) {
+ if (mScheduledBackupEligibility.appGetsFullBackup(app)
+ && mScheduledBackupEligibility.appIsEligibleForBackup(
+ app.applicationInfo)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
} else {
@@ -1825,6 +1839,15 @@ public class UserBackupManagerService {
*/
public int requestBackup(String[] packages, IBackupObserver observer,
IBackupManagerMonitor monitor, int flags) {
+ return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP);
+ }
+
+ /**
+ * Requests a backup for the inputted {@code packages} with a specified {@link
+ * IBackupManagerMonitor} and {@link OperationType}.
+ */
+ public int requestBackup(String[] packages, IBackupObserver observer,
+ IBackupManagerMonitor monitor, int flags, @OperationType int operationType) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
if (packages == null || packages.length < 1) {
@@ -1871,7 +1894,21 @@ public class UserBackupManagerService {
OnTaskFinishedListener listener =
caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
+ BackupEligibilityRules backupEligibilityRules = getEligibilityRulesForOperation(
+ operationType);
+
+ Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
+ msg.obj = getRequestBackupParams(packages, observer, monitor, flags, backupEligibilityRules,
+ transportClient, transportDirName, listener);
+ mBackupHandler.sendMessage(msg);
+ return BackupManager.SUCCESS;
+ }
+ @VisibleForTesting
+ BackupParams getRequestBackupParams(String[] packages, IBackupObserver observer,
+ IBackupManagerMonitor monitor, int flags, BackupEligibilityRules backupEligibilityRules,
+ TransportClient transportClient, String transportDirName,
+ OnTaskFinishedListener listener) {
ArrayList<String> fullBackupList = new ArrayList<>();
ArrayList<String> kvBackupList = new ArrayList<>();
for (String packageName : packages) {
@@ -1882,12 +1919,12 @@ public class UserBackupManagerService {
try {
PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName,
PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
- if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo, mUserId)) {
+ if (!backupEligibilityRules.appIsEligibleForBackup(packageInfo.applicationInfo)) {
BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
}
- if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
+ if (backupEligibilityRules.appGetsFullBackup(packageInfo)) {
fullBackupList.add(packageInfo.packageName);
} else {
kvBackupList.add(packageInfo.packageName);
@@ -1897,6 +1934,7 @@ public class UserBackupManagerService {
BackupManager.ERROR_PACKAGE_NOT_FOUND);
}
}
+
EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
fullBackupList.size());
if (MORE_DEBUG) {
@@ -1915,11 +1953,9 @@ public class UserBackupManagerService {
boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
- Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
- msg.obj = new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
- observer, monitor, listener, true, nonIncrementalBackup);
- mBackupHandler.sendMessage(msg);
- return BackupManager.SUCCESS;
+ return new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
+ observer, monitor, listener, /* userInitiated */ true, nonIncrementalBackup,
+ backupEligibilityRules);
}
/** Cancel all running backups. */
@@ -2448,7 +2484,7 @@ public class UserBackupManagerService {
try {
PackageInfo appInfo = mPackageManager.getPackageInfoAsUser(
entry.packageName, 0, mUserId);
- if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
+ if (!mScheduledBackupEligibility.appGetsFullBackup(appInfo)) {
// The head app isn't supposed to get full-data backups [any more];
// so we cull it and force a loop around to consider the new head
// app.
@@ -2529,7 +2565,8 @@ public class UserBackupManagerService {
/* backupObserver */ null,
/* monitor */ null,
/* userInitiated */ false,
- "BMS.beginFullBackup()");
+ "BMS.beginFullBackup()",
+ getEligibilityRulesForOperation(OperationType.BACKUP));
// Acquiring wakelock for PerformFullTransportBackupTask before its start.
mWakelock.acquire();
(new Thread(mRunningFullBackupTask)).start();
@@ -2968,7 +3005,7 @@ public class UserBackupManagerService {
AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
- pkgList);
+ pkgList, mScheduledBackupEligibility);
final int token = generateRandomIntegerToken();
synchronized (mAdbBackupRestoreConfirmations) {
mAdbBackupRestoreConfirmations.put(token, params);
@@ -3053,7 +3090,8 @@ public class UserBackupManagerService {
/* backupObserver */ null,
/* monitor */ null,
/* userInitiated */ false,
- "BMS.fullTransportBackup()");
+ "BMS.fullTransportBackup()",
+ getEligibilityRulesForOperation(OperationType.BACKUP));
// Acquiring wakelock for PerformFullTransportBackupTask before its start.
mWakelock.acquire();
(new Thread(task, "full-transport-master")).start();
@@ -4104,8 +4142,8 @@ public class UserBackupManagerService {
TransportClient transportClient =
mTransportManager.getCurrentTransportClient(callerLogString);
boolean eligible =
- AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager, mUserId);
+ mScheduledBackupEligibility.appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName);
if (transportClient != null) {
mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
}
@@ -4127,9 +4165,8 @@ public class UserBackupManagerService {
mTransportManager.getCurrentTransportClient(callerLogString);
List<String> eligibleApps = new LinkedList<>();
for (String packageName : packages) {
- if (AppBackupUtils
- .appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager, mUserId)) {
+ if (mScheduledBackupEligibility.appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName)) {
eligibleApps.add(packageName);
}
}
@@ -4142,6 +4179,17 @@ public class UserBackupManagerService {
}
}
+ public BackupEligibilityRules getEligibilityRulesForOperation(
+ @OperationType int operationType) {
+ return getEligibilityRules(mPackageManager, mUserId, operationType);
+ }
+
+ private static BackupEligibilityRules getEligibilityRules(PackageManager packageManager,
+ int userId, @OperationType int operationType) {
+ return new BackupEligibilityRules(packageManager,
+ LocalServices.getService(PackageManagerInternal.class), userId, operationType);
+ }
+
/** Prints service state for 'dumpsys backup'. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
long identityToken = Binder.clearCallingIdentity();
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 0a7159bfe1b7..a69bd6b62264 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -24,6 +24,8 @@ import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEA
import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManager.OperationType;
import android.app.backup.IFullBackupRestoreObserver;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -38,7 +40,7 @@ import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.KeyValueAdbBackupEngine;
import com.android.server.backup.UserBackupManagerService;
-import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.PasswordUtils;
import java.io.ByteArrayOutputStream;
@@ -83,12 +85,14 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
private final String mCurrentPassword;
private final String mEncryptPassword;
private final int mCurrentOpToken;
+ private final BackupEligibilityRules mBackupEligibilityRules;
public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
- boolean doCompress, boolean doKeyValue, String[] packages, AtomicBoolean latch) {
+ boolean doCompress, boolean doKeyValue, String[] packages, AtomicBoolean latch,
+ BackupEligibilityRules backupEligibilityRules) {
super(observer);
mUserBackupManagerService = backupManagerService;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
@@ -119,6 +123,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
}
mCompress = doCompress;
mKeyValue = doKeyValue;
+ mBackupEligibilityRules = backupEligibilityRules;
}
private void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
@@ -286,15 +291,14 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
while (iter.hasNext()) {
PackageInfo pkg = iter.next().getValue();
- if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo,
- mUserBackupManagerService.getUserId())
- || AppBackupUtils.appIsStopped(pkg.applicationInfo)) {
+ if (!mBackupEligibilityRules.appIsEligibleForBackup(pkg.applicationInfo)
+ || mBackupEligibilityRules.appIsStopped(pkg.applicationInfo)) {
iter.remove();
if (DEBUG) {
Slog.i(TAG, "Package " + pkg.packageName
+ " is not eligible for backup, removing.");
}
- } else if (AppBackupUtils.appIsKeyValueOnly(pkg)) {
+ } else if (mBackupEligibilityRules.appIsKeyValueOnly(pkg)) {
iter.remove();
if (DEBUG) {
Slog.i(TAG, "Package " + pkg.packageName
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 738dd9bf0f0d..1fa88920ca74 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -53,7 +53,7 @@ import com.android.server.backup.internal.Operation;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportNotAvailableException;
-import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.android.server.backup.utils.BackupObserverUtils;
@@ -107,7 +107,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
IBackupObserver backupObserver,
IBackupManagerMonitor monitor,
boolean userInitiated,
- String caller) {
+ String caller,
+ BackupEligibilityRules backupEligibilityRules) {
TransportManager transportManager = backupManagerService.getTransportManager();
TransportClient transportClient = transportManager.getCurrentTransportClient(caller);
OnTaskFinishedListener listener =
@@ -124,7 +125,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
backupObserver,
monitor,
listener,
- userInitiated);
+ userInitiated,
+ backupEligibilityRules);
}
private static final String TAG = "PFTBT";
@@ -151,6 +153,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
private volatile boolean mCancelAll;
private final int mCurrentOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+ private final BackupEligibilityRules mBackupEligibilityRules;
public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
TransportClient transportClient,
@@ -158,7 +161,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
String[] whichPackages, boolean updateSchedule,
FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver,
@Nullable IBackupManagerMonitor monitor, @Nullable OnTaskFinishedListener listener,
- boolean userInitiated) {
+ boolean userInitiated, BackupEligibilityRules backupEligibilityRules) {
super(observer);
this.mUserBackupManagerService = backupManagerService;
mTransportClient = transportClient;
@@ -176,6 +179,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mUserId = backupManagerService.getUserId();
+ mBackupEligibilityRules = backupEligibilityRules;
if (backupManagerService.isBackupOperationInProgress()) {
if (DEBUG) {
@@ -193,7 +197,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
PackageInfo info = pm.getPackageInfoAsUser(pkg,
PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
mCurrentPackage = info;
- if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, mUserId)) {
+ if (!mBackupEligibilityRules.appIsEligibleForBackup(info.applicationInfo)) {
// Cull any packages that have indicated that backups are not permitted,
// that run as system-domain uids but do not define their own backup agents,
// as well as any explicit mention of the 'special' shared-storage agent
@@ -209,7 +213,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, pkg,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
- } else if (!AppBackupUtils.appGetsFullBackup(info)) {
+ } else if (!mBackupEligibilityRules.appGetsFullBackup(info)) {
// Cull any packages that are found in the queue but now aren't supposed
// to get full-data backup operations.
if (MORE_DEBUG) {
@@ -224,7 +228,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, pkg,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
- } else if (AppBackupUtils.appIsStopped(info.applicationInfo)) {
+ } else if (mBackupEligibilityRules.appIsStopped(info.applicationInfo)) {
// Cull any packages in the 'stopped' state: they've either just been
// installed or have explicitly been force-stopped by the user. In both
// cases we do not want to launch them for backup.
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 87a8e4982529..1bb434950563 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -20,6 +20,8 @@ import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManager.OperationType;
import android.app.backup.RestoreSet;
import android.os.Handler;
import android.os.HandlerThread;
@@ -222,7 +224,9 @@ public class BackupHandler extends Handler {
listener,
Collections.emptyList(),
/* userInitiated */ false,
- /* nonIncremental */ false);
+ /* nonIncremental */ false,
+ backupManagerService.getEligibilityRulesForOperation(
+ OperationType.BACKUP));
} catch (Exception e) {
// unable to ask the transport its dir name -- transient failure, since
// the above check succeeded. Try again next time.
@@ -279,7 +283,8 @@ public class BackupHandler extends Handler {
params.observer, params.includeApks, params.includeObbs,
params.includeShared, params.doWidgets, params.curPassword,
params.encryptPassword, params.allApps, params.includeSystem,
- params.doCompress, params.includeKeyValue, params.packages, params.latch);
+ params.doCompress, params.includeKeyValue, params.packages, params.latch,
+ params.backupEligibilityRules);
(new Thread(task, "adb-backup")).start();
break;
}
@@ -299,7 +304,9 @@ public class BackupHandler extends Handler {
params.pmToken,
params.isSystemRestore,
params.filterSet,
- params.listener);
+ params.listener,
+ backupManagerService.getEligibilityRulesForOperation(
+ OperationType.BACKUP));
synchronized (backupManagerService.getPendingRestores()) {
if (backupManagerService.isRestoreInProgress()) {
@@ -462,7 +469,8 @@ public class BackupHandler extends Handler {
params.listener,
params.fullPackages,
/* userInitiated */ true,
- params.nonIncrementalBackup);
+ params.nonIncrementalBackup,
+ params.mBackupEligibilityRules);
break;
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index a4e58a1faeac..6124171c7a0e 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -67,7 +67,7 @@ import com.android.server.backup.remote.RemoteCallable;
import com.android.server.backup.remote.RemoteResult;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportNotAvailableException;
-import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupEligibilityRules;
import libcore.io.IoUtils;
@@ -220,7 +220,8 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
OnTaskFinishedListener listener,
List<String> pendingFullBackups,
boolean userInitiated,
- boolean nonIncremental) {
+ boolean nonIncremental,
+ BackupEligibilityRules backupEligibilityRules) {
KeyValueBackupReporter reporter =
new KeyValueBackupReporter(backupManagerService, observer, monitor);
KeyValueBackupTask task =
@@ -234,7 +235,8 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
listener,
pendingFullBackups,
userInitiated,
- nonIncremental);
+ nonIncremental,
+ backupEligibilityRules);
Thread thread = new Thread(task, "key-value-backup-" + THREAD_COUNT.incrementAndGet());
thread.start();
KeyValueBackupReporter.onNewThread(thread.getName());
@@ -258,6 +260,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
private final List<String> mPendingFullBackups;
private final Object mQueueLock;
@Nullable private final DataChangedJournal mJournal;
+ private final BackupEligibilityRules mBackupEligibilityRules;
@Nullable private PerformFullTransportBackupTask mFullBackupTask;
@Nullable private IBackupAgent mAgent;
@@ -307,7 +310,8 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
OnTaskFinishedListener taskFinishedListener,
List<String> pendingFullBackups,
boolean userInitiated,
- boolean nonIncremental) {
+ boolean nonIncremental,
+ BackupEligibilityRules backupEligibilityRules) {
mBackupManagerService = backupManagerService;
mPackageManager = backupManagerService.getPackageManager();
mTransportClient = transportClient;
@@ -330,6 +334,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
mQueueLock = mBackupManagerService.getQueueLock();
mBlankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
mUserId = backupManagerService.getUserId();
+ mBackupEligibilityRules = backupEligibilityRules;
}
private void registerTask() {
@@ -417,7 +422,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
for (String packageName : succeedingPackages) {
if (appsBackedUp.contains(packageName)) {
- Log.v(TAG, "Skipping package which was backed up this time :" + packageName);
+ Log.v(TAG, "Skipping package which was backed up this time: " + packageName);
// Skip packages we backed up in this run.
continue;
}
@@ -456,9 +461,9 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
/** Determine if a package is eligible to be backed up to the transport */
private boolean isEligibleForNoDataCall(PackageInfo packageInfo) {
- return AppBackupUtils.appIsKeyValueOnly(packageInfo)
- && AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(mTransportClient,
- packageInfo.packageName, mPackageManager, mUserId);
+ return mBackupEligibilityRules.appIsKeyValueOnly(packageInfo)
+ && mBackupEligibilityRules.appIsRunningAndEligibleForBackupWithTransport(
+ mTransportClient, packageInfo.packageName);
}
/** Send the "no data changed" message to a transport for a specific package */
@@ -642,7 +647,8 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
mReporter.getObserver(),
mReporter.getMonitor(),
mTaskFinishedListener,
- mUserInitiated);
+ mUserInitiated,
+ mBackupEligibilityRules);
}
private void backupPm() throws TaskException {
@@ -704,15 +710,15 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
throw AgentException.permanent(e);
}
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
- if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mUserId)) {
+ if (!mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo)) {
mReporter.onPackageNotEligibleForBackup(packageName);
throw AgentException.permanent();
}
- if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
+ if (mBackupEligibilityRules.appGetsFullBackup(packageInfo)) {
mReporter.onPackageEligibleForFullBackup(packageName);
throw AgentException.permanent();
}
- if (AppBackupUtils.appIsStopped(applicationInfo)) {
+ if (mBackupEligibilityRules.appIsStopped(applicationInfo)) {
mReporter.onPackageStopped(packageName);
throw AgentException.permanent();
}
@@ -843,7 +849,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
/** Same as {@link #extractAgentData(PackageInfo)}, but only for PM package. */
private void extractPmAgentData(PackageInfo packageInfo) throws AgentException, TaskException {
Preconditions.checkArgument(packageInfo.packageName.equals(PM_PACKAGE));
- BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
+ BackupAgent pmAgent = mBackupManagerService.makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind());
extractAgentData(packageInfo, mAgent);
}
diff --git a/services/backup/java/com/android/server/backup/params/AdbBackupParams.java b/services/backup/java/com/android/server/backup/params/AdbBackupParams.java
index 5c1ba24ffbc2..f08c5fa3919b 100644
--- a/services/backup/java/com/android/server/backup/params/AdbBackupParams.java
+++ b/services/backup/java/com/android/server/backup/params/AdbBackupParams.java
@@ -18,6 +18,8 @@ package com.android.server.backup.params;
import android.os.ParcelFileDescriptor;
+import com.android.server.backup.utils.BackupEligibilityRules;
+
public class AdbBackupParams extends AdbParams {
public boolean includeApks;
@@ -29,10 +31,12 @@ public class AdbBackupParams extends AdbParams {
public boolean doCompress;
public boolean includeKeyValue;
public String[] packages;
+ public BackupEligibilityRules backupEligibilityRules;
public AdbBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem,
- boolean compress, boolean doKeyValue, String[] pkgList) {
+ boolean compress, boolean doKeyValue, String[] pkgList,
+ BackupEligibilityRules eligibilityRules) {
fd = output;
includeApks = saveApks;
includeObbs = saveObbs;
@@ -43,5 +47,6 @@ public class AdbBackupParams extends AdbParams {
doCompress = compress;
includeKeyValue = doKeyValue;
packages = pkgList;
+ backupEligibilityRules = eligibilityRules;
}
}
diff --git a/services/backup/java/com/android/server/backup/params/BackupParams.java b/services/backup/java/com/android/server/backup/params/BackupParams.java
index 2ba8ec16a45c..800257002f01 100644
--- a/services/backup/java/com/android/server/backup/params/BackupParams.java
+++ b/services/backup/java/com/android/server/backup/params/BackupParams.java
@@ -21,6 +21,7 @@ import android.app.backup.IBackupObserver;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.utils.BackupEligibilityRules;
import java.util.ArrayList;
@@ -35,11 +36,12 @@ public class BackupParams {
public OnTaskFinishedListener listener;
public boolean userInitiated;
public boolean nonIncrementalBackup;
+ public BackupEligibilityRules mBackupEligibilityRules;
public BackupParams(TransportClient transportClient, String dirName,
ArrayList<String> kvPackages, ArrayList<String> fullPackages, IBackupObserver observer,
IBackupManagerMonitor monitor, OnTaskFinishedListener listener, boolean userInitiated,
- boolean nonIncrementalBackup) {
+ boolean nonIncrementalBackup, BackupEligibilityRules backupEligibilityRules) {
this.transportClient = transportClient;
this.dirName = dirName;
this.kvPackages = kvPackages;
@@ -49,5 +51,6 @@ public class BackupParams {
this.listener = listener;
this.userInitiated = userInitiated;
this.nonIncrementalBackup = nonIncrementalBackup;
+ this.mBackupEligibilityRules = backupEligibilityRules;
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 12113fea12a4..a7e360403ccc 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -66,7 +66,7 @@ import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import libcore.io.IoUtils;
@@ -186,7 +186,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
int pmToken,
boolean isFullSystemRestore,
@Nullable String[] filterSet,
- OnTaskFinishedListener listener) {
+ OnTaskFinishedListener listener,
+ BackupEligibilityRules backupEligibilityRules) {
this.backupManagerService = backupManagerService;
mUserId = backupManagerService.getUserId();
mTransportManager = backupManagerService.getTransportManager();
@@ -218,7 +219,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// We want everything and a pony
List<PackageInfo> apps =
PackageManagerBackupAgent.getStorableApplications(
- backupManagerService.getPackageManager(), mUserId);
+ backupManagerService.getPackageManager(), mUserId,
+ backupEligibilityRules);
filterSet = packagesToNames(apps);
if (DEBUG) {
Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
@@ -245,7 +247,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
continue;
}
- if (AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, mUserId)) {
+ if (backupEligibilityRules.appIsEligibleForBackup(info.applicationInfo)) {
mAcceptSet.add(info);
}
} catch (NameNotFoundException e) {
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index 35dfccf32924..162921528e95 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -23,6 +23,7 @@ import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_A
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
+import android.app.backup.BackupManager.OperationType;
import android.app.backup.BackupTransport;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -46,12 +47,34 @@ import java.util.Set;
/**
* Utility methods wrapping operations on ApplicationInfo and PackageInfo.
*/
-public class AppBackupUtils {
+public class BackupEligibilityRules {
private static final boolean DEBUG = false;
// Whitelist of system packages that are eligible for backup in non-system users.
private static final Set<String> systemPackagesWhitelistedForAllUsers =
Sets.newArraySet(PACKAGE_MANAGER_SENTINEL, PLATFORM_PACKAGE_NAME);
+ private final PackageManager mPackageManager;
+ private final PackageManagerInternal mPackageManagerInternal;
+ private final int mUserId;
+ @OperationType private final int mOperationType;
+
+ public static BackupEligibilityRules forBackup(PackageManager packageManager,
+ PackageManagerInternal packageManagerInternal,
+ int userId) {
+ return new BackupEligibilityRules(packageManager, packageManagerInternal, userId,
+ OperationType.BACKUP);
+ }
+
+ public BackupEligibilityRules(PackageManager packageManager,
+ PackageManagerInternal packageManagerInternal,
+ int userId,
+ @OperationType int operationType) {
+ mPackageManager = packageManager;
+ mPackageManagerInternal = packageManagerInternal;
+ mUserId = userId;
+ mOperationType = operationType;
+ }
+
/**
* Returns whether app is eligible for backup.
*
@@ -64,23 +87,18 @@ public class AppBackupUtils {
* <li>it is the special shared-storage backup package used for 'adb backup'
* </ol>
*/
- public static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) {
- return appIsEligibleForBackup(
- app, LocalServices.getService(PackageManagerInternal.class), userId);
- }
-
@VisibleForTesting
- static boolean appIsEligibleForBackup(
- ApplicationInfo app, PackageManagerInternal packageManager, int userId) {
+ public boolean appIsEligibleForBackup(ApplicationInfo app) {
// 1. their manifest states android:allowBackup="false"
- if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
+ boolean appAllowsBackup = (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
+ if (!appAllowsBackup && !forceFullBackup(app.uid, mOperationType)) {
return false;
}
// 2. they run as a system-level uid
if (UserHandle.isCore(app.uid)) {
// and the backup is happening for non-system user on a non-whitelisted package.
- if (userId != UserHandle.USER_SYSTEM
+ if (mUserId != UserHandle.USER_SYSTEM
&& !systemPackagesWhitelistedForAllUsers.contains(app.packageName)) {
return false;
}
@@ -101,7 +119,7 @@ public class AppBackupUtils {
return false;
}
- return !appIsDisabled(app, packageManager, userId);
+ return !appIsDisabled(app);
}
/**
@@ -114,18 +132,16 @@ public class AppBackupUtils {
* {@link BackupTransport#isAppEligibleForBackup(PackageInfo, boolean)}
* </ol>
*/
- public static boolean appIsRunningAndEligibleForBackupWithTransport(
+ public boolean appIsRunningAndEligibleForBackupWithTransport(
@Nullable TransportClient transportClient,
- String packageName,
- PackageManager pm,
- int userId) {
+ String packageName) {
try {
- PackageInfo packageInfo = pm.getPackageInfoAsUser(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES, userId);
+ PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
- if (!appIsEligibleForBackup(applicationInfo, userId)
+ if (!appIsEligibleForBackup(applicationInfo)
|| appIsStopped(applicationInfo)
- || appIsDisabled(applicationInfo, userId)) {
+ || appIsDisabled(applicationInfo)) {
return false;
}
if (transportClient != null) {
@@ -134,7 +150,7 @@ public class AppBackupUtils {
transportClient.connectOrThrow(
"AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport");
return transport.isAppEligibleForBackup(
- packageInfo, AppBackupUtils.appGetsFullBackup(packageInfo));
+ packageInfo, appGetsFullBackup(packageInfo));
} catch (Exception e) {
Slog.e(TAG, "Unable to ask about eligibility: " + e.getMessage());
}
@@ -147,14 +163,11 @@ public class AppBackupUtils {
}
/** Avoid backups of 'disabled' apps. */
- static boolean appIsDisabled(ApplicationInfo app, int userId) {
- return appIsDisabled(app, LocalServices.getService(PackageManagerInternal.class), userId);
- }
-
@VisibleForTesting
- static boolean appIsDisabled(
- ApplicationInfo app, PackageManagerInternal packageManager, int userId) {
- int enabledSetting = packageManager.getApplicationEnabledState(app.packageName, userId);
+ boolean appIsDisabled(
+ ApplicationInfo app) {
+ int enabledSetting = mPackageManagerInternal.getApplicationEnabledState(app.packageName,
+ mUserId);
switch (enabledSetting) {
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
@@ -180,7 +193,7 @@ public class AppBackupUtils {
* <li>The app has just been installed.
* </ul>
*/
- public static boolean appIsStopped(ApplicationInfo app) {
+ public boolean appIsStopped(ApplicationInfo app) {
return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0);
}
@@ -188,7 +201,13 @@ public class AppBackupUtils {
* Returns whether the app can get full backup. Does *not* check overall backup eligibility
* policy!
*/
- public static boolean appGetsFullBackup(PackageInfo pkg) {
+ @VisibleForTesting
+ public boolean appGetsFullBackup(PackageInfo pkg) {
+ if (forceFullBackup(pkg.applicationInfo.uid, mOperationType)) {
+ // If this is a migration, all non-system packages get full backup.
+ return true;
+ }
+
if (pkg.applicationInfo.backupAgentName != null) {
// If it has an agent, it gets full backups only if it says so
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0;
@@ -198,11 +217,20 @@ public class AppBackupUtils {
return true;
}
+ public boolean appIgnoresIncludeExcludeRules(ApplicationInfo app) {
+ return forceFullBackup(app.uid, mOperationType);
+ }
+
+ private boolean forceFullBackup(int appUid, @OperationType int operationType) {
+ return operationType == OperationType.MIGRATION &&
+ !UserHandle.isCore(appUid);
+ }
+
/**
* Returns whether the app is only capable of doing key/value. We say it's not if it allows full
* backup, and it is otherwise.
*/
- public static boolean appIsKeyValueOnly(PackageInfo pkg) {
+ public boolean appIsKeyValueOnly(PackageInfo pkg) {
return !appGetsFullBackup(pkg);
}
@@ -224,8 +252,7 @@ public class AppBackupUtils {
*
* Note that if {@param target} is null we return false.
*/
- public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target,
- PackageManagerInternal pmi) {
+ public boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
if (target == null || target.packageName == null) {
return false;
}
@@ -266,7 +293,7 @@ public class AppBackupUtils {
// TODO(b/73988180): address the case that app has declared restoreAnyVersion and is
// restoring from higher version to lower after having rotated the key (i.e. higher
// version has different sig than lower version that we want to restore to)
- return pmi.isDataRestoreSafe(storedSigs[0], target.packageName);
+ return mPackageManagerInternal.isDataRestoreSafe(storedSigs[0], target.packageName);
} else {
// the app couldn't have rotated keys, since it was signed with multiple sigs - do
// a check to see if we find a match for all stored sigs
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index 97bde9c03440..8e8bac435503 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -159,7 +159,9 @@ public class RestoreUtils {
Signature[] sigs = manifestSignatures.get(info.packageName);
PackageManagerInternal pmi = LocalServices.getService(
PackageManagerInternal.class);
- if (AppBackupUtils.signaturesMatch(sigs, pkg, pmi)) {
+ BackupEligibilityRules eligibilityRules =
+ BackupEligibilityRules.forBackup(packageManager, pmi, userId);
+ if (eligibilityRules.signaturesMatch(sigs, pkg)) {
// If this is a system-uid app without a declared backup agent,
// don't restore any of the file data.
if (UserHandle.isCore(pkg.applicationInfo.uid)
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index d2d382dfc14d..bf8e9c8512ae 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -394,7 +394,8 @@ public class TarBackupReader {
}
RestorePolicy policy = RestorePolicy.IGNORE;
-
+ BackupEligibilityRules eligibilityRules = BackupEligibilityRules.forBackup(packageManager,
+ pmi, userId);
// Okay, got the manifest info we need...
try {
PackageInfo pkgInfo = packageManager.getPackageInfoAsUser(
@@ -413,7 +414,7 @@ public class TarBackupReader {
// such packages are signed with the platform cert instead of
// the app developer's cert, so they're different on every
// device.
- if (AppBackupUtils.signaturesMatch(signatures, pkgInfo, pmi)) {
+ if (eligibilityRules.signaturesMatch(signatures, pkgInfo)) {
if ((pkgInfo.applicationInfo.flags
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
Slog.i(TAG, "Package has restoreAnyVersion; taking data");
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7241ad32065f..64d0e911652e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -119,7 +119,7 @@ java_library_static {
"android.hardware.tv.cec-V1.0-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.1-java",
- "android.hardware.biometrics.fingerprint-V2.2-java",
+ "android.hardware.biometrics.fingerprint-V2.3-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8aeb0879afa1..02c08cc4cd39 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4324,9 +4324,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceInternetPermission();
final int uid = Binder.getCallingUid();
final int connectivityInfo = encodeBool(hasConnectivity);
- mHandler.sendMessage(
- mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network));
+ // Handle ConnectivityDiagnostics event before attempting to revalidate the network. This
+ // forces an ordering of ConnectivityDiagnostics events in the case where hasConnectivity
+ // does not match the known connectivity of the network - this causes NetworkMonitor to
+ // revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event.
final NetworkAgentInfo nai;
if (network == null) {
nai = getDefaultNetwork();
@@ -4339,6 +4341,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
connectivityInfo, 0, nai));
}
+
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network));
}
private void handleReportNetworkConnectivity(
@@ -5145,14 +5150,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private void onPackageAdded(String packageName, int uid) {
- if (TextUtils.isEmpty(packageName) || uid < 0) {
- Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid);
- return;
- }
- mPermissionMonitor.onPackageAdded(packageName, uid);
- }
-
private void onPackageReplaced(String packageName, int uid) {
if (TextUtils.isEmpty(packageName) || uid < 0) {
Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
@@ -5178,7 +5175,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
return;
}
- mPermissionMonitor.onPackageRemoved(uid);
final int userId = UserHandle.getUserId(uid);
synchronized (mVpns) {
@@ -5228,8 +5224,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
onUserRemoved(userId);
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
onUserUnlocked(userId);
- } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- onPackageAdded(packageName, uid);
} else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
onPackageReplaced(packageName, uid);
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index 028d41234dd0..886bfb8f4736 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
@@ -159,7 +160,14 @@ public final class DropBoxManagerService extends SystemService {
@Override
public DropBoxManager.Entry getNextEntry(String tag, long millis, String callingPackage) {
- return DropBoxManagerService.this.getNextEntry(tag, millis, callingPackage);
+ return getNextEntryWithAttribution(tag, millis, callingPackage, null);
+ }
+
+ @Override
+ public DropBoxManager.Entry getNextEntryWithAttribution(String tag, long millis,
+ String callingPackage, String callingAttributionTag) {
+ return DropBoxManagerService.this.getNextEntry(tag, millis, callingPackage,
+ callingAttributionTag);
}
@Override
@@ -470,7 +478,8 @@ public final class DropBoxManagerService extends SystemService {
}
}
- private boolean checkPermission(int callingUid, String callingPackage) {
+ private boolean checkPermission(int callingUid, String callingPackage,
+ @Nullable String callingAttributionTag) {
// If callers have this permission, then we don't need to check
// USAGE_STATS, because they are part of the system and have agreed to
// check USAGE_STATS before passing the data along.
@@ -484,8 +493,9 @@ public final class DropBoxManagerService extends SystemService {
android.Manifest.permission.READ_LOGS, TAG);
// Callers also need the ability to read usage statistics
- switch (getContext().getSystemService(AppOpsManager.class)
- .noteOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage)) {
+ switch (getContext().getSystemService(AppOpsManager.class).noteOp(
+ AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage, callingAttributionTag,
+ null)) {
case AppOpsManager.MODE_ALLOWED:
return true;
case AppOpsManager.MODE_DEFAULT:
@@ -498,8 +508,8 @@ public final class DropBoxManagerService extends SystemService {
}
public synchronized DropBoxManager.Entry getNextEntry(String tag, long millis,
- String callingPackage) {
- if (!checkPermission(Binder.getCallingUid(), callingPackage)) {
+ String callingPackage, @Nullable String callingAttributionTag) {
+ if (!checkPermission(Binder.getCallingUid(), callingPackage, callingAttributionTag)) {
return null;
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index de96aaa1d940..c87dcd7874f8 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -75,6 +75,16 @@ public class GestureLauncherService extends SystemService {
*/
@VisibleForTesting static final long POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS = 500;
+ /**
+ * Number of taps required to launch panic ui.
+ */
+ private static final int PANIC_POWER_TAP_COUNT_THRESHOLD = 5;
+
+ /**
+ * Number of taps required to launch camera shortcut.
+ */
+ private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
+
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
private final CameraLiftTriggerEventListener mCameraLiftTriggerListener =
@@ -127,8 +137,15 @@ public class GestureLauncherService extends SystemService {
* Whether camera double tap power button gesture is currently enabled;
*/
private boolean mCameraDoubleTapPowerEnabled;
+
+ /**
+ * Whether panic button gesture is currently enabled
+ */
+ private boolean mPanicButtonGestureEnabled;
+
private long mLastPowerDown;
private int mPowerButtonConsecutiveTaps;
+ private int mPowerButtonSlowConsecutiveTaps;
public GestureLauncherService(Context context) {
this(context, new MetricsLogger());
@@ -141,10 +158,12 @@ public class GestureLauncherService extends SystemService {
mMetricsLogger = metricsLogger;
}
+ @Override
public void onStart() {
LocalServices.addService(GestureLauncherService.class, this);
}
+ @Override
public void onBootPhase(int phase) {
if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Resources resources = mContext.getResources();
@@ -160,6 +179,7 @@ public class GestureLauncherService extends SystemService {
"GestureLauncherService");
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
+ updatePanicButtonGestureEnabled();
mUserId = ActivityManager.getCurrentUser();
mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
@@ -177,6 +197,9 @@ public class GestureLauncherService extends SystemService {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED),
false, mSettingObserver, mUserId);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.PANIC_GESTURE_ENABLED),
+ false, mSettingObserver, mUserId);
}
private void updateCameraRegistered() {
@@ -202,6 +225,14 @@ public class GestureLauncherService extends SystemService {
}
}
+ @VisibleForTesting
+ void updatePanicButtonGestureEnabled() {
+ boolean enabled = isPanicButtonGestureEnabled(mContext, mUserId);
+ synchronized (this) {
+ mPanicButtonGestureEnabled = enabled;
+ }
+ }
+
private void unregisterCameraLaunchGesture() {
if (mCameraLaunchRegistered) {
mCameraLaunchRegistered = false;
@@ -327,6 +358,14 @@ public class GestureLauncherService extends SystemService {
}
/**
+ * Whether to enable panic button gesture.
+ */
+ public static boolean isPanicButtonGestureEnabled(Context context, int userId) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.PANIC_GESTURE_ENABLED, 0, userId) != 0;
+ }
+
+ /**
* Whether to enable the camera launch gesture.
*/
public static boolean isCameraLaunchEnabled(Resources resources) {
@@ -355,6 +394,13 @@ public class GestureLauncherService extends SystemService {
isCameraLiftTriggerEnabled(resources);
}
+ /**
+ * Attempts to intercept power key down event by detecting certain gesture patterns
+ *
+ * @param interactive true if the event's policy contains {@code FLAG_INTERACTIVE}
+ * @param outLaunched true if some action is taken as part of the key intercept (eg, app launch)
+ * @return true if the key down event is intercepted
+ */
public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
MutableBoolean outLaunched) {
if (event.isLongPress()) {
@@ -363,41 +409,60 @@ public class GestureLauncherService extends SystemService {
// taps or consecutive taps, so we want to ignore the long press event.
return false;
}
- boolean launched = false;
+ boolean launchCamera = false;
+ boolean launchPanic = false;
boolean intercept = false;
long powerTapInterval;
synchronized (this) {
powerTapInterval = event.getEventTime() - mLastPowerDown;
- if (mCameraDoubleTapPowerEnabled
- && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
- launched = true;
- intercept = interactive;
- mPowerButtonConsecutiveTaps++;
- } else if (powerTapInterval < POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
- mPowerButtonConsecutiveTaps++;
- } else {
+ mLastPowerDown = event.getEventTime();
+ if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
+ // Tap too slow, reset consecutive tap counts.
+ mPowerButtonConsecutiveTaps = 1;
+ mPowerButtonSlowConsecutiveTaps = 1;
+ } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
+ // Tap too slow for shortcuts
mPowerButtonConsecutiveTaps = 1;
+ mPowerButtonSlowConsecutiveTaps++;
+ } else {
+ // Fast consecutive tap
+ mPowerButtonConsecutiveTaps++;
+ mPowerButtonSlowConsecutiveTaps++;
+ }
+ if (mPanicButtonGestureEnabled
+ && mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
+ launchPanic = true;
+ intercept = interactive;
+ } else if (mCameraDoubleTapPowerEnabled
+ && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
+ && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
+ launchCamera = true;
+ intercept = interactive;
}
- mLastPowerDown = event.getEventTime();
}
- if (DBG && mPowerButtonConsecutiveTaps > 1) {
- Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps) +
- " consecutive power button taps detected");
+ if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
+ Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps)
+ + " consecutive power button taps detected, "
+ + Long.valueOf(mPowerButtonSlowConsecutiveTaps)
+ + " consecutive slow power button taps detected");
}
- if (launched) {
+ if (launchCamera) {
Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
+ powerTapInterval + "ms");
- launched = handleCameraGesture(false /* useWakelock */,
+ launchCamera = handleCameraGesture(false /* useWakelock */,
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
- if (launched) {
+ if (launchCamera) {
mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
(int) powerTapInterval);
}
+ } else if (launchPanic) {
+ Slog.i(TAG, "Panic gesture detected, launching panic.");
}
- mMetricsLogger.histogram("power_consecutive_short_tap_count", mPowerButtonConsecutiveTaps);
+ mMetricsLogger.histogram("power_consecutive_short_tap_count",
+ mPowerButtonSlowConsecutiveTaps);
mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
- outLaunched.value = launched;
- return intercept && launched;
+ outLaunched.value = launchCamera || launchPanic;
+ return intercept && (launchCamera || launchPanic);
}
/**
@@ -445,6 +510,7 @@ public class GestureLauncherService extends SystemService {
registerContentObservers();
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
+ updatePanicButtonGestureEnabled();
}
}
};
@@ -454,6 +520,7 @@ public class GestureLauncherService extends SystemService {
if (userId == mUserId) {
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
+ updatePanicButtonGestureEnabled();
}
}
};
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index fd7abfa65c86..1689656479a5 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -23,6 +23,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.net.ConnectivityModuleConnector;
@@ -306,6 +307,8 @@ public class PackageWatchdog {
MonitoredPackage pkg = newMonitoredPackage(packageNames.get(i), durationMs, false);
if (pkg != null) {
packages.add(pkg);
+ } else {
+ Slog.w(TAG, "Failed to create MonitoredPackage for pkg=" + packageNames.get(i));
}
}
@@ -861,6 +864,25 @@ public class PackageWatchdog {
});
}
+ /**
+ * Gets PackageInfo for the given package. Matches any user and apex.
+ *
+ * @throws PackageManager.NameNotFoundException if no such package is installed.
+ */
+ private PackageInfo getPackageInfo(String packageName)
+ throws PackageManager.NameNotFoundException {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ // The MATCH_ANY_USER flag doesn't mix well with the MATCH_APEX
+ // flag, so make two separate attempts to get the package info.
+ // We don't need both flags at the same time because we assume
+ // apex files are always installed for all users.
+ return pm.getPackageInfo(packageName, PackageManager.MATCH_ANY_USER);
+ } catch (PackageManager.NameNotFoundException e) {
+ return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
+ }
+ }
+
@Nullable
private VersionedPackage getVersionedPackage(String packageName) {
final PackageManager pm = mContext.getPackageManager();
@@ -868,8 +890,7 @@ public class PackageWatchdog {
return null;
}
try {
- final long versionCode = pm.getPackageInfo(
- packageName, 0 /* flags */).getLongVersionCode();
+ final long versionCode = getPackageInfo(packageName).getLongVersionCode();
return new VersionedPackage(packageName, versionCode);
} catch (PackageManager.NameNotFoundException e) {
return null;
diff --git a/services/core/java/com/android/server/RuntimeService.java b/services/core/java/com/android/server/RuntimeService.java
index bb39ccc52af2..f4249f844873 100644
--- a/services/core/java/com/android/server/RuntimeService.java
+++ b/services/core/java/com/android/server/RuntimeService.java
@@ -23,10 +23,9 @@ import android.service.runtime.RuntimeServiceInfoProto;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import libcore.timezone.TimeZoneDataFiles;
-import libcore.util.CoreLibraryDebug;
-import libcore.util.DebugInfo;
-
+import com.android.i18n.timezone.DebugInfo;
+import com.android.i18n.timezone.I18nModuleDebug;
+import com.android.i18n.timezone.TimeZoneDataFiles;
import com.android.internal.util.DumpUtils;
import com.android.timezone.distro.DistroException;
import com.android.timezone.distro.DistroVersion;
@@ -61,14 +60,14 @@ public class RuntimeService extends Binder {
boolean protoFormat = hasOption(args, "--proto");
ProtoOutputStream proto = null;
- DebugInfo coreLibraryDebugInfo = CoreLibraryDebug.getDebugInfo();
- addTimeZoneApkDebugInfo(coreLibraryDebugInfo);
+ DebugInfo i18nLibraryDebugInfo = I18nModuleDebug.getDebugInfo();
+ addTimeZoneApkDebugInfo(i18nLibraryDebugInfo);
if (protoFormat) {
proto = new ProtoOutputStream(fd);
- reportTimeZoneInfoProto(coreLibraryDebugInfo, proto);
+ reportTimeZoneInfoProto(i18nLibraryDebugInfo, proto);
} else {
- reportTimeZoneInfo(coreLibraryDebugInfo, pw);
+ reportTimeZoneInfo(i18nLibraryDebugInfo, pw);
}
if (protoFormat) {
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index a91f2f60c02d..e5d0021a2bdf 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -56,8 +56,7 @@ import java.util.Objects;
/**
* Maintains a binding to the best service that matches the given intent information. Bind and
- * unbind callbacks, as well as all binder operations, will all be run on a single thread, but the
- * exact thread is left undefined.
+ * unbind callbacks, as well as all binder operations, will all be run on a single thread.
*/
public class ServiceWatcher implements ServiceConnection {
@@ -73,7 +72,11 @@ public class ServiceWatcher implements ServiceConnection {
public interface BinderRunner {
/** Called to run client code with the binder. */
void run(IBinder binder) throws RemoteException;
- /** Called if an error occurred and the function could not be run. */
+ /**
+ * Called if an error occurred and the function could not be run. This callback is only
+ * intended for resource deallocation and cleanup in response to a single binder operation,
+ * it should not be used to propagate errors further.
+ */
default void onError() {}
}
@@ -189,8 +192,15 @@ public class ServiceWatcher implements ServiceConnection {
public ServiceWatcher(Context context, String action,
@Nullable BinderRunner onBind, @Nullable Runnable onUnbind,
@BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
+ this(context, FgThread.getHandler(), action, onBind, onUnbind, enableOverlayResId,
+ nonOverlayPackageResId);
+ }
+
+ public ServiceWatcher(Context context, Handler handler, String action,
+ @Nullable BinderRunner onBind, @Nullable Runnable onUnbind,
+ @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
mContext = context;
- mHandler = FgThread.getHandler();
+ mHandler = handler;
mIntent = new Intent(Objects.requireNonNull(action));
Resources resources = context.getResources();
@@ -278,13 +288,6 @@ public class ServiceWatcher implements ServiceConnection {
return true;
}
- /**
- * Returns information on the currently selected service.
- */
- public ServiceInfo getBoundService() {
- return mServiceInfo;
- }
-
private void onBestServiceChanged(boolean forceRebind) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -380,7 +383,7 @@ public class ServiceWatcher implements ServiceConnection {
}
@Override
- public void onBindingDied(ComponentName component) {
+ public final void onBindingDied(ComponentName component) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (D) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c3dee1b414e0..1520dd351c97 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -43,8 +43,6 @@ import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIE
import static android.os.storage.OnObbStateChangeListener.MOUNTED;
import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
import static android.os.storage.StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST;
-import static android.os.storage.StorageManager.PROP_FUSE;
-import static android.os.storage.StorageManager.PROP_SETTINGS_FUSE;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -131,7 +129,6 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.DataUnit;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -233,13 +230,6 @@ class StorageManagerService extends IStorageManager.Stub
*/
private static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
- /**
- * If {@code 1}, enables FuseDaemon to intercept file system ops. If {@code -1},
- * disables FuseDaemon. If {@code 0}, uses the default value from the build system.
- */
- private static final String FUSE_ENABLED = "fuse_enabled";
- private static final boolean DEFAULT_FUSE_ENABLED = true;
-
@GuardedBy("mLock")
private final Set<Integer> mFuseMountedUser = new ArraySet<>();
@@ -609,8 +599,6 @@ class StorageManagerService extends IStorageManager.Stub
// Not guarded by a lock.
private final StorageSessionController mStorageSessionController;
- private final boolean mIsFuseEnabled;
-
private final boolean mVoldAppDataIsolationEnabled;
@GuardedBy("mLock")
@@ -926,7 +914,6 @@ class StorageManagerService extends IStorageManager.Stub
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
mContext.getMainExecutor(), (properties) -> {
refreshIsolatedStorageSettings();
- refreshFuseSettings();
});
refreshIsolatedStorageSettings();
}
@@ -993,27 +980,6 @@ class StorageManagerService extends IStorageManager.Stub
}
/**
- * The most recent flag change takes precedence. Change fuse Settings flag if Device Config is
- * changed. Settings flag change will in turn change fuse system property (persist.sys.fuse)
- * whenever the user reboots.
- */
- private void refreshFuseSettings() {
- int isFuseEnabled = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
- FUSE_ENABLED, 0);
- if (isFuseEnabled == 1) {
- Slog.d(TAG, "Device Config flag for FUSE is enabled, turn Settings fuse flag on");
- SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX
- + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "true");
- } else if (isFuseEnabled == -1) {
- Slog.d(TAG, "Device Config flag for FUSE is disabled, turn Settings fuse flag off");
- SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX
- + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "false");
- }
- // else, keep the build config.
- // This can be overridden by direct adjustment of persist.sys.fflag.override.settings_fuse
- }
-
- /**
* MediaProvider has a ton of code that makes assumptions about storage
* paths never changing, so we outright kill them to pick up new state.
*/
@@ -1091,13 +1057,9 @@ class StorageManagerService extends IStorageManager.Stub
final UserManager userManager = mContext.getSystemService(UserManager.class);
final List<UserInfo> users = userManager.getUsers();
- if (mIsFuseEnabled) {
- mStorageSessionController.onReset(mVold, () -> {
- mHandler.removeCallbacksAndMessages(null);
- });
- } else {
- killMediaProvider(users);
- }
+ mStorageSessionController.onReset(mVold, () -> {
+ mHandler.removeCallbacksAndMessages(null);
+ });
final int[] systemUnlockedUsers;
synchronized (mLock) {
@@ -1490,8 +1452,7 @@ class StorageManagerService extends IStorageManager.Stub
final ActivityManagerInternal amInternal =
LocalServices.getService(ActivityManagerInternal.class);
- if (mIsFuseEnabled && vol.mountUserId >= 0
- && !amInternal.isUserRunning(vol.mountUserId, 0)) {
+ if (vol.mountUserId >= 0 && !amInternal.isUserRunning(vol.mountUserId, 0)) {
Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
+ Integer.toString(vol.mountUserId) + " is no longer running.");
return;
@@ -1803,11 +1764,7 @@ class StorageManagerService extends IStorageManager.Stub
SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));
- // If there is no value in the property yet (first boot after data wipe), this value may be
- // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if
- // different
- mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
- mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean(
+ mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mContext = context;
mResolver = mContext.getContentResolver();
@@ -1821,7 +1778,7 @@ class StorageManagerService extends IStorageManager.Stub
// Add OBB Action Handler to StorageManagerService thread.
mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
- mStorageSessionController = new StorageSessionController(mContext, mIsFuseEnabled);
+ mStorageSessionController = new StorageSessionController(mContext);
mInstaller = new Installer(mContext);
mInstaller.onStart();
@@ -1869,26 +1826,6 @@ class StorageManagerService extends IStorageManager.Stub
PackageManager.FEATURE_AUTOMOTIVE);
}
- /**
- * Checks if user changed the persistent settings_fuse flag from Settings UI
- * and updates PROP_FUSE (reboots if changed).
- */
- private void updateFusePropFromSettings() {
- boolean settingsFuseFlag = SystemProperties.getBoolean(PROP_SETTINGS_FUSE,
- DEFAULT_FUSE_ENABLED);
- Slog.d(TAG, "FUSE flags. Settings: " + settingsFuseFlag
- + ". Default: " + DEFAULT_FUSE_ENABLED);
-
- if (mIsFuseEnabled != settingsFuseFlag) {
- Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag);
- // Set prop_fuse to match prop_settings_fuse because it is used by native daemons like
- // init, zygote, installd and vold
- SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag));
- // Then perform hard reboot to kick policy into place
- mContext.getSystemService(PowerManager.class).reboot("fuse_prop");
- }
- }
-
private void start() {
connectStoraged();
connectVold();
@@ -1987,15 +1924,6 @@ class StorageManagerService extends IStorageManager.Stub
if (provider != null) {
mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
}
-
- if (!mIsFuseEnabled) {
- try {
- mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null,
- mAppOpsCallback);
- mIAppOpsService.startWatchingMode(OP_LEGACY_STORAGE, null, mAppOpsCallback);
- } catch (RemoteException e) {
- }
- }
}
private ProviderInfo getProviderInfo(String authority) {
@@ -2071,7 +1999,6 @@ class StorageManagerService extends IStorageManager.Stub
private void bootCompleted() {
mBootCompleted = true;
mHandler.obtainMessage(H_BOOT_COMPLETED).sendToTarget();
- updateFusePropFromSettings();
}
private void handleBootCompleted() {
@@ -3311,6 +3238,12 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void lockUserKey(int userId) {
+ // Do not lock user 0 data for headless system user
+ if (userId == UserHandle.USER_SYSTEM
+ && UserManager.isHeadlessSystemUserMode()) {
+ throw new IllegalArgumentException("Headless system user data cannot be locked..");
+ }
+
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
try {
@@ -4269,14 +4202,14 @@ class StorageManagerService extends IStorageManager.Stub
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (mIsFuseEnabled && mStorageManagerInternal.isExternalStorageService(uid)) {
+ if (mStorageManagerInternal.isExternalStorageService(uid)) {
// Determine if caller requires pass_through mount; note that we do this for
// all processes that share a UID with MediaProvider; but this is fine, since
// those processes anyway share the same rights as MediaProvider.
return Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
}
- if (mIsFuseEnabled && (mDownloadsAuthorityAppId == UserHandle.getAppId(uid)
+ if ((mDownloadsAuthorityAppId == UserHandle.getAppId(uid)
|| mExternalStorageAuthorityAppId == UserHandle.getAppId(uid))) {
// DownloadManager can write in app-private directories on behalf of apps;
// give it write access to Android/
@@ -4286,7 +4219,7 @@ class StorageManagerService extends IStorageManager.Stub
final boolean hasMtp = mIPackageManager.checkUidPermission(ACCESS_MTP, uid) ==
PERMISSION_GRANTED;
- if (mIsFuseEnabled && hasMtp) {
+ if (hasMtp) {
ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
0, UserHandle.getUserId(uid));
if (ai != null && ai.isSignedWithPlatformKey()) {
@@ -4605,9 +4538,10 @@ class StorageManagerService extends IStorageManager.Stub
ServiceManager.getServiceOrThrow("vold"));
for (String pkg : packageList) {
final String packageObbDir =
- String.format("/storage/emulated/%d/Android/obb/%s/", userId, pkg);
+ String.format(Locale.US, "/storage/emulated/%d/Android/obb/%s/",
+ userId, pkg);
final String packageDataDir =
- String.format("/storage/emulated/%d/Android/data/%s/",
+ String.format(Locale.US, "/storage/emulated/%d/Android/data/%s/",
userId, pkg);
// Create package obb and data dir if it doesn't exist.
@@ -4737,7 +4671,7 @@ class StorageManagerService extends IStorageManager.Stub
return true;
}
- private void killAppForOpChange(int code, int uid, String packageName) {
+ private void killAppForOpChange(int code, int uid) {
final IActivityManager am = ActivityManager.getService();
try {
am.killUid(UserHandle.getAppId(uid), UserHandle.USER_ALL,
@@ -4749,32 +4683,25 @@ class StorageManagerService extends IStorageManager.Stub
public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode) {
final long token = Binder.clearCallingIdentity();
try {
- if (mIsFuseEnabled) {
- // When using FUSE, we may need to kill the app if the op changes
- switch(code) {
- case OP_REQUEST_INSTALL_PACKAGES:
- // Always kill regardless of op change, to remount apps /storage
- killAppForOpChange(code, uid, packageName);
- return;
- case OP_MANAGE_EXTERNAL_STORAGE:
- if (mode != MODE_ALLOWED) {
- // Only kill if op is denied, to lose external_storage gid
- // Killing when op is granted to pickup the gid automatically,
- // results in a bad UX, especially since the gid only gives access
- // to unreliable volumes, USB OTGs that are rarely mounted. The app
- // will get the external_storage gid on next organic restart.
- if (packageName != null) {
- killAppForOpChange(code, uid, packageName);
- } else {
- // TODO(b/158283222) this can happen, figure out if we need
- // to kill in this case as well.
- }
- }
- return;
- case OP_LEGACY_STORAGE:
- updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED);
- return;
- }
+ // When using FUSE, we may need to kill the app if the op changes
+ switch(code) {
+ case OP_REQUEST_INSTALL_PACKAGES:
+ // Always kill regardless of op change, to remount apps /storage
+ killAppForOpChange(code, uid);
+ return;
+ case OP_MANAGE_EXTERNAL_STORAGE:
+ if (mode != MODE_ALLOWED) {
+ // Only kill if op is denied, to lose external_storage gid
+ // Killing when op is granted to pickup the gid automatically,
+ // results in a bad UX, especially since the gid only gives access
+ // to unreliable volumes, USB OTGs that are rarely mounted. The app
+ // will get the external_storage gid on next organic restart.
+ killAppForOpChange(code, uid);
+ }
+ return;
+ case OP_LEGACY_STORAGE:
+ updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED);
+ return;
}
if (mode == MODE_ALLOWED && (code == OP_READ_EXTERNAL_STORAGE
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 135fe0478426..a4c6c8751d93 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -63,7 +63,6 @@ import android.telephony.CellSignalStrengthLte;
import android.telephony.CellSignalStrengthNr;
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
-import android.telephony.DataFailCause;
import android.telephony.DisconnectCause;
import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneCapability;
@@ -1707,7 +1706,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
apn = preciseState.getDataConnectionApn();
state = preciseState.getState();
networkType = preciseState.getNetworkType();
- linkProps = preciseState.getDataConnectionLinkProperties();
+ linkProps = preciseState.getLinkProperties();
}
if (VDBG) {
log("notifyDataConnectionForSubscriber: subId=" + subId
@@ -1800,11 +1799,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mPreciseDataConnectionStates.get(phoneId).put(
apnType,
- new PreciseDataConnectionState(
- TelephonyManager.DATA_UNKNOWN,
- TelephonyManager.NETWORK_TYPE_UNKNOWN,
- apnType, null, null,
- DataFailCause.NONE, null));
+ new PreciseDataConnectionState.Builder()
+ .setApnTypes(apnType)
+ .build());
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
@@ -1986,11 +1983,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mPreciseDataConnectionStates.get(phoneId).put(
apnType,
- new PreciseDataConnectionState(
- TelephonyManager.DATA_UNKNOWN,
- TelephonyManager.NETWORK_TYPE_UNKNOWN,
- apnType, null, null,
- failCause, null));
+ new PreciseDataConnectionState.Builder()
+ .setApnTypes(apnType)
+ .setFailCause(failCause)
+ .build());
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
@@ -2642,6 +2638,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
shouldCheckLocationPermissions = true;
}
+ boolean isPermissionCheckSuccessful = true;
+
if (shouldCheckLocationPermissions) {
LocationAccessPolicy.LocationPermissionResult result =
LocationAccessPolicy.checkLocationPermission(
@@ -2651,14 +2649,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
throw new SecurityException("Unable to listen for events " + events + " due to "
+ "insufficient location permissions.");
case DENIED_SOFT:
- return false;
+ isPermissionCheckSuccessful = false;
}
}
if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mContext, subId, callingPackage, callingFeatureId, message)) {
- return false;
+ isPermissionCheckSuccessful = false;
}
}
@@ -2688,7 +2686,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
}
- return true;
+ return isPermissionCheckSuccessful;
}
private void handleRemoveListLocked() {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index be080e5cce62..915189c085c2 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -81,6 +81,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import static android.app.UiModeManager.DEFAULT_PRIORITY;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
import static android.app.UiModeManager.MODE_NIGHT_YES;
@@ -1446,6 +1447,8 @@ final class UiModeManagerService extends SystemService {
pw.println(" Print this help text.");
pw.println(" night [yes|no|auto|custom]");
pw.println(" Set or read night mode.");
+ pw.println(" car [yes|no]");
+ pw.println(" Set or read car mode.");
pw.println(" time [start|end] <ISO time>");
pw.println(" Set custom start/end schedule time"
+ " (night mode must be set to custom to apply).");
@@ -1461,6 +1464,8 @@ final class UiModeManagerService extends SystemService {
switch (cmd) {
case "night":
return handleNightMode();
+ case "car":
+ return handleCarMode();
case "time":
return handleCustomTime();
default:
@@ -1558,6 +1563,34 @@ final class UiModeManagerService extends SystemService {
return -1;
}
}
+
+ private int handleCarMode() throws RemoteException {
+ final PrintWriter err = getErrPrintWriter();
+ final String modeStr = getNextArg();
+ if (modeStr == null) {
+ printCurrentCarMode();
+ return 0;
+ }
+
+ if (modeStr.equals("yes")) {
+ mInterface.enableCarMode(0 /* flags */, DEFAULT_PRIORITY, "" /* package */);
+ printCurrentCarMode();
+ return 0;
+ } else if (modeStr.equals("no")) {
+ mInterface.disableCarMode(0 /* flags */);
+ printCurrentCarMode();
+ return 0;
+ } else {
+ err.println("Error: mode must be 'yes', or 'no'");
+ return -1;
+ }
+ }
+
+ private void printCurrentCarMode() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final int currMode = mInterface.getCurrentModeType();
+ pw.println("Car mode: " + (currMode == Configuration.UI_MODE_TYPE_CAR ? "yes" : "no"));
+ }
}
public final class LocalService extends UiModeManagerInternal {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 4ff9eb11c142..72f29b431880 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -43,6 +43,7 @@ import android.os.IBinder;
import android.os.IExternalVibratorService;
import android.os.IVibratorService;
import android.os.IVibratorStateListener;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
@@ -70,6 +71,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.InputDevice;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -134,7 +136,7 @@ public class VibratorService extends IVibratorService.Stub
private final SparseArray<VibrationEffect> mFallbackEffects;
private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
private final WorkSource mTmpWorkSource = new WorkSource();
- private final Handler mH = new Handler();
+ private final Handler mH;
private final Object mLock = new Object();
private final Context mContext;
@@ -147,6 +149,7 @@ public class VibratorService extends IVibratorService.Stub
private Vibrator mVibrator;
private SettingsObserver mSettingObserver;
+ private final NativeWrapper mNativeWrapper;
private volatile VibrateThread mThread;
// mInputDeviceVibrators lock should be acquired after mLock, if both are
@@ -208,7 +211,12 @@ public class VibratorService extends IVibratorService.Stub
}
};
- private class Vibration implements IBinder.DeathRecipient {
+ /**
+ * Holder for a vibration to be played. This class can be shared with native methods for
+ * hardware callback support.
+ */
+ @VisibleForTesting
+ public final class Vibration implements IBinder.DeathRecipient {
public final IBinder token;
// Start time in CLOCK_BOOTTIME base.
public final long startTime;
@@ -248,9 +256,9 @@ public class VibratorService extends IVibratorService.Stub
}
}
- // Called by native
- @SuppressWarnings("unused")
- private void onComplete() {
+ /** Callback for when vibration is complete, to be called by native. */
+ @VisibleForTesting
+ public void onComplete() {
synchronized (mLock) {
if (this == mCurrentVibration) {
doCancelVibrateLocked();
@@ -354,15 +362,23 @@ public class VibratorService extends IVibratorService.Stub
}
VibratorService(Context context) {
- vibratorInit();
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ VibratorService(Context context, Injector injector) {
+ mNativeWrapper = injector.getNativeWrapper();
+ mH = injector.createHandler(Looper.myLooper());
+
+ mNativeWrapper.vibratorInit();
// Reset the hardware to a default state, in case this is a runtime
// restart instead of a fresh boot.
- vibratorOff();
+ mNativeWrapper.vibratorOff();
- mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
- mSupportsExternalControl = vibratorSupportsExternalControl();
- mSupportedEffects = asList(vibratorGetSupportedEffects());
- mCapabilities = vibratorGetCapabilities();
+ mSupportsAmplitudeControl = mNativeWrapper.vibratorSupportsAmplitudeControl();
+ mSupportsExternalControl = mNativeWrapper.vibratorSupportsExternalControl();
+ mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects());
+ mCapabilities = mNativeWrapper.vibratorGetCapabilities();
mContext = context;
PowerManager pm = context.getSystemService(PowerManager.class);
@@ -419,7 +435,7 @@ public class VibratorService extends IVibratorService.Stub
mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH));
mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH));
- ServiceManager.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
+ injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
}
private VibrationEffect createEffectFromResource(int resId) {
@@ -642,7 +658,7 @@ public class VibratorService extends IVibratorService.Stub
if (effect == null) {
synchronized (mLock) {
mAlwaysOnEffects.delete(alwaysOnId);
- vibratorAlwaysOnDisable(alwaysOnId);
+ mNativeWrapper.vibratorAlwaysOnDisable(alwaysOnId);
}
} else {
if (!verifyVibrationEffect(effect)) {
@@ -1198,11 +1214,11 @@ public class VibratorService extends IVibratorService.Stub
private void updateAlwaysOnLocked(int id, Vibration vib) {
final int intensity = getCurrentIntensityLocked(vib);
if (!shouldVibrate(vib, intensity)) {
- vibratorAlwaysOnDisable(id);
+ mNativeWrapper.vibratorAlwaysOnDisable(id);
} else {
final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
final int strength = intensityToEffectStrength(intensity);
- vibratorAlwaysOnEnable(id, prebaked.getId(), strength);
+ mNativeWrapper.vibratorAlwaysOnEnable(id, prebaked.getId(), strength);
}
}
@@ -1238,7 +1254,7 @@ public class VibratorService extends IVibratorService.Stub
//synchronized (mInputDeviceVibrators) {
// return !mInputDeviceVibrators.isEmpty() || vibratorExists();
//}
- return vibratorExists();
+ return mNativeWrapper.vibratorExists();
}
private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) {
@@ -1262,7 +1278,7 @@ public class VibratorService extends IVibratorService.Stub
// Note: ordering is important here! Many haptic drivers will reset their
// amplitude when enabled, so we always have to enable first, then set the
// amplitude.
- vibratorOn(millis);
+ mNativeWrapper.vibratorOn(millis);
doVibratorSetAmplitude(amplitude);
}
}
@@ -1273,7 +1289,7 @@ public class VibratorService extends IVibratorService.Stub
private void doVibratorSetAmplitude(int amplitude) {
if (mSupportsAmplitudeControl) {
- vibratorSetAmplitude(amplitude);
+ mNativeWrapper.vibratorSetAmplitude(amplitude);
}
}
@@ -1291,7 +1307,7 @@ public class VibratorService extends IVibratorService.Stub
mInputDeviceVibrators.get(i).cancel();
}
} else {
- vibratorOff();
+ mNativeWrapper.vibratorOff();
}
}
} finally {
@@ -1310,7 +1326,7 @@ public class VibratorService extends IVibratorService.Stub
}
// Input devices don't support prebaked effect, so skip trying it with them.
if (!usingInputDeviceVibrators) {
- long duration = vibratorPerformEffect(prebaked.getId(),
+ long duration = mNativeWrapper.vibratorPerformEffect(prebaked.getId(),
prebaked.getEffectStrength(), vib,
hasCapability(IVibrator.CAP_PERFORM_CALLBACK));
long timeout = duration;
@@ -1363,7 +1379,7 @@ public class VibratorService extends IVibratorService.Stub
PrimitiveEffect[] primitiveEffects =
composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]);
- vibratorPerformComposedEffect(primitiveEffects, vib);
+ mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib);
// Composed effects don't actually give us an estimated duration, so we just guess here.
noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length);
@@ -1454,7 +1470,7 @@ public class VibratorService extends IVibratorService.Stub
}
}
mVibratorUnderExternalControl = externalControl;
- vibratorSetExternalControl(externalControl);
+ mNativeWrapper.vibratorSetExternalControl(externalControl);
}
private void dumpInternal(PrintWriter pw) {
@@ -1688,6 +1704,100 @@ public class VibratorService extends IVibratorService.Stub
}
}
+ /** Wrapper around the static-native methods of {@link VibratorService} for tests. */
+ @VisibleForTesting
+ public static class NativeWrapper {
+
+ /** Checks if vibrator exists on device. */
+ public boolean vibratorExists() {
+ return VibratorService.vibratorExists();
+ }
+
+ /** Initializes connection to vibrator HAL service. */
+ public void vibratorInit() {
+ VibratorService.vibratorInit();
+ }
+
+ /** Turns vibrator on for given time. */
+ public void vibratorOn(long milliseconds) {
+ VibratorService.vibratorOn(milliseconds);
+ }
+
+ /** Turns vibrator off. */
+ public void vibratorOff() {
+ VibratorService.vibratorOff();
+ }
+
+ /** Returns true if vibrator supports {@link #vibratorSetAmplitude(int)}. */
+ public boolean vibratorSupportsAmplitudeControl() {
+ return VibratorService.vibratorSupportsAmplitudeControl();
+ }
+
+ /** Sets the amplitude for the vibrator to run. */
+ public void vibratorSetAmplitude(int amplitude) {
+ VibratorService.vibratorSetAmplitude(amplitude);
+ }
+
+ /** Returns all predefined effects supported by the device vibrator. */
+ public int[] vibratorGetSupportedEffects() {
+ return VibratorService.vibratorGetSupportedEffects();
+ }
+
+ /** Turns vibrator on to perform one of the supported effects. */
+ public long vibratorPerformEffect(long effect, long strength, Vibration vibration,
+ boolean withCallback) {
+ return VibratorService.vibratorPerformEffect(effect, strength, vibration, withCallback);
+ }
+
+ /** Turns vibrator on to perform one of the supported composed effects. */
+ public void vibratorPerformComposedEffect(
+ VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) {
+ VibratorService.vibratorPerformComposedEffect(effect, vibration);
+ }
+
+ /** Returns true if vibrator supports {@link #vibratorSetExternalControl(boolean)}. */
+ public boolean vibratorSupportsExternalControl() {
+ return VibratorService.vibratorSupportsExternalControl();
+ }
+
+ /** Enabled the device vibrator to be controlled by another service. */
+ public void vibratorSetExternalControl(boolean enabled) {
+ VibratorService.vibratorSetExternalControl(enabled);
+ }
+
+ /** Returns all capabilities of the device vibrator. */
+ public long vibratorGetCapabilities() {
+ return VibratorService.vibratorGetCapabilities();
+ }
+
+ /** Enable always-on vibration with given id and effect. */
+ public void vibratorAlwaysOnEnable(long id, long effect, long strength) {
+ VibratorService.vibratorAlwaysOnEnable(id, effect, strength);
+ }
+
+ /** Disable always-on vibration for given id. */
+ public void vibratorAlwaysOnDisable(long id) {
+ VibratorService.vibratorAlwaysOnDisable(id);
+ }
+ }
+
+ /** Point of injection for test dependencies */
+ @VisibleForTesting
+ static class Injector {
+
+ NativeWrapper getNativeWrapper() {
+ return new NativeWrapper();
+ }
+
+ Handler createHandler(Looper looper) {
+ return new Handler(looper);
+ }
+
+ void addService(String name, IBinder service) {
+ ServiceManager.addService(name, service);
+ }
+ }
+
BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 49676ded1d4e..1815dac4c3a8 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -23,7 +23,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.hidl.manager.V1_0.IServiceManager;
import android.os.Binder;
-import android.os.Build;
import android.os.Debug;
import android.os.Handler;
import android.os.IPowerManager;
@@ -32,10 +31,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.StructRlimit;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -51,13 +46,8 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -143,7 +133,6 @@ public class Watchdog {
private IActivityController mController;
private boolean mAllowRestart = true;
- private final OpenFdMonitor mOpenFdMonitor;
private final List<Integer> mInterestingJavaPids = new ArrayList<>();
/**
@@ -349,8 +338,6 @@ public class Watchdog {
// Initialize monitor for Binder threads.
addMonitor(new BinderThreadMonitor());
- mOpenFdMonitor = OpenFdMonitor.create();
-
mInterestingJavaPids.add(Process.myPid());
// See the notes on DEFAULT_TIMEOUT.
@@ -602,40 +589,30 @@ public class Watchdog {
timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
}
- boolean fdLimitTriggered = false;
- if (mOpenFdMonitor != null) {
- fdLimitTriggered = mOpenFdMonitor.monitor();
- }
-
- if (!fdLimitTriggered) {
- final int waitState = evaluateCheckerCompletionLocked();
- if (waitState == COMPLETED) {
- // The monitors have returned; reset
- waitedHalf = false;
- continue;
- } else if (waitState == WAITING) {
- // still waiting but within their configured intervals; back off and recheck
- continue;
- } else if (waitState == WAITED_HALF) {
- if (!waitedHalf) {
- Slog.i(TAG, "WAITED_HALF");
- // We've waited half the deadlock-detection interval. Pull a stack
- // trace and wait another half.
- ArrayList<Integer> pids = new ArrayList<>(mInterestingJavaPids);
- ActivityManagerService.dumpStackTraces(pids, null, null,
- getInterestingNativePids(), null);
- waitedHalf = true;
- }
- continue;
+ final int waitState = evaluateCheckerCompletionLocked();
+ if (waitState == COMPLETED) {
+ // The monitors have returned; reset
+ waitedHalf = false;
+ continue;
+ } else if (waitState == WAITING) {
+ // still waiting but within their configured intervals; back off and recheck
+ continue;
+ } else if (waitState == WAITED_HALF) {
+ if (!waitedHalf) {
+ Slog.i(TAG, "WAITED_HALF");
+ // We've waited half the deadlock-detection interval. Pull a stack
+ // trace and wait another half.
+ ArrayList<Integer> pids = new ArrayList<>(mInterestingJavaPids);
+ ActivityManagerService.dumpStackTraces(pids, null, null,
+ getInterestingNativePids(), null);
+ waitedHalf = true;
}
-
- // something is overdue!
- blockedCheckers = getBlockedCheckersLocked();
- subject = describeCheckersLocked(blockedCheckers);
- } else {
- blockedCheckers = Collections.emptyList();
- subject = "Open FD high water mark reached";
+ continue;
}
+
+ // something is overdue!
+ blockedCheckers = getBlockedCheckersLocked();
+ subject = describeCheckersLocked(blockedCheckers);
allowRestart = mAllowRestart;
}
@@ -738,94 +715,4 @@ public class Watchdog {
Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e);
}
}
-
- public static final class OpenFdMonitor {
- /**
- * Number of FDs below the soft limit that we trigger a runtime restart at. This was
- * chosen arbitrarily, but will need to be at least 6 in order to have a sufficient number
- * of FDs in reserve to complete a dump.
- */
- private static final int FD_HIGH_WATER_MARK = 12;
-
- private final File mDumpDir;
- private final File mFdHighWaterMark;
-
- public static OpenFdMonitor create() {
- // Only run the FD monitor on debuggable builds (such as userdebug and eng builds).
- if (!Build.IS_DEBUGGABLE) {
- return null;
- }
-
- final StructRlimit rlimit;
- try {
- rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE);
- } catch (ErrnoException errno) {
- Slog.w(TAG, "Error thrown from getrlimit(RLIMIT_NOFILE)", errno);
- return null;
- }
-
- // The assumption we're making here is that FD numbers are allocated (more or less)
- // sequentially, which is currently (and historically) true since open is currently
- // specified to always return the lowest-numbered non-open file descriptor for the
- // current process.
- //
- // We do this to avoid having to enumerate the contents of /proc/self/fd in order to
- // count the number of descriptors open in the process.
- final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK));
- return new OpenFdMonitor(new File("/data/anr"), fdThreshold);
- }
-
- OpenFdMonitor(File dumpDir, File fdThreshold) {
- mDumpDir = dumpDir;
- mFdHighWaterMark = fdThreshold;
- }
-
- /**
- * Dumps open file descriptors and their full paths to a temporary file in {@code mDumpDir}.
- */
- private void dumpOpenDescriptors() {
- // We cannot exec lsof to get more info about open file descriptors because a newly
- // forked process will not have the permissions to readlink. Instead list all open
- // descriptors from /proc/pid/fd and resolve them.
- List<String> dumpInfo = new ArrayList<>();
- String fdDirPath = String.format("/proc/%d/fd/", Process.myPid());
- File[] fds = new File(fdDirPath).listFiles();
- if (fds == null) {
- dumpInfo.add("Unable to list " + fdDirPath);
- } else {
- for (File f : fds) {
- String fdSymLink = f.getAbsolutePath();
- String resolvedPath = "";
- try {
- resolvedPath = Os.readlink(fdSymLink);
- } catch (ErrnoException ex) {
- resolvedPath = ex.getMessage();
- }
- dumpInfo.add(fdSymLink + "\t" + resolvedPath);
- }
- }
-
- // Dump the fds & paths to a temp file.
- try {
- File dumpFile = File.createTempFile("anr_fd_", "", mDumpDir);
- Path out = Paths.get(dumpFile.getAbsolutePath());
- Files.write(out, dumpInfo, StandardCharsets.UTF_8);
- } catch (IOException ex) {
- Slog.w(TAG, "Unable to write open descriptors to file: " + ex);
- }
- }
-
- /**
- * @return {@code true} if the high water mark was breached and a dump was written,
- * {@code false} otherwise.
- */
- public boolean monitor() {
- if (mFdHighWaterMark.exists()) {
- dumpOpenDescriptors();
- return true;
- }
-
- return false;
- }
- }
}
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index df3b6880fdfb..ed83a644cbfb 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -878,6 +878,7 @@ public class AdbDebuggingManager {
case MESSAGE_ADB_DENY:
if (mThread != null) {
+ Slog.w(TAG, "Denying adb confirmation");
mThread.sendResponse("NO");
logAdbConnectionChanged(null, AdbProtoEnums.USER_DENIED, false);
}
@@ -887,7 +888,7 @@ public class AdbDebuggingManager {
String key = (String) msg.obj;
if ("trigger_restart_min_framework".equals(
SystemProperties.get("vold.decrypt"))) {
- Slog.d(TAG, "Deferring adb confirmation until after vold decrypt");
+ Slog.w(TAG, "Deferring adb confirmation until after vold decrypt");
if (mThread != null) {
mThread.sendResponse("NO");
logAdbConnectionChanged(key, AdbProtoEnums.DENIED_VOLD_DECRYPT, false);
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index ef81d7159c42..29bb5428dd84 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -34,6 +34,7 @@ import android.hardware.usb.UsbManager;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -509,6 +510,14 @@ public class AdbService extends IAdbManager.Stub {
}
@Override
+ public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
+ ParcelFileDescriptor err, String[] args) {
+ return new AdbShellCommand(this).exec(
+ this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+ args);
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
diff --git a/services/core/java/com/android/server/adb/AdbShellCommand.java b/services/core/java/com/android/server/adb/AdbShellCommand.java
new file mode 100644
index 000000000000..76918529d071
--- /dev/null
+++ b/services/core/java/com/android/server/adb/AdbShellCommand.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.adb;
+
+import android.os.BasicShellCommandHandler;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * Interprets and executes 'adb shell cmd adb [args]'.
+ */
+class AdbShellCommand extends BasicShellCommandHandler {
+
+ private final AdbService mService;
+
+ AdbShellCommand(AdbService service) {
+ mService = Objects.requireNonNull(service);
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(null);
+ }
+
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "is-wifi-supported": {
+ pw.println(Boolean.toString(mService.isAdbWifiSupported()));
+ return 0;
+ }
+ case "is-wifi-qr-supported": {
+ pw.println(Boolean.toString(mService.isAdbWifiQrSupported()));
+ return 0;
+ }
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Adb service commands:");
+ pw.println(" help or -h");
+ pw.println(" Print this help text.");
+ pw.println(" is-wifi-supported");
+ pw.println(" Returns \"true\" if adb over wifi is supported.");
+ pw.println(" is-wifi-qr-supported");
+ pw.println(" Returns \"true\" if adb over wifi + QR pairing is supported.");
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 56953708e383..bd51c7a1773d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -18,7 +18,6 @@ package com.android.server.am;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
import static android.os.Process.NFC_UID;
import static android.os.Process.ROOT_UID;
@@ -216,7 +215,7 @@ public final class ActiveServices {
* Watch for apps being put into forced app standby, so we can step their fg
* services down.
*/
- class ForcedStandbyListener extends AppStateTracker.Listener {
+ class ForcedStandbyListener implements AppStateTracker.ServiceStateListener {
@Override
public void stopForegroundServicesForUidPackage(final int uid, final String packageName) {
synchronized (mAm) {
@@ -404,7 +403,7 @@ public final class ActiveServices {
void systemServicesReady() {
AppStateTracker ast = LocalServices.getService(AppStateTracker.class);
- ast.addListener(new ForcedStandbyListener());
+ ast.addServiceStateListener(new ForcedStandbyListener());
mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class);
setWhiteListAllowWhileInUsePermissionInFgs();
}
@@ -691,7 +690,7 @@ public final class ActiveServices {
}
if (allowBackgroundActivityStarts) {
- r.whitelistBgActivityStartsOnServiceStart();
+ r.allowBgActivityStartsOnServiceStart();
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
@@ -1361,13 +1360,12 @@ public final class ActiveServices {
+ String.format("0x%08X", manifestType)
+ " in service element of manifest file");
}
- if ((foregroundServiceType & FOREGROUND_SERVICE_TYPE_LOCATION) != 0
- && !r.mAllowWhileInUsePermissionInFgs) {
- // If the foreground service is not started from TOP process, do not allow it to
- // have location capability, this prevents BG started FGS to have while-in-use
- // location permission.
+ // If the foreground service is not started from TOP process, do not allow it to
+ // have while-in-use location/camera/microphone access.
+ if (!r.mAllowWhileInUsePermissionInFgs) {
Slog.w(TAG,
- "BG started FGS can not have location capability: service "
+ "Foreground service started from background can not have "
+ + "location/camera/microphone access: service "
+ r.shortInstanceName);
}
}
@@ -2047,7 +2045,7 @@ public final class ActiveServices {
s.whitelistManager = true;
}
if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
- s.setHasBindingWhitelistingBgActivityStarts(true);
+ s.setAllowedBgActivityStartsByBinding(true);
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
@@ -3459,9 +3457,9 @@ public final class ActiveServices {
updateWhitelistManagerLocked(s.app);
}
}
- // And do the same for bg activity starts whitelisting.
+ // And do the same for bg activity starts ability.
if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
- s.updateHasBindingWhitelistingBgActivityStarts();
+ s.updateIsAllowedBgActivityStartsByBinding();
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 135ac9a7846e..775119c18037 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
import android.app.ActivityThread;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -252,7 +253,8 @@ final class ActivityManagerConstants extends ContentObserver {
// allowing the next pending start to run.
public long BG_START_TIMEOUT = DEFAULT_BG_START_TIMEOUT;
- // For how long after a whitelisted service's start its process can start a background activity
+ // For a service that has been allowed to start background activities, how long after it started
+ // its process can start a background activity.
public long SERVICE_BG_ACTIVITY_START_TIMEOUT = DEFAULT_SERVICE_BG_ACTIVITY_START_TIMEOUT;
// Initial backoff delay for retrying bound foreground services
@@ -336,6 +338,11 @@ final class ActivityManagerConstants extends ContentObserver {
*/
public int PENDINGINTENT_WARNING_THRESHOLD = DEFAULT_PENDINGINTENT_WARNING_THRESHOLD;
+ /**
+ * Component names of the services which will keep critical code path of the host warm
+ */
+ public final ArraySet<ComponentName> KEEP_WARMING_SERVICES = new ArraySet<ComponentName>();
+
private List<String> mDefaultImperceptibleKillExemptPackages;
private List<Integer> mDefaultImperceptibleKillExemptProcStates;
@@ -384,6 +391,33 @@ final class ActivityManagerConstants extends ContentObserver {
public static long MIN_ASSOC_LOG_DURATION = DEFAULT_MIN_ASSOC_LOG_DURATION;
+ private static final String KEY_BINDER_HEAVY_HITTER_WATCHER_ENABLED =
+ "binder_heavy_hitter_watcher_enabled";
+ private static final String KEY_BINDER_HEAVY_HITTER_WATCHER_BATCHSIZE =
+ "binder_heavy_hitter_watcher_batchsize";
+ private static final String KEY_BINDER_HEAVY_HITTER_WATCHER_THRESHOLD =
+ "binder_heavy_hitter_watcher_threshold";
+ private static final String KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED =
+ "binder_heavy_hitter_auto_sampler_enabled";
+ private static final String KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE =
+ "binder_heavy_hitter_auto_sampler_batchsize";
+ private static final String KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD =
+ "binder_heavy_hitter_auto_sampler_threshold";
+
+ private final boolean mDefaultBinderHeavyHitterWatcherEnabled;
+ private final int mDefaultBinderHeavyHitterWatcherBatchSize;
+ private final float mDefaultBinderHeavyHitterWatcherThreshold;
+ private final boolean mDefaultBinderHeavyHitterAutoSamplerEnabled;
+ private final int mDefaultBinderHeavyHitterAutoSamplerBatchSize;
+ private final float mDefaultBinderHeavyHitterAutoSamplerThreshold;
+
+ public static boolean BINDER_HEAVY_HITTER_WATCHER_ENABLED;
+ public static int BINDER_HEAVY_HITTER_WATCHER_BATCHSIZE;
+ public static float BINDER_HEAVY_HITTER_WATCHER_THRESHOLD;
+ public static boolean BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED;
+ public static int BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE;
+ public static float BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD;
+
private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -415,6 +449,14 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_MIN_ASSOC_LOG_DURATION:
updateMinAssocLogDuration();
break;
+ case KEY_BINDER_HEAVY_HITTER_WATCHER_ENABLED:
+ case KEY_BINDER_HEAVY_HITTER_WATCHER_BATCHSIZE:
+ case KEY_BINDER_HEAVY_HITTER_WATCHER_THRESHOLD:
+ case KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED:
+ case KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE:
+ case KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD:
+ updateBinderHeavyHitterWatcher();
+ break;
default:
break;
}
@@ -442,6 +484,29 @@ final class ActivityManagerConstants extends ContentObserver {
.boxed().collect(Collectors.toList());
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.addAll(mDefaultImperceptibleKillExemptProcStates);
+ mDefaultBinderHeavyHitterWatcherEnabled = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_defaultBinderHeavyHitterWatcherEnabled);
+ mDefaultBinderHeavyHitterWatcherBatchSize = context.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultBinderHeavyHitterWatcherBatchSize);
+ mDefaultBinderHeavyHitterWatcherThreshold = context.getResources().getFloat(
+ com.android.internal.R.dimen.config_defaultBinderHeavyHitterWatcherThreshold);
+ mDefaultBinderHeavyHitterAutoSamplerEnabled = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_defaultBinderHeavyHitterAutoSamplerEnabled);
+ mDefaultBinderHeavyHitterAutoSamplerBatchSize = context.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultBinderHeavyHitterAutoSamplerBatchSize);
+ mDefaultBinderHeavyHitterAutoSamplerThreshold = context.getResources().getFloat(
+ com.android.internal.R.dimen.config_defaultBinderHeavyHitterAutoSamplerThreshold);
+ BINDER_HEAVY_HITTER_WATCHER_ENABLED = mDefaultBinderHeavyHitterWatcherEnabled;
+ BINDER_HEAVY_HITTER_WATCHER_BATCHSIZE = mDefaultBinderHeavyHitterWatcherBatchSize;
+ BINDER_HEAVY_HITTER_WATCHER_THRESHOLD = mDefaultBinderHeavyHitterWatcherThreshold;
+ BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED = mDefaultBinderHeavyHitterAutoSamplerEnabled;
+ BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE = mDefaultBinderHeavyHitterAutoSamplerBatchSize;
+ BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD = mDefaultBinderHeavyHitterAutoSamplerThreshold;
+ service.scheduleUpdateBinderHeavyHitterWatcherConfig();
+ KEEP_WARMING_SERVICES.addAll(Arrays.stream(
+ context.getResources().getStringArray(
+ com.android.internal.R.array.config_keep_warming_services))
+ .map(ComponentName::unflattenFromString).collect(Collectors.toSet()));
}
public void start(ContentResolver resolver) {
@@ -687,6 +752,31 @@ final class ActivityManagerConstants extends ContentObserver {
/* defaultValue */ DEFAULT_MIN_ASSOC_LOG_DURATION);
}
+ private void updateBinderHeavyHitterWatcher() {
+ BINDER_HEAVY_HITTER_WATCHER_ENABLED = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_BINDER_HEAVY_HITTER_WATCHER_ENABLED,
+ mDefaultBinderHeavyHitterWatcherEnabled);
+ BINDER_HEAVY_HITTER_WATCHER_BATCHSIZE = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_BINDER_HEAVY_HITTER_WATCHER_BATCHSIZE,
+ mDefaultBinderHeavyHitterWatcherBatchSize);
+ BINDER_HEAVY_HITTER_WATCHER_THRESHOLD = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_BINDER_HEAVY_HITTER_WATCHER_THRESHOLD,
+ mDefaultBinderHeavyHitterWatcherThreshold);
+ BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED,
+ mDefaultBinderHeavyHitterAutoSamplerEnabled);
+ BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE,
+ mDefaultBinderHeavyHitterAutoSamplerBatchSize);
+ BINDER_HEAVY_HITTER_WATCHER_THRESHOLD = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD,
+ mDefaultBinderHeavyHitterAutoSamplerThreshold);
+ mService.scheduleUpdateBinderHeavyHitterWatcherConfig();
+ }
+
void dump(PrintWriter pw) {
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
+ Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":");
@@ -759,6 +849,18 @@ final class ActivityManagerConstants extends ContentObserver {
pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.toArray()));
pw.print(" "); pw.print(KEY_MIN_ASSOC_LOG_DURATION); pw.print("=");
pw.println(MIN_ASSOC_LOG_DURATION);
+ pw.print(" "); pw.print(KEY_BINDER_HEAVY_HITTER_WATCHER_ENABLED); pw.print("=");
+ pw.println(BINDER_HEAVY_HITTER_WATCHER_ENABLED);
+ pw.print(" "); pw.print(KEY_BINDER_HEAVY_HITTER_WATCHER_BATCHSIZE); pw.print("=");
+ pw.println(BINDER_HEAVY_HITTER_WATCHER_BATCHSIZE);
+ pw.print(" "); pw.print(KEY_BINDER_HEAVY_HITTER_WATCHER_THRESHOLD); pw.print("=");
+ pw.println(BINDER_HEAVY_HITTER_WATCHER_THRESHOLD);
+ pw.print(" "); pw.print(KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED); pw.print("=");
+ pw.println(BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED);
+ pw.print(" "); pw.print(KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE); pw.print("=");
+ pw.println(BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE);
+ pw.print(" "); pw.print(KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD); pw.print("=");
+ pw.println(BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 27c014b91c03..cfd2bf913b9c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -82,8 +82,6 @@ import static android.os.Process.removeAllProcessGroups;
import static android.os.Process.sendSignal;
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
-import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED;
-import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
@@ -234,7 +232,6 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
-import android.location.LocationManager;
import android.media.audiofx.AudioEffect;
import android.net.Proxy;
import android.net.Uri;
@@ -294,6 +291,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -313,6 +311,7 @@ import android.view.autofill.AutofillManagerInternal;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.ProcessMap;
@@ -323,7 +322,10 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
+import com.android.internal.os.BinderCallHeavyHitterWatcher.HeavyHitterContainer;
import com.android.internal.os.BinderInternal;
+import com.android.internal.os.BinderTransactionNameResolver;
import com.android.internal.os.ByteTransferPipe;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
@@ -657,6 +659,14 @@ public class ActivityManagerService extends IActivityManager.Stub
int mUidChangeDispatchCount;
/**
+ * Uids of apps with current active camera sessions. Access synchronized on
+ * the IntArray instance itself, and no other locks must be acquired while that
+ * one is held.
+ */
+ @GuardedBy("mActiveCameraUids")
+ final IntArray mActiveCameraUids = new IntArray(4);
+
+ /**
* Helper class which strips out priority and proto arguments then calls the dump function with
* the appropriate arguments. If priority arguments are omitted, function calls the legacy
* dump command.
@@ -1526,7 +1536,7 @@ public class ActivityManagerService extends IActivityManager.Stub
void onOomAdjMessage(String msg);
}
- final AnrHelper mAnrHelper = new AnrHelper();
+ final AnrHelper mAnrHelper = new AnrHelper(this);
/**
* Runtime CPU use collection thread. This object's lock is used to
@@ -1624,6 +1634,7 @@ public class ActivityManagerService extends IActivityManager.Stub
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
static final int KILL_APP_ZYGOTE_MSG = 71;
+ static final int BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG = 72;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1669,11 +1680,20 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
@Nullable ContentCaptureManagerInternal mContentCaptureService;
+ /*
+ * The default duration for the binder heavy hitter auto sampler
+ */
+ private static final long BINDER_HEAVY_HITTER_AUTO_SAMPLER_DURATION_MS = 300000L;
+
+ /**
+ * The default throttling duration for the binder heavy hitter auto sampler
+ */
+ private static final long BINDER_HEAVY_HITTER_AUTO_SAMPLER_THROTTLE_MS = 3600000L;
+
/**
- * Set of {@link ProcessRecord} that have either {@link ProcessRecord#hasTopUi()} or
- * {@link ProcessRecord#runningRemoteAnimation} set to {@code true}.
+ * The last time when the binder heavy hitter auto sampler started.
*/
- final ArraySet<ProcessRecord> mTopUiOrRunningRemoteAnimApps = new ArraySet<>();
+ private long mLastBinderHeavyHitterAutoSamplerStart = 0L;
final class UiHandler extends Handler {
public UiHandler() {
@@ -1948,6 +1968,9 @@ public class ActivityManagerService extends IActivityManager.Stub
mProcessList.handleAllTrustStorageUpdateLocked();
}
} break;
+ case BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG: {
+ handleBinderHeavyHitterAutoSamplerTimeOut();
+ } break;
}
}
}
@@ -2040,7 +2063,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (proc != null) {
long startTime = SystemClock.currentThreadTimeMillis();
- long pss = Debug.getPss(pid, tmp, null);
+ // skip background PSS calculation of apps that are capturing
+ // camera imagery
+ final boolean usingCamera = isCameraActiveForUid(proc.uid);
+ long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
long endTime = SystemClock.currentThreadTimeMillis();
synchronized (ActivityManagerService.this) {
if (pss != 0 && proc.thread != null && proc.setProcState == procState
@@ -2053,6 +2079,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ProcessList.abortNextPssTime(proc.procStateMemTracker);
if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
": " + (proc.thread == null ? "NO_THREAD " : "") +
+ (usingCamera ? "CAMERA " : "") +
(proc.pid != pid ? "PID_CHANGED " : "") +
" initState=" + procState + " curState=" +
proc.setProcState + " " +
@@ -2142,6 +2169,14 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
});
+
+ final int[] cameraOp = {AppOpsManager.OP_CAMERA};
+ mAppOpsService.startWatchingActive(cameraOp, new IAppOpsActiveCallback.Stub() {
+ @Override
+ public void opActiveChanged(int op, int uid, String packageName, boolean active) {
+ cameraActiveChanged(uid, active);
+ }
+ });
}
public void setWindowManager(WindowManagerService wm) {
@@ -3304,6 +3339,9 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public boolean setProcessMemoryTrimLevel(String process, int userId, int level)
throws RemoteException {
+ if (!isCallerShell()) {
+ throw new SecurityException("Only shell can call it");
+ }
synchronized (this) {
final ProcessRecord app = findProcessLocked(process, userId, "setProcessMemoryTrimLevel");
if (app == null) {
@@ -4884,7 +4922,13 @@ public class ActivityManagerService extends IActivityManager.Stub
mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
}
- boolean didSomething = mProcessList.killPackageProcessesLocked(packageName, appId, userId,
+ // Notify first that the package is stopped, so its process won't be restarted unexpectedly
+ // if there is an activity of the package without attached process becomes visible when
+ // killing its other processes with visible activities.
+ boolean didSomething =
+ mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);
+
+ didSomething |= mProcessList.killPackageProcessesLocked(packageName, appId, userId,
ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
evenPersistent, true /* setRemoved */,
packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
@@ -4893,9 +4937,6 @@ public class ActivityManagerService extends IActivityManager.Stub
(packageName == null ? ("stop user " + userId) : ("stop " + packageName))
+ " due to " + reason);
- didSomething |=
- mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);
-
if (mServices.bringDownDisabledPackageServicesLocked(
packageName, null /* filterByClasses */, userId, evenPersistent, doit)) {
if (!doit) {
@@ -5996,9 +6037,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- private boolean isAppBad(ApplicationInfo info) {
+ private boolean isAppBad(final String processName, final int uid) {
synchronized (this) {
- return mAppErrors.isBadProcessLocked(info);
+ return mAppErrors.isBadProcessLocked(processName, uid);
}
}
@@ -6066,7 +6107,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mPendingStartActivityUids.isPendingTopPid(pr.uid, pids[i]);
states[i] = isPendingTop ? PROCESS_STATE_TOP : pr.getCurProcState();
if (scores != null) {
- scores[i] = isPendingTop ? ProcessList.FOREGROUND_APP_ADJ : pr.curAdj;
+ scores[i] = isPendingTop ? (ProcessList.FOREGROUND_APP_ADJ - 1) : pr.curAdj;
}
} else {
states[i] = PROCESS_STATE_NONEXISTENT;
@@ -6342,6 +6383,9 @@ public class ActivityManagerService extends IActivityManager.Stub
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
+ if (mInternal.isPendingTopUid(uid)) {
+ return ActivityManager.APP_START_MODE_NORMAL;
+ }
UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
+ packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
@@ -8052,6 +8096,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
+ if (Thread.holdsLock(mActivityTaskManager.getGlobalLock())) {
+ Slog.wtf(TAG, new IllegalStateException("Unable to check Uri permission"
+ + " because caller is holding WM lock; assuming permission denied"));
+ return PackageManager.PERMISSION_DENIED;
+ }
+
final String name = uri.getAuthority();
final long ident = Binder.clearCallingIdentity();
ContentProviderHolder holder = null;
@@ -9209,16 +9259,31 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
final long identity = Binder.clearCallingIdentity();
try {
- boolean permissionChange = KILL_APP_REASON_PERMISSIONS_REVOKED.equals(reason)
- || KILL_APP_REASON_GIDS_CHANGED.equals(reason);
mProcessList.killPackageProcessesLocked(null /* packageName */, appId, userId,
ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
true /* callerWillRestart */, true /* doit */, true /* evenPersistent */,
false /* setRemoved */,
- permissionChange ? ApplicationExitInfo.REASON_PERMISSION_CHANGE
- : ApplicationExitInfo.REASON_OTHER,
- permissionChange ? ApplicationExitInfo.SUBREASON_UNKNOWN
- : ApplicationExitInfo.SUBREASON_KILL_UID,
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_KILL_UID,
+ reason != null ? reason : "kill uid");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void killUidForPermissionChange(int appId, int userId, String reason) {
+ enforceCallingPermission(Manifest.permission.KILL_UID, "killUid");
+ synchronized (this) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mProcessList.killPackageProcessesLocked(null /* packageName */, appId, userId,
+ ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
+ true /* callerWillRestart */, true /* doit */, true /* evenPersistent */,
+ false /* setRemoved */,
+ ApplicationExitInfo.REASON_PERMISSION_CHANGE,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
reason != null ? reason : "kill uid");
} finally {
Binder.restoreCallingIdentity(identity);
@@ -14714,7 +14779,6 @@ public class ActivityManagerService extends IActivityManager.Stub
mProcessesToGc.remove(app);
mPendingPssProcesses.remove(app);
- mTopUiOrRunningRemoteAnimApps.remove(app);
ProcessList.abortNextPssTime(app.procStateMemTracker);
// Dismiss any open dialogs.
@@ -15823,7 +15887,6 @@ public class ActivityManagerService extends IActivityManager.Stub
|| Intent.ACTION_FACTORY_RESET.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
- || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
|| TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
|| SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)
|| AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)
@@ -18225,6 +18288,27 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ final void cameraActiveChanged(@UserIdInt int uid, boolean active) {
+ synchronized (mActiveCameraUids) {
+ final int curIndex = mActiveCameraUids.indexOf(uid);
+ if (active) {
+ if (curIndex < 0) {
+ mActiveCameraUids.add(uid);
+ }
+ } else {
+ if (curIndex >= 0) {
+ mActiveCameraUids.remove(curIndex);
+ }
+ }
+ }
+ }
+
+ final boolean isCameraActiveForUid(@UserIdInt int uid) {
+ synchronized (mActiveCameraUids) {
+ return mActiveCameraUids.indexOf(uid) >= 0;
+ }
+ }
+
@GuardedBy("this")
final void doStopUidLocked(int uid, final UidRecord uidRec) {
mServices.stopInBackgroundLocked(uid);
@@ -18288,11 +18372,15 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- // Now safely dispatch changes to device idle controller.
- for (int i = 0; i < N; i++) {
- PendingTempWhitelist ptw = list[i];
- mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
- ptw.duration, true, ptw.tag);
+ // Now safely dispatch changes to device idle controller. Skip this if we're early
+ // in boot and the controller hasn't yet been brought online: we do not apply
+ // device idle policy anyway at this phase.
+ if (mLocalDeviceIdleController != null) {
+ for (int i = 0; i < N; i++) {
+ PendingTempWhitelist ptw = list[i];
+ mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
+ ptw.duration, true, ptw.tag);
+ }
}
// And now we can safely remove them from the map.
@@ -18524,22 +18612,6 @@ public class ActivityManagerService extends IActivityManager.Stub
return proc;
}
- /**
- * @return {@code true} if {@link #mTopUiOrRunningRemoteAnimApps} set contains {@code app} or when there are no apps
- * in this list, an false otherwise.
- */
- boolean containsTopUiOrRunningRemoteAnimOrEmptyLocked(ProcessRecord app) {
- return mTopUiOrRunningRemoteAnimApps.isEmpty() || mTopUiOrRunningRemoteAnimApps.contains(app);
- }
-
- void addTopUiOrRunningRemoteAnim(ProcessRecord app) {
- mTopUiOrRunningRemoteAnimApps.add(app);
- }
-
- void removeTopUiOrRunningRemoteAnim(ProcessRecord app) {
- mTopUiOrRunningRemoteAnimApps.remove(app);
- }
-
@Override
public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo,
boolean runGc, String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) {
@@ -19687,8 +19759,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public boolean isAppBad(ApplicationInfo info) {
- return ActivityManagerService.this.isAppBad(info);
+ public boolean isAppBad(final String processName, final int uid) {
+ return ActivityManagerService.this.isAppBad(processName, uid);
}
@Override
@@ -20034,6 +20106,130 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
+ * Update the binder call heavy hitter watcher per the new configuration
+ */
+ void scheduleUpdateBinderHeavyHitterWatcherConfig() {
+ // There are two sets of configs: the default watcher and the auto sampler,
+ // the default one takes precedence. System would kick off auto sampler when there is
+ // an anomaly (i.e., consecutive ANRs), but it'll be stopped automatically after a while.
+ mHandler.post(() -> {
+ final boolean enabled;
+ final int batchSize;
+ final float threshold;
+ final BinderCallHeavyHitterListener listener;
+ synchronized (ActivityManagerService.this) {
+ if (mConstants.BINDER_HEAVY_HITTER_WATCHER_ENABLED) {
+ // Default watcher takes precedence, ignore the auto sampler.
+ mHandler.removeMessages(BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG);
+ // Set the watcher with the default watcher's config
+ enabled = true;
+ batchSize = mConstants.BINDER_HEAVY_HITTER_WATCHER_BATCHSIZE;
+ threshold = mConstants.BINDER_HEAVY_HITTER_WATCHER_THRESHOLD;
+ listener = (a, b, c, d) -> mHandler.post(
+ () -> handleBinderHeavyHitters(a, b, c, d));
+ } else if (mHandler.hasMessages(BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG)) {
+ // There is an ongoing auto sampler session, update it
+ enabled = mConstants.BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED;
+ batchSize = mConstants.BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE;
+ threshold = mConstants.BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD;
+ listener = (a, b, c, d) -> mHandler.post(
+ () -> handleBinderHeavyHitters(a, b, c, d));
+ } else {
+ // Stop it
+ enabled = false;
+ batchSize = 0;
+ threshold = 0.0f;
+ listener = null;
+ }
+ }
+ Binder.setHeavyHitterWatcherConfig(enabled, batchSize, threshold, listener);
+ });
+ }
+
+ /**
+ * Kick off the watcher to run for given timeout, it could be throttled however.
+ */
+ void scheduleBinderHeavyHitterAutoSampler() {
+ mHandler.post(() -> {
+ final int batchSize;
+ final float threshold;
+ final long now;
+ synchronized (ActivityManagerService.this) {
+ if (!mConstants.BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED) {
+ // It's configured OFF
+ return;
+ }
+ if (mConstants.BINDER_HEAVY_HITTER_WATCHER_ENABLED) {
+ // If the default watcher is active already, don't start the auto sampler
+ return;
+ }
+ now = SystemClock.uptimeMillis();
+ if (mLastBinderHeavyHitterAutoSamplerStart
+ + BINDER_HEAVY_HITTER_AUTO_SAMPLER_THROTTLE_MS > now) {
+ // Too frequent, throttle it
+ return;
+ }
+ batchSize = mConstants.BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE;
+ threshold = mConstants.BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD;
+ }
+ // No lock is needed because we are accessing these variables in handle thread only.
+ mLastBinderHeavyHitterAutoSamplerStart = now;
+ // Start the watcher with the auto sampler's config.
+ Binder.setHeavyHitterWatcherConfig(true, batchSize, threshold,
+ (a, b, c, d) -> mHandler.post(() -> handleBinderHeavyHitters(a, b, c, d)));
+ // Schedule to stop it after given timeout.
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG),
+ BINDER_HEAVY_HITTER_AUTO_SAMPLER_DURATION_MS);
+ });
+ }
+
+ /**
+ * Stop the binder heavy hitter auto sampler after given timeout.
+ */
+ private void handleBinderHeavyHitterAutoSamplerTimeOut() {
+ synchronized (ActivityManagerService.this) {
+ if (mConstants.BINDER_HEAVY_HITTER_WATCHER_ENABLED) {
+ // The default watcher is ON, don't bother to stop it.
+ return;
+ }
+ }
+ Binder.setHeavyHitterWatcherConfig(false, 0, 0.0f, null);
+ }
+
+ /**
+ * Handle the heavy hitters
+ */
+ private void handleBinderHeavyHitters(@NonNull final List<HeavyHitterContainer> hitters,
+ final int totalBinderCalls, final float threshold, final long timeSpan) {
+ final int size = hitters.size();
+ if (size == 0) {
+ return;
+ }
+ // Simply log it for now
+ final String pfmt = "%.1f%%";
+ final BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
+ final StringBuilder sb = new StringBuilder("Excessive incoming binder calls(>")
+ .append(String.format(pfmt, threshold * 100))
+ .append(',').append(totalBinderCalls)
+ .append(',').append(timeSpan)
+ .append("ms): ");
+ for (int i = 0; i < size; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ final HeavyHitterContainer container = hitters.get(i);
+ sb.append('[').append(container.mUid)
+ .append(',').append(container.mClass.getName())
+ .append(',').append(resolver.getMethodName(container.mClass, container.mCode))
+ .append(',').append(container.mCode)
+ .append(',').append(String.format(pfmt, container.mFrequency * 100))
+ .append(']');
+ }
+ Slog.w(TAG, sb.toString());
+ }
+
+ /**
* Attach an agent to the specified process (proces name or PID)
*/
public void attachAgent(String process, String path) {
@@ -20365,4 +20561,17 @@ public class ActivityManagerService extends IActivityManager.Stub
Binder.restoreCallingIdentity(token);
}
}
+
+ /**
+ * Resets the state of the {@link com.android.server.am.AppErrors} instance.
+ * This is intended for testing within the CTS only and is protected by
+ * android.permission.RESET_APP_ERRORS.
+ */
+ @Override
+ public void resetAppErrors() {
+ enforceCallingPermission(Manifest.permission.RESET_APP_ERRORS, "resetAppErrors");
+ synchronized (this) {
+ mAppErrors.resetStateLocked();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 8f8990fdaae7..a7119d18117a 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -44,10 +44,26 @@ class AnrHelper {
*/
private static final long EXPIRED_REPORT_TIME_MS = TimeUnit.MINUTES.toMillis(1);
+ /**
+ * If the last ANR occurred within this given time, consider it's anomaly.
+ */
+ private static final long CONSECUTIVE_ANR_TIME_MS = TimeUnit.MINUTES.toMillis(2);
+
@GuardedBy("mAnrRecords")
private final ArrayList<AnrRecord> mAnrRecords = new ArrayList<>();
private final AtomicBoolean mRunning = new AtomicBoolean(false);
+ private final ActivityManagerService mService;
+
+ /**
+ * The timestamp when the last ANR occurred.
+ */
+ private long mLastAnrTimeMs = 0L;
+
+ AnrHelper(final ActivityManagerService service) {
+ mService = service;
+ }
+
void appNotResponding(ProcessRecord anrProcess, String annotation) {
appNotResponding(anrProcess, null /* activityShortComponentName */, null /* aInfo */,
null /* parentShortComponentName */, null /* parentProcess */,
@@ -89,6 +105,7 @@ class AnrHelper {
public void run() {
AnrRecord r;
while ((r = next()) != null) {
+ scheduleBinderHeavyHitterAutoSamplerIfNecessary();
final long startTime = SystemClock.uptimeMillis();
// If there are many ANR at the same time, the latency may be larger. If the latency
// is too large, the stack trace might not be meaningful.
@@ -109,6 +126,15 @@ class AnrHelper {
}
}
}
+
+ }
+
+ private void scheduleBinderHeavyHitterAutoSamplerIfNecessary() {
+ final long now = SystemClock.uptimeMillis();
+ if (mLastAnrTimeMs + CONSECUTIVE_ANR_TIME_MS > now) {
+ mService.scheduleBinderHeavyHitterAutoSampler();
+ }
+ mLastAnrTimeMs = now;
}
private static class AnrRecord {
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 50d2cab0af81..2e92ac0fb3d6 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -33,7 +33,6 @@ import android.app.ApplicationExitInfo;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.VersionedPackage;
import android.net.Uri;
import android.os.Binder;
@@ -108,6 +107,16 @@ class AppErrors {
mPackageWatchdog = watchdog;
}
+ /** Resets the current state but leaves the constructor-provided fields unchanged. */
+ public void resetStateLocked() {
+ Slog.i(TAG, "Resetting AppErrors");
+ mAppsNotReportingCrashes.clear();
+ mProcessCrashTimes.clear();
+ mProcessCrashTimesPersistent.clear();
+ mProcessCrashShowDialogTimes.clear();
+ mBadProcesses.clear();
+ }
+
void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) {
if (mProcessCrashTimes.getMap().isEmpty() && mBadProcesses.getMap().isEmpty()) {
return;
@@ -263,16 +272,16 @@ class AppErrors {
return needSep;
}
- boolean isBadProcessLocked(ApplicationInfo info) {
- return mBadProcesses.get(info.processName, info.uid) != null;
+ boolean isBadProcessLocked(final String processName, final int uid) {
+ return mBadProcesses.get(processName, uid) != null;
}
- void clearBadProcessLocked(ApplicationInfo info) {
- mBadProcesses.remove(info.processName, info.uid);
+ void clearBadProcessLocked(final String processName, final int uid) {
+ mBadProcesses.remove(processName, uid);
}
- void resetProcessCrashTimeLocked(ApplicationInfo info) {
- mProcessCrashTimes.remove(info.processName, info.uid);
+ void resetProcessCrashTimeLocked(final String processName, final int uid) {
+ mProcessCrashTimes.remove(processName, uid);
}
void resetProcessCrashTimeLocked(boolean resetEntireUser, int appId, int userId) {
@@ -548,7 +557,7 @@ class AppErrors {
if (r != null && !r.isolated && res != AppErrorDialog.RESTART) {
// XXX Can't keep track of crash time for isolated processes,
// since they don't have a persistent identity.
- mProcessCrashTimes.put(r.info.processName, r.uid,
+ mProcessCrashTimes.put(r.processName, r.uid,
SystemClock.uptimeMillis());
}
}
@@ -695,8 +704,8 @@ class AppErrors {
boolean tryAgain = false;
if (!app.isolated) {
- crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
- crashTimePersistent = mProcessCrashTimesPersistent.get(app.info.processName, app.uid);
+ crashTime = mProcessCrashTimes.get(app.processName, app.uid);
+ crashTimePersistent = mProcessCrashTimesPersistent.get(app.processName, app.uid);
} else {
crashTime = crashTimePersistent = null;
}
@@ -723,10 +732,10 @@ class AppErrors {
if (crashTime != null && now < crashTime + ProcessList.MIN_CRASH_INTERVAL) {
// The process crashed again very quickly. If it was a bound foreground service, let's
// try to restart again in a while, otherwise the process loses!
- Slog.w(TAG, "Process " + app.info.processName
+ Slog.w(TAG, "Process " + app.processName
+ " has crashed too many times: killing!");
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
- app.userId, app.info.processName, app.uid);
+ app.userId, app.processName, app.uid);
mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
if (!app.isPersistent()) {
// We don't want to start this process again until the user
@@ -734,13 +743,13 @@ class AppErrors {
// need to keep it running. If a persistent process is actually
// repeatedly crashing, then badness for everyone.
EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
- app.info.processName);
+ app.processName);
if (!app.isolated) {
// XXX We don't have a way to mark isolated processes
// as bad, since they don't have a peristent identity.
- mBadProcesses.put(app.info.processName, app.uid,
+ mBadProcesses.put(app.processName, app.uid,
new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
- mProcessCrashTimes.remove(app.info.processName, app.uid);
+ mProcessCrashTimes.remove(app.processName, app.uid);
}
app.bad = true;
app.removed = true;
@@ -785,8 +794,8 @@ class AppErrors {
if (!app.isolated) {
// XXX Can't keep track of crash times for isolated processes,
// because they don't have a persistent identity.
- mProcessCrashTimes.put(app.info.processName, app.uid, now);
- mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now);
+ mProcessCrashTimes.put(app.processName, app.uid, now);
+ mProcessCrashTimesPersistent.put(app.processName, app.uid, now);
}
if (app.crashHandler != null) mService.mHandler.post(app.crashHandler);
@@ -829,7 +838,7 @@ class AppErrors {
}
Long crashShowErrorTime = null;
if (!proc.isolated) {
- crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.info.processName,
+ crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.processName,
proc.uid);
}
final boolean showFirstCrash = Settings.Global.getInt(
@@ -850,7 +859,7 @@ class AppErrors {
&& (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
proc.getDialogController().showCrashDialogs(data);
if (!proc.isolated) {
- mProcessCrashShowDialogTimes.put(proc.info.processName, proc.uid, now);
+ mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
}
} else {
// The device is asleep, so just pretend that the user
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 02fb34e73f7f..374c215fc6d0 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -522,7 +522,7 @@ public final class AppExitInfoTracker {
AppExitInfoContainer container = records.get(filterUid);
if (container != null) {
mTmpInfoList.clear();
- results.addAll(container.toListLocked(mTmpInfoList, filterPid));
+ list.addAll(container.toListLocked(mTmpInfoList, filterPid));
}
return AppExitInfoTracker.FOREACH_ACTION_NONE;
});
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index be17b1bc600c..494f06ebc324 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -62,7 +62,8 @@ public class BroadcastConstants {
public float DEFERRAL_DECAY_FACTOR = DEFAULT_DEFERRAL_DECAY_FACTOR;
// Minimum that the deferral time can decay to until the backlog fully clears
public long DEFERRAL_FLOOR = DEFAULT_DEFERRAL_FLOOR;
- // For how long after a whitelisted receiver's start its process can start a background activity
+ // For a receiver that has been allowed to start background activities, how long after it
+ // started its process can start a background activity.
public long ALLOW_BG_ACTIVITY_START_TIMEOUT = DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT;
// Settings override tracking for this instance
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 1cc41b22838e..1fa62c6c40ba 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -470,22 +470,23 @@ public final class BroadcastQueue {
// if this receiver was slow, impose deferral policy on the app. This will kick in
// when processNextBroadcastLocked() next finds this uid as a receiver identity.
if (!r.timeoutExempt) {
- if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
+ // r.curApp can be null if finish has raced with process death - benign
+ // edge case, and we just ignore it because we're already cleaning up
+ // as expected.
+ if (r.curApp != null
+ && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
// Core system packages are exempt from deferral policy
if (!UserHandle.isCore(r.curApp.uid)) {
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1)
+ " was slow: " + receiver + " br=" + r);
}
- if (r.curApp != null) {
- mDispatcher.startDeferring(r.curApp.uid);
- } else {
- Slog.d(TAG_BROADCAST, "finish receiver curApp is null? " + r);
- }
+ mDispatcher.startDeferring(r.curApp.uid);
} else {
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid
- + " receiver was slow but not deferring: " + receiver + " br=" + r);
+ + " receiver was slow but not deferring: "
+ + receiver + " br=" + r);
}
}
}
@@ -603,7 +604,7 @@ public final class BroadcastQueue {
}
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
- BroadcastFilter filter, boolean ordered, int index) {
+ BroadcastFilter filter, boolean ordered, int index, boolean skipOomAdj) {
boolean skip = false;
if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
filter.packageName, filter.owningUid)) {
@@ -787,8 +788,10 @@ public final class BroadcastQueue {
// are already core system stuff so don't matter for this.
r.curApp = filter.receiverList.app;
filter.receiverList.app.curReceivers.add(r);
- mService.updateOomAdjLocked(r.curApp, true,
- OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
+ if (!skipOomAdj) {
+ mService.updateOomAdjLocked(r.curApp, true,
+ OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
+ }
}
}
try {
@@ -904,6 +907,10 @@ public final class BroadcastQueue {
} else if (r.intent.getData() != null) {
b.append(r.intent.getData());
}
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG, "Broadcast temp whitelist uid=" + uid + " duration=" + duration
+ + " : " + b.toString());
+ }
mService.tempWhitelistUidLocked(uid, duration, b.toString());
}
@@ -980,7 +987,8 @@ public final class BroadcastQueue {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering non-ordered on [" + mQueueName + "] to registered "
+ target + ": " + r);
- deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
+ deliverToRegisteredReceiverLocked(r,
+ (BroadcastFilter) target, false, i, skipOomAdj);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
@@ -1032,7 +1040,7 @@ public final class BroadcastQueue {
// No more broadcasts are deliverable right now, so all done!
mDispatcher.scheduleDeferralCheckLocked(false);
mService.scheduleAppGcsLocked();
- if (looped) {
+ if (looped && !skipOomAdj) {
// If we had finished the last ordered broadcast, then
// make sure all processes have correct oom and sched
// adjustments.
@@ -1278,7 +1286,7 @@ public final class BroadcastQueue {
"Delivering ordered ["
+ mQueueName + "] to registered "
+ filter + ": " + r);
- deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
+ deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx, skipOomAdj);
if (r.receiver == null || !r.ordered) {
// The receiver has already finished, so schedule to
// process the next one.
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 8ef67f97e8d4..40743b8be1ea 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -89,8 +89,8 @@ final class BroadcastRecord extends Binder {
int manifestSkipCount; // number of manifest receivers skipped.
BroadcastQueue queue; // the outbound queue handling this broadcast
- // if set to true, app's process will be temporarily whitelisted to start activities
- // from background for the duration of the broadcast dispatch
+ // if set to true, app's process will be temporarily allowed to start activities from background
+ // for the duration of the broadcast dispatch
final boolean allowBackgroundActivityStarts;
static final int IDLE = 0;
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 58b0a157e2c2..f0343e1d807c 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -76,7 +76,11 @@ import android.app.ApplicationExitInfo;
import android.app.usage.UsageEvents;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ServiceInfo;
import android.os.Debug;
import android.os.Handler;
@@ -265,6 +269,43 @@ public final class OomAdjuster {
void initSettings() {
mCachedAppOptimizer.init();
+ if (mService.mConstants.KEEP_WARMING_SERVICES.size() > 0) {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+ mService.mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mService) {
+ handleUserSwitchedLocked();
+ }
+ }
+ }, filter, null, mService.mHandler);
+ }
+ }
+
+ /**
+ * Update the keep-warming service flags upon user switches
+ */
+ @VisibleForTesting
+ @GuardedBy("mService")
+ void handleUserSwitchedLocked() {
+ final ArraySet<ComponentName> warmServices = mService.mConstants.KEEP_WARMING_SERVICES;
+ final ArrayList<ProcessRecord> processes = mProcessList.mLruProcesses;
+ for (int i = processes.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = processes.get(i);
+ boolean includeWarmPkg = false;
+ for (int j = warmServices.size() - 1; j >= 0; j--) {
+ if (app.pkgList.containsKey(warmServices.valueAt(j).getPackageName())) {
+ includeWarmPkg = true;
+ break;
+ }
+ }
+ if (!includeWarmPkg) {
+ continue;
+ }
+ for (int j = app.numberOfRunningServices() - 1; j >= 0; j--) {
+ app.getRunningServiceAt(j).updateKeepWarmLocked();
+ }
+ }
}
/**
@@ -1151,17 +1192,8 @@ public final class OomAdjuster {
// is currently showing UI.
app.systemNoUi = true;
if (app == topApp) {
- // If specific system app has set ProcessRecord.mHasTopUi or is running a remote
- // animation (ProcessRecord.runningRemoteAnimation), this will prevent topApp
- // to use SCHED_GROUP_TOP_APP to ensure process with mHasTopUi will have exclusive
- // access to configured cores.
- if (mService.containsTopUiOrRunningRemoteAnimOrEmptyLocked(app)) {
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
- } else {
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
- }
app.systemNoUi = false;
-
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
app.adjType = "pers-top-activity";
} else if (app.hasTopUi()) {
// sched group/proc state adjustment is below
@@ -1202,20 +1234,10 @@ public final class OomAdjuster {
boolean foregroundActivities = false;
if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) {
-
- // If specific system app has set ProcessRecord.mHasTopUi or is running a remote
- // animation (ProcessRecord.runningRemoteAnimation), this will prevent topApp
- // to use SCHED_GROUP_TOP_APP to ensure process with mHasTopUi will have exclusive
- // access to configured cores.
- if (mService.containsTopUiOrRunningRemoteAnimOrEmptyLocked(app)) {
- adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
- app.adjType = "top-activity";
- } else {
- adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- app.adjType = "top-activity-behind-topui";
- }
+ // The last app on the list is the foreground app.
+ adj = ProcessList.FOREGROUND_APP_ADJ;
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ app.adjType = "top-activity";
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -1489,7 +1511,7 @@ public final class OomAdjuster {
"Raise procstate to started service: " + app);
}
}
- if (app.hasShownUi && !app.getCachedIsHomeProcess()) {
+ if (!s.mKeepWarming && app.hasShownUi && !app.getCachedIsHomeProcess()) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
// UI stuff. We'll tag it with a label just to help
@@ -1498,7 +1520,8 @@ public final class OomAdjuster {
app.adjType = "cch-started-ui-services";
}
} else {
- if (now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
+ if (s.mKeepWarming
+ || now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
// This service has seen some activity within
// recent memory, so we will keep its process ahead
// of the background processes.
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 5a0ea7586301..1038069f5d11 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -110,7 +110,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.RuntimeInit;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -155,9 +154,6 @@ public final class ProcessList {
static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
- // A system property to control if fuse is enabled.
- static final String ANDROID_FUSE_ENABLED = "persist.sys.fuse";
-
// The minimum time we allow between crashes, for us to consider this
// application to be bad and stop and its services and reject broadcasts.
static final int MIN_CRASH_INTERVAL = 60 * 1000;
@@ -719,13 +715,8 @@ public final class ProcessList {
// want some apps enabled while some apps disabled
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
- boolean fuseEnabled = SystemProperties.getBoolean(ANDROID_FUSE_ENABLED, false);
- boolean voldAppDataIsolationEnabled = SystemProperties.getBoolean(
+ mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
- if (!fuseEnabled && voldAppDataIsolationEnabled) {
- Slog.e(TAG, "Fuse is not enabled while vold app data isolation is enabled");
- }
- mVoldAppDataIsolationEnabled = fuseEnabled && voldAppDataIsolationEnabled;
mAppDataIsolationWhitelistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
@@ -2059,7 +2050,9 @@ public final class ProcessList {
final int pid = precedence.pid;
long now = System.currentTimeMillis();
final long end = now + PROC_KILL_TIMEOUT;
+ final int oldPolicy = StrictMode.getThreadPolicyMask();
try {
+ StrictMode.setThreadPolicyMask(0);
Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
// It's killed successfully, but we'd make sure the cleanup work is done.
synchronized (precedence) {
@@ -2078,9 +2071,11 @@ public final class ProcessList {
}
}
} catch (Exception e) {
- // It's still alive...
+ // It's still alive... maybe blocked at uninterruptible sleep ?
Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
- + app);
+ + app, e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldPolicy);
}
}
try {
@@ -2368,9 +2363,9 @@ public final class ProcessList {
if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
// If we are in the background, then check to see if this process
// is bad. If so, we will just silently fail.
- if (mService.mAppErrors.isBadProcessLocked(info)) {
+ if (mService.mAppErrors.isBadProcessLocked(processName, info.uid)) {
if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
- + "/" + info.processName);
+ + "/" + processName);
return null;
}
} else {
@@ -2379,13 +2374,13 @@ public final class ProcessList {
// least one crash dialog again, and make the process good again
// if it had been bad.
if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
- + "/" + info.processName);
- mService.mAppErrors.resetProcessCrashTimeLocked(info);
- if (mService.mAppErrors.isBadProcessLocked(info)) {
+ + "/" + processName);
+ mService.mAppErrors.resetProcessCrashTimeLocked(processName, info.uid);
+ if (mService.mAppErrors.isBadProcessLocked(processName, info.uid)) {
EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
UserHandle.getUserId(info.uid), info.uid,
info.processName);
- mService.mAppErrors.clearBadProcessLocked(info);
+ mService.mAppErrors.clearBadProcessLocked(processName, info.uid);
if (app != null) {
app.bad = false;
}
@@ -2425,7 +2420,15 @@ public final class ProcessList {
ProcessList.killProcessGroup(app.uid, app.pid);
checkSlow(startTime, "startProcess: done killing old proc");
- Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ if (!app.killed || mService.mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL
+ || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
+ || app.lastCachedPss < getCachedRestoreThresholdKb()) {
+ // Throw a wtf if it's not killed, or killed but not because the system was in
+ // memory pressure + the app was in "cch-empty" and used large amount of memory
+ Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ } else {
+ Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ }
// We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
// routine of it yet, but we'd set it as the precedence of the new process.
precedence = app;
@@ -2828,7 +2831,15 @@ public final class ProcessList {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
} else if (old != null) {
- Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ if (old.killed) {
+ // The old process has been killed, we probably haven't had
+ // a chance to clean up the old record, just log a warning
+ Slog.w(TAG, "Existing proc " + old + " was killed "
+ + (SystemClock.uptimeMillis() - old.mKillTime)
+ + "ms ago when adding " + proc);
+ } else {
+ Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ }
}
UidRecord uidRec = mActiveUids.get(proc.uid);
if (uidRec == null) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 4c75ab21d6f2..6e1bd8faeaf9 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -274,7 +274,7 @@ class ProcessRecord implements WindowProcessListener {
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
- // A set of tokens that currently contribute to this process being temporarily whitelisted
+ // A set of tokens that currently contribute to this process being temporarily allowed
// to start activities even if it's not in the foreground
final ArraySet<Binder> mAllowBackgroundActivityStartsTokens = new ArraySet<>();
// a set of UIDs of all bound clients
@@ -352,6 +352,8 @@ class ProcessRecord implements WindowProcessListener {
boolean mReachable; // Whether or not this process is reachable from given process
+ long mKillTime; // The timestamp in uptime when this process was killed.
+
void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
long startTime) {
this.startUid = startUid;
@@ -626,7 +628,7 @@ class ProcessRecord implements WindowProcessListener {
}
}
if (mAllowBackgroundActivityStartsTokens.size() > 0) {
- pw.print(prefix); pw.println("Background activity start whitelist tokens:");
+ pw.print(prefix); pw.println("Background activity start tokens:");
for (int i = 0; i < mAllowBackgroundActivityStartsTokens.size(); i++) {
pw.print(prefix); pw.print(" - ");
pw.println(mAllowBackgroundActivityStartsTokens.valueAt(i));
@@ -925,6 +927,7 @@ class ProcessRecord implements WindowProcessListener {
if (!mPersistent) {
killed = true;
killedByAm = true;
+ mKillTime = SystemClock.uptimeMillis();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1268,7 +1271,6 @@ class ProcessRecord implements WindowProcessListener {
void setHasTopUi(boolean hasTopUi) {
mHasTopUi = hasTopUi;
mWindowProcessController.setHasTopUi(hasTopUi);
- updateTopUiOrRunningRemoteAnim();
}
boolean hasTopUi() {
@@ -1519,19 +1521,10 @@ class ProcessRecord implements WindowProcessListener {
Slog.i(TAG, "Setting runningRemoteAnimation=" + runningRemoteAnimation
+ " for pid=" + pid);
}
- updateTopUiOrRunningRemoteAnim();
mService.updateOomAdjLocked(this, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
- void updateTopUiOrRunningRemoteAnim() {
- if (runningRemoteAnimation || hasTopUi()) {
- mService.addTopUiOrRunningRemoteAnim(this);
- } else {
- mService.removeTopUiOrRunningRemoteAnim(this);
- }
- }
-
public long getInputDispatchingTimeout() {
return mWindowProcessController.getInputDispatchingTimeout();
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 749a9909144e..db05d65b92fe 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -43,6 +43,7 @@ import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.LocalServices;
@@ -130,13 +131,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
int pendingConnectionImportance; // To be filled in to ProcessRecord once it connects
// any current binding to this service has BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag?
- private boolean mHasBindingWhitelistingBgActivityStarts;
- // is this service currently whitelisted to start activities from background by providing
+ private boolean mIsAllowedBgActivityStartsByBinding;
+ // is this service currently allowed to start activities from background by providing
// allowBackgroundActivityStarts=true to startServiceLocked()?
- private boolean mHasStartedWhitelistingBgActivityStarts;
- // used to clean up the state of hasStartedWhitelistingBgActivityStarts after a timeout
- private Runnable mStartedWhitelistingBgActivityStartsCleanUp;
- private ProcessRecord mAppForStartedWhitelistingBgActivityStarts;
+ private boolean mIsAllowedBgActivityStartsByStart;
+ // used to clean up the state of mIsAllowedBgActivityStartsByStart after a timeout
+ private Runnable mCleanUpAllowBgActivityStartsByStartCallback;
+ private ProcessRecord mAppForAllowingBgActivityStartsByStart;
// allow while-in-use permissions in foreground service or not.
// while-in-use permissions in FGS started from background might be restricted.
@@ -149,6 +150,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
private int lastStartId; // identifier of most recent start request.
+ boolean mKeepWarming; // Whether or not it'll keep critical code path of the host warm
+
static class StartItem {
final ServiceRecord sr;
final boolean taskRemoved;
@@ -393,13 +396,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
if (whitelistManager) {
pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
}
- if (mHasBindingWhitelistingBgActivityStarts) {
- pw.print(prefix); pw.print("hasBindingWhitelistingBgActivityStarts=");
- pw.println(mHasBindingWhitelistingBgActivityStarts);
+ if (mIsAllowedBgActivityStartsByBinding) {
+ pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByBinding=");
+ pw.println(mIsAllowedBgActivityStartsByBinding);
}
- if (mHasStartedWhitelistingBgActivityStarts) {
- pw.print(prefix); pw.print("hasStartedWhitelistingBgActivityStarts=");
- pw.println(mHasStartedWhitelistingBgActivityStarts);
+ if (mIsAllowedBgActivityStartsByStart) {
+ pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
+ pw.println(mIsAllowedBgActivityStartsByStart);
}
pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
pw.println(mAllowWhileInUsePermissionInFgs);
@@ -517,6 +520,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
lastActivity = SystemClock.uptimeMillis();
userId = UserHandle.getUserId(appInfo.uid);
createdFromFg = callerIsFg;
+ updateKeepWarmLocked();
}
public ServiceState getTracker() {
@@ -556,31 +560,31 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
public void setProcess(ProcessRecord _proc) {
if (_proc != null) {
- // We're starting a new process for this service, but a previous one is whitelisted.
- // Remove that whitelisting now (unless the new process is the same as the previous one,
- // which is a common case).
- if (mAppForStartedWhitelistingBgActivityStarts != null) {
- if (mAppForStartedWhitelistingBgActivityStarts != _proc) {
- mAppForStartedWhitelistingBgActivityStarts
+ // We're starting a new process for this service, but a previous one is allowed to start
+ // background activities. Remove that ability now (unless the new process is the same as
+ // the previous one, which is a common case).
+ if (mAppForAllowingBgActivityStartsByStart != null) {
+ if (mAppForAllowingBgActivityStartsByStart != _proc) {
+ mAppForAllowingBgActivityStartsByStart
.removeAllowBackgroundActivityStartsToken(this);
- ams.mHandler.removeCallbacks(mStartedWhitelistingBgActivityStartsCleanUp);
+ ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
}
}
// Make sure the cleanup callback knows about the new process.
- mAppForStartedWhitelistingBgActivityStarts = mHasStartedWhitelistingBgActivityStarts
+ mAppForAllowingBgActivityStartsByStart = mIsAllowedBgActivityStartsByStart
? _proc : null;
- if (mHasStartedWhitelistingBgActivityStarts
- || mHasBindingWhitelistingBgActivityStarts) {
+ if (mIsAllowedBgActivityStartsByStart
+ || mIsAllowedBgActivityStartsByBinding) {
_proc.addAllowBackgroundActivityStartsToken(this);
} else {
_proc.removeAllowBackgroundActivityStartsToken(this);
}
}
if (app != null && app != _proc) {
- // If the old app is whitelisted because of a service start, leave it whitelisted until
- // the cleanup callback runs. Otherwise we can remove it from the whitelist immediately
- // (it can't be bound now).
- if (!mHasStartedWhitelistingBgActivityStarts) {
+ // If the old app is allowed to start bg activities because of a service start, leave it
+ // that way until the cleanup callback runs. Otherwise we can remove its bg activity
+ // start ability immediately (it can't be bound now).
+ if (!mIsAllowedBgActivityStartsByStart) {
app.removeAllowBackgroundActivityStartsToken(this);
}
app.updateBoundClientUids();
@@ -644,89 +648,88 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
return startRequested && (stopIfKilled || isStartCanceled) && pendingStarts.isEmpty();
}
- void updateHasBindingWhitelistingBgActivityStarts() {
- boolean hasWhitelistingBinding = false;
+ void updateIsAllowedBgActivityStartsByBinding() {
+ boolean isAllowedByBinding = false;
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
for (int i = 0; i < cr.size(); i++) {
if ((cr.get(i).flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
- hasWhitelistingBinding = true;
+ isAllowedByBinding = true;
break;
}
}
- if (hasWhitelistingBinding) {
+ if (isAllowedByBinding) {
break;
}
}
- setHasBindingWhitelistingBgActivityStarts(hasWhitelistingBinding);
+ setAllowedBgActivityStartsByBinding(isAllowedByBinding);
}
- void setHasBindingWhitelistingBgActivityStarts(boolean newValue) {
- if (mHasBindingWhitelistingBgActivityStarts != newValue) {
- mHasBindingWhitelistingBgActivityStarts = newValue;
- updateParentProcessBgActivityStartsWhitelistingToken();
+ void setAllowedBgActivityStartsByBinding(boolean newValue) {
+ if (mIsAllowedBgActivityStartsByBinding != newValue) {
+ mIsAllowedBgActivityStartsByBinding = newValue;
+ updateParentProcessBgActivityStartsToken();
}
}
/**
- * Called when the service is started with allowBackgroundActivityStarts set. We whitelist
- * it for background activity starts, setting up a callback to remove the whitelisting after a
- * timeout. Note that the whitelisting persists for the process even if the service is
- * subsequently stopped.
+ * Called when the service is started with allowBackgroundActivityStarts set. We allow
+ * it for background activity starts, setting up a callback to remove this ability after a
+ * timeout. Note that the ability for starting background activities persists for the process
+ * even if the service is subsequently stopped.
*/
- void whitelistBgActivityStartsOnServiceStart() {
- setHasStartedWhitelistingBgActivityStarts(true);
+ void allowBgActivityStartsOnServiceStart() {
+ setAllowedBgActivityStartsByStart(true);
if (app != null) {
- mAppForStartedWhitelistingBgActivityStarts = app;
+ mAppForAllowingBgActivityStartsByStart = app;
}
// This callback is stateless, so we create it once when we first need it.
- if (mStartedWhitelistingBgActivityStartsCleanUp == null) {
- mStartedWhitelistingBgActivityStartsCleanUp = () -> {
+ if (mCleanUpAllowBgActivityStartsByStartCallback == null) {
+ mCleanUpAllowBgActivityStartsByStartCallback = () -> {
synchronized (ams) {
- if (app == mAppForStartedWhitelistingBgActivityStarts) {
- // The process we whitelisted is still running the service. We remove
- // the started whitelisting, but it may still be whitelisted via bound
- // connections.
- setHasStartedWhitelistingBgActivityStarts(false);
- } else if (mAppForStartedWhitelistingBgActivityStarts != null) {
- // The process we whitelisted is not running the service. It therefore
- // can't be bound so we can unconditionally remove the whitelist.
- mAppForStartedWhitelistingBgActivityStarts
+ if (app == mAppForAllowingBgActivityStartsByStart) {
+ // The process we allowed is still running the service. We remove
+ // the ability by start, but it may still be allowed via bound connections.
+ setAllowedBgActivityStartsByStart(false);
+ } else if (mAppForAllowingBgActivityStartsByStart != null) {
+ // The process we allowed is not running the service. It therefore can't be
+ // bound so we can unconditionally remove the ability.
+ mAppForAllowingBgActivityStartsByStart
.removeAllowBackgroundActivityStartsToken(ServiceRecord.this);
}
- mAppForStartedWhitelistingBgActivityStarts = null;
+ mAppForAllowingBgActivityStartsByStart = null;
}
};
}
// if there's a request pending from the past, drop it before scheduling a new one
- ams.mHandler.removeCallbacks(mStartedWhitelistingBgActivityStartsCleanUp);
- ams.mHandler.postDelayed(mStartedWhitelistingBgActivityStartsCleanUp,
+ ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
+ ams.mHandler.postDelayed(mCleanUpAllowBgActivityStartsByStartCallback,
ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
}
- private void setHasStartedWhitelistingBgActivityStarts(boolean newValue) {
- if (mHasStartedWhitelistingBgActivityStarts != newValue) {
- mHasStartedWhitelistingBgActivityStarts = newValue;
- updateParentProcessBgActivityStartsWhitelistingToken();
+ private void setAllowedBgActivityStartsByStart(boolean newValue) {
+ if (mIsAllowedBgActivityStartsByStart != newValue) {
+ mIsAllowedBgActivityStartsByStart = newValue;
+ updateParentProcessBgActivityStartsToken();
}
}
/**
- * Whether the process this service runs in should be temporarily whitelisted to start
+ * Whether the process this service runs in should be temporarily allowed to start
* activities from background depends on the current state of both
- * {@code hasStartedWhitelistingBgActivityStarts} and
- * {@code hasBindingWhitelistingBgActivityStarts}. If either is true, this ServiceRecord
+ * {@code mIsAllowedBgActivityStartsByStart} and
+ * {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord
* should be contributing as a token in parent ProcessRecord.
*
* @see com.android.server.am.ProcessRecord#mAllowBackgroundActivityStartsTokens
*/
- private void updateParentProcessBgActivityStartsWhitelistingToken() {
+ private void updateParentProcessBgActivityStartsToken() {
if (app == null) {
return;
}
- if (mHasStartedWhitelistingBgActivityStarts || mHasBindingWhitelistingBgActivityStarts) {
+ if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) {
// if the token is already there it's safe to "re-add it" - we're dealing with
// a set of Binder objects
app.addAllowBackgroundActivityStartsToken(this);
@@ -735,6 +738,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
}
}
+ @GuardedBy("ams")
+ void updateKeepWarmLocked() {
+ mKeepWarming = ams.mConstants.KEEP_WARMING_SERVICES.contains(name)
+ && (ams.mUserController.getCurrentUserId() == userId
+ || ams.isSingleton(processName, appInfo, instanceName.getClassName(),
+ serviceInfo.flags));
+ }
+
public AppBindRecord retrieveAppBindingLocked(Intent intent,
ProcessRecord app) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6efa97c3f07c..e6480fc6cde8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -44,6 +44,7 @@ import static android.app.AppOpsManager.RestrictionBypass;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
+import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM_OPS;
import static android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE;
import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
import static android.app.AppOpsManager.UID_STATE_CACHED;
@@ -75,6 +76,7 @@ import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
@@ -372,6 +374,10 @@ public class AppOpsService extends IAppOpsService.Stub {
/** Last runtime permission access message collected and ready for reporting */
@GuardedBy("this")
private RuntimeAppOpAccessMessage mCollectedRuntimePermissionMessage;
+
+ /** Package Manager internal. Access via {@link #getPackageManagerInternal()} */
+ private @Nullable PackageManagerInternal mPackageManagerInternal;
+
/**
* An unsynchronized pool of {@link OpEventProxyInfo} objects.
*/
@@ -1522,8 +1528,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
} else if (action.equals(Intent.ACTION_PACKAGE_REPLACED)) {
- AndroidPackage pkg = LocalServices.getService(
- PackageManagerInternal.class).getPackage(pkgName);
+ AndroidPackage pkg = getPackageManagerInternal().getPackage(pkgName);
if (pkg == null) {
return;
}
@@ -1602,7 +1607,8 @@ public class AppOpsService extends IAppOpsService.Stub {
packageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
packageUpdateFilter.addDataScheme("package");
- mContext.registerReceiver(mOnPackageUpdatedReceiver, packageUpdateFilter);
+ mContext.registerReceiverAsUser(mOnPackageUpdatedReceiver, UserHandle.ALL,
+ packageUpdateFilter, null, null);
synchronized (this) {
for (int uidNum = mUidStates.size() - 1; uidNum >= 0; uidNum--) {
@@ -1645,7 +1651,7 @@ public class AppOpsService extends IAppOpsService.Stub {
final IntentFilter packageSuspendFilter = new IntentFilter();
packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
- mContext.registerReceiver(new BroadcastReceiver() {
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
@@ -1669,7 +1675,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
}
- }, packageSuspendFilter);
+ }, UserHandle.ALL, packageSuspendFilter, null, null);
final IntentFilter packageAddedFilter = new IntentFilter();
packageAddedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -1680,8 +1686,7 @@ public class AppOpsService extends IAppOpsService.Stub {
final Uri data = intent.getData();
final String packageName = data.getSchemeSpecificPart();
- PackageInfo pi = LocalServices.getService(
- PackageManagerInternal.class).getPackageInfo(packageName,
+ PackageInfo pi = getPackageManagerInternal().getPackageInfo(packageName,
PackageManager.GET_PERMISSIONS, Process.myUid(), mContext.getUserId());
if (isSamplingTarget(pi)) {
synchronized (this) {
@@ -1699,9 +1704,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}, RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS);
- PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
- packageManagerInternal.setExternalSourcesPolicy(
+ getPackageManagerInternal().setExternalSourcesPolicy(
new PackageManagerInternal.ExternalSourcesPolicy() {
@Override
public int getPackageTrustedToInstallApps(String packageName, int uid) {
@@ -2056,6 +2059,8 @@ public class AppOpsService extends IAppOpsService.Stub {
public void getHistoricalOps(int uid, String packageName, String attributionTag,
List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
int flags, RemoteCallback callback) {
+ PackageManager pm = mContext.getPackageManager();
+
ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
@@ -2063,8 +2068,16 @@ public class AppOpsService extends IAppOpsService.Stub {
ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
+ boolean isCallerPermissionController;
+ try {
+ isCallerPermissionController = pm.getPackageUid(
+ mContext.getPackageManager().getPermissionControllerPackageName(), 0)
+ == Binder.getCallingUid();
+ } catch (PackageManager.NameNotFoundException doesNotHappen) {
+ return;
+ }
- if (!isCallerSystem && !isCallerInstrumented) {
+ if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
mHandler.post(() -> callback.sendResult(new Bundle()));
return;
}
@@ -2353,10 +2366,8 @@ public class AppOpsService extends IAppOpsService.Stub {
continue;
}
- PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
- boolean supportsRuntimePermissions = packageManagerInternal.getUidTargetSdkVersion(uid)
- >= Build.VERSION_CODES.M;
+ boolean supportsRuntimePermissions = getPackageManagerInternal()
+ .getUidTargetSdkVersion(uid) >= Build.VERSION_CODES.M;
UserHandle user = UserHandle.getUserHandleForUid(uid);
boolean isRevokedCompat;
@@ -2433,6 +2444,8 @@ public class AppOpsService extends IAppOpsService.Stub {
@Nullable IAppOpsCallback permissionPolicyCallback) {
enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingOp(code);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+
ArraySet<ModeCallback> repCbs = null;
code = AppOpsManager.opToSwitch(code);
@@ -2845,6 +2858,8 @@ public class AppOpsService extends IAppOpsService.Stub {
private int checkOperationImpl(int code, int uid, String packageName,
boolean raw) {
verifyIncomingOp(code);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
@@ -2964,6 +2979,8 @@ public class AppOpsService extends IAppOpsService.Stub {
boolean shouldCollectMessage) {
verifyIncomingUid(proxyUid);
verifyIncomingOp(code);
+ verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
+ verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolveProxyPackageName == null) {
@@ -3015,6 +3032,8 @@ public class AppOpsService extends IAppOpsService.Stub {
@Nullable String message, boolean shouldCollectMessage) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
@@ -3392,6 +3411,8 @@ public class AppOpsService extends IAppOpsService.Stub {
String message, boolean shouldCollectMessage) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
@@ -3472,6 +3493,8 @@ public class AppOpsService extends IAppOpsService.Stub {
String attributionTag) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return;
@@ -3691,6 +3714,14 @@ public class AppOpsService extends IAppOpsService.Stub {
throw new IllegalArgumentException("Bad operation #" + op);
}
+ private void verifyIncomingPackage(@Nullable String packageName, @UserIdInt int userId) {
+ if (packageName != null && getPackageManagerInternal().filterAppAccess(packageName,
+ Binder.getCallingUid(), userId)) {
+ throw new IllegalArgumentException(
+ packageName + " not found from " + Binder.getCallingUid());
+ }
+ }
+
private @Nullable UidState getUidStateLocked(int uid, boolean edit) {
UidState uidState = mUidStates.get(uid);
if (uidState == null) {
@@ -3797,6 +3828,17 @@ public class AppOpsService extends IAppOpsService.Stub {
}
/**
+ * @return {@link PackageManagerInternal}
+ */
+ private @NonNull PackageManagerInternal getPackageManagerInternal() {
+ if (mPackageManagerInternal == null) {
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ return mPackageManagerInternal;
+ }
+
+ /**
* Create a restriction description matching the properties of the package.
*
* @param context A context to use
@@ -5760,6 +5802,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
verifyIncomingOp(code);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+
final String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return false;
@@ -5903,11 +5947,13 @@ public class AppOpsService extends IAppOpsService.Stub {
int newLeftDistance = AppOpsManager.leftCircularDistance(opCode,
mSampledAppOpCode, _NUM_OP);
- if (mAcceptableLeftDistance < newLeftDistance) {
+ if (mAcceptableLeftDistance < newLeftDistance
+ && mSamplingStrategy != SAMPLING_STRATEGY_UNIFORM_OPS) {
return;
}
- if (mAcceptableLeftDistance > newLeftDistance) {
+ if (mAcceptableLeftDistance > newLeftDistance
+ && mSamplingStrategy != SAMPLING_STRATEGY_UNIFORM_OPS) {
mAcceptableLeftDistance = newLeftDistance;
mMessagesCollectedCount = 0.0f;
}
@@ -5945,13 +5991,13 @@ public class AppOpsService extends IAppOpsService.Stub {
if (mSampledPackage == null) {
if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
mSamplingStrategy = SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
- resampleAppOpForPackageLocked(packageName);
+ resampleAppOpForPackageLocked(packageName, true);
}
} else if (mRarelyUsedPackages.contains(packageName)) {
mRarelyUsedPackages.remove(packageName);
if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
mSamplingStrategy = SAMPLING_STRATEGY_RARELY_USED;
- resampleAppOpForPackageLocked(packageName);
+ resampleAppOpForPackageLocked(packageName, true);
}
}
}
@@ -5968,17 +6014,23 @@ public class AppOpsService extends IAppOpsService.Stub {
/** Resamples package and appop to watch from the list provided. */
private void resamplePackageAndAppOpLocked(@NonNull List<String> packageNames) {
if (!packageNames.isEmpty()) {
- mSamplingStrategy = SAMPLING_STRATEGY_UNIFORM;
- resampleAppOpForPackageLocked(packageNames.get(
- ThreadLocalRandom.current().nextInt(packageNames.size())));
+ if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
+ mSamplingStrategy = SAMPLING_STRATEGY_UNIFORM;
+ resampleAppOpForPackageLocked(packageNames.get(
+ ThreadLocalRandom.current().nextInt(packageNames.size())), true);
+ } else {
+ mSamplingStrategy = SAMPLING_STRATEGY_UNIFORM_OPS;
+ resampleAppOpForPackageLocked(packageNames.get(
+ ThreadLocalRandom.current().nextInt(packageNames.size())), false);
+ }
}
}
/** Resamples appop for the chosen package and initializes sampling state */
- private void resampleAppOpForPackageLocked(@NonNull String packageName) {
+ private void resampleAppOpForPackageLocked(@NonNull String packageName, boolean pickOp) {
mMessagesCollectedCount = 0.0f;
- mSampledAppOpCode = ThreadLocalRandom.current().nextInt(_NUM_OP);
- mAcceptableLeftDistance = _NUM_OP;
+ mSampledAppOpCode = pickOp ? ThreadLocalRandom.current().nextInt(_NUM_OP) : OP_NONE;
+ mAcceptableLeftDistance = _NUM_OP - 1;
mSampledPackage = packageName;
}
@@ -6118,6 +6170,7 @@ public class AppOpsService extends IAppOpsService.Stub {
case "root":
return Process.ROOT_UID;
case "shell":
+ case "dumpstate":
return Process.SHELL_UID;
case "media":
return Process.MEDIA_UID;
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index fd8853b8fdfd..84de25c06ebf 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -46,6 +46,14 @@
"include-filter": "android.app.cts.ActivityManagerApi29Test"
}
]
- }
+ },
+ {
+ "name": "CtsStatsdHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.cts.statsd.atom.UidAtomTests#testAppOps"
+ }
+ ]
+ }
]
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3d5d5178972c..2d77d6f8add8 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -283,6 +283,7 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_HDMI_VOLUME_CHECK = 28;
private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29;
private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30;
+ private static final int MSG_CHECK_MODE_FOR_UID = 31;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -422,6 +423,9 @@ public class AudioService extends IAudioService.Stub
private final boolean mUseFixedVolume;
+ // If absolute volume is supported in AVRCP device
+ private volatile boolean mAvrcpAbsVolSupported = false;
+
/**
* Default stream type used for volume control in the absence of playback
* e.g. user on homescreen, no app playing anything, presses hardware volume buttons, this
@@ -1317,6 +1321,11 @@ public class AudioService extends IAudioService.Stub
device, caller, true /*hasModifyAudioSettings*/);
}
mStreamStates[streamType].checkFixedVolumeDevices();
+
+ // Unmute streams if device is full volume
+ if (mFullVolumeDevices.contains(device)) {
+ mStreamStates[streamType].mute(false);
+ }
}
}
@@ -2948,11 +2957,10 @@ public class AudioService extends IAudioService.Stub
// Don't show volume UI when:
// - Hdmi-CEC system audio mode is on and we are a TV panel
- // - CEC volume control enabled on a set-top box
private int updateFlagsForTvPlatform(int flags) {
synchronized (mHdmiClientLock) {
- if ((mHdmiTvClient != null && mHdmiSystemAudioSupported && mHdmiCecVolumeControlEnabled)
- || (mHdmiPlaybackClient != null && mHdmiCecVolumeControlEnabled)) {
+ if (mHdmiTvClient != null && mHdmiSystemAudioSupported
+ && mHdmiCecVolumeControlEnabled) {
flags &= ~AudioManager.FLAG_SHOW_UI;
}
}
@@ -3676,12 +3684,14 @@ public class AudioService extends IAudioService.Stub
private final IBinder mCb; // To be notified of client's death
private final int mPid;
private final int mUid;
+ private String mPackage;
private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
- SetModeDeathHandler(IBinder cb, int pid, int uid) {
+ SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) {
mCb = cb;
mPid = pid;
mUid = uid;
+ mPackage = caller;
}
public void binderDied() {
@@ -3719,6 +3729,10 @@ public class AudioService extends IAudioService.Stub
public int getUid() {
return mUid;
}
+
+ public String getPackage() {
+ return mPackage;
+ }
}
/** @see AudioManager#setMode(int) */
@@ -3800,6 +3814,9 @@ public class AudioService extends IAudioService.Stub
hdlr = h;
// Remove from client list so that it is re-inserted at top of list
iter.remove();
+ if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) {
+ mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr);
+ }
try {
hdlr.getBinder().unlinkToDeath(hdlr, 0);
if (cb != hdlr.getBinder()){
@@ -3830,7 +3847,7 @@ public class AudioService extends IAudioService.Stub
}
} else {
if (hdlr == null) {
- hdlr = new SetModeDeathHandler(cb, pid, uid);
+ hdlr = new SetModeDeathHandler(cb, pid, uid, caller);
}
// Register for client death notification
try {
@@ -3877,6 +3894,7 @@ public class AudioService extends IAudioService.Stub
// Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
mModeLogger.log(
new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode));
+
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
int device = getDeviceForStream(streamType);
int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
@@ -3887,6 +3905,16 @@ public class AudioService extends IAudioService.Stub
// change of mode may require volume to be re-applied on some devices
updateAbsVolumeMultiModeDevices(oldMode, actualMode);
+
+ if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) {
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MODE_FOR_UID,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ hdlr,
+ CHECK_MODE_FOR_UID_PERIOD_MS);
+ }
}
return newModeOwnerPid;
}
@@ -4994,7 +5022,7 @@ public class AudioService extends IAudioService.Stub
return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
}
if (audioSystemDeviceOut == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP
- && mDeviceBroker.isAvrcpAbsoluteVolumeSupported()) {
+ && mAvrcpAbsVolSupported) {
return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
}
return AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE;
@@ -5711,12 +5739,12 @@ public class AudioService extends IAudioService.Stub
}
// must be called while synchronized VolumeStreamState.class
- /*package*/ void applyDeviceVolume_syncVSS(int device, boolean isAvrcpAbsVolSupported) {
+ /*package*/ void applyDeviceVolume_syncVSS(int device) {
int index;
if (isFullyMuted()) {
index = 0;
} else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
- && isAvrcpAbsVolSupported) {
+ && mAvrcpAbsVolSupported) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
index = (mIndexMax + 5)/10;
@@ -5729,7 +5757,6 @@ public class AudioService extends IAudioService.Stub
}
public void applyAllVolumes() {
- final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();
synchronized (VolumeStreamState.class) {
// apply device specific volumes first
int index;
@@ -5739,7 +5766,7 @@ public class AudioService extends IAudioService.Stub
if (isFullyMuted()) {
index = 0;
} else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
- && isAvrcpAbsVolSupported) {
+ && mAvrcpAbsVolSupported) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
index = (mIndexMax + 5)/10;
@@ -5979,7 +6006,6 @@ public class AudioService extends IAudioService.Stub
}
public void checkFixedVolumeDevices() {
- final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();
synchronized (VolumeStreamState.class) {
// ignore settings for fixed volume devices: volume should always be at max or 0
if (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) {
@@ -5990,7 +6016,7 @@ public class AudioService extends IAudioService.Stub
|| (isFixedVolumeDevice(device) && index != 0)) {
mIndexMap.put(device, mIndexMax);
}
- applyDeviceVolume_syncVSS(device, isAvrcpAbsVolSupported);
+ applyDeviceVolume_syncVSS(device);
}
}
}
@@ -6154,11 +6180,9 @@ public class AudioService extends IAudioService.Stub
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
- final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();
-
synchronized (VolumeStreamState.class) {
// Apply volume
- streamState.applyDeviceVolume_syncVSS(device, isAvrcpAbsVolSupported);
+ streamState.applyDeviceVolume_syncVSS(device);
// Apply change to all streams using this one as alias
int numStreamTypes = AudioSystem.getNumStreamTypes();
@@ -6168,13 +6192,11 @@ public class AudioService extends IAudioService.Stub
// Make sure volume is also maxed out on A2DP device for aliased stream
// that may have a different device selected
int streamDevice = getDeviceForStream(streamType);
- if ((device != streamDevice) && isAvrcpAbsVolSupported
+ if ((device != streamDevice) && mAvrcpAbsVolSupported
&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) {
- mStreamStates[streamType].applyDeviceVolume_syncVSS(device,
- isAvrcpAbsVolSupported);
+ mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
}
- mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice,
- isAvrcpAbsVolSupported);
+ mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
}
}
}
@@ -6401,6 +6423,35 @@ public class AudioService extends IAudioService.Stub
case MSG_BROADCAST_MICROPHONE_MUTE:
mSystemServer.sendMicrophoneMuteChangedIntent();
break;
+
+ case MSG_CHECK_MODE_FOR_UID:
+ synchronized (mDeviceBroker.mSetModeLock) {
+ if (msg.obj == null) {
+ break;
+ }
+ // If the app corresponding to this mode death handler object is not
+ // capturing or playing audio anymore after 3 seconds, remove it
+ // from the stack. Otherwise, check again in 3 seconds.
+ SetModeDeathHandler h = (SetModeDeathHandler) msg.obj;
+ if (mSetModeDeathHandlers.indexOf(h) < 0) {
+ break;
+ }
+ if (mRecordMonitor.isRecordingActiveForUid(h.getUid())
+ || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) {
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MODE_FOR_UID,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ h,
+ CHECK_MODE_FOR_UID_PERIOD_MS);
+ break;
+ }
+ // For now just log the fact that an app is hogging the audio mode.
+ // TODO(b/160260850): remove abusive app from audio mode stack.
+ mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid()));
+ }
+ break;
}
}
}
@@ -6482,6 +6533,7 @@ public class AudioService extends IAudioService.Stub
// address is not used for now, but may be used when multiple a2dp devices are supported
sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
+ address + " support=" + support));
+ mAvrcpAbsVolSupported = support;
mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support);
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
@@ -7043,6 +7095,8 @@ public class AudioService extends IAudioService.Stub
private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed
+ // check playback or record activity every 3 seconds for UIDs owning mode IN_COMMUNICATION
+ private static final int CHECK_MODE_FOR_UID_PERIOD_MS = 3000;
private int safeMediaVolumeIndex(int device) {
if (!mSafeMediaVolumeDevices.contains(device)) {
@@ -7439,8 +7493,7 @@ public class AudioService extends IAudioService.Stub
pw.print(" mCameraSoundForced="); pw.println(mCameraSoundForced);
pw.print(" mHasVibrator="); pw.println(mHasVibrator);
pw.print(" mVolumePolicy="); pw.println(mVolumePolicy);
- pw.print(" mAvrcpAbsVolSupported=");
- pw.println(mDeviceBroker.isAvrcpAbsoluteVolumeSupported());
+ pw.print(" mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
pw.print(" mIsSingleVolume="); pw.println(mIsSingleVolume);
pw.print(" mUseFixedVolume="); pw.println(mUseFixedVolume);
pw.print(" mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
@@ -8844,7 +8897,7 @@ public class AudioService extends IAudioService.Stub
//======================
- // Audioserver state displatch
+ // Audioserver state dispatch
//======================
private class AsdProxy implements IBinder.DeathRecipient {
private final IAudioServerStateDispatcher mAsd;
@@ -9005,10 +9058,15 @@ public class AudioService extends IAudioService.Stub
if (DEBUG_VOL) {
Log.d(TAG, "Persisting Volume Behavior for DeviceType: " + deviceType);
}
- System.putIntForUser(mContentResolver,
- getSettingsNameForDeviceVolumeBehavior(deviceType),
- deviceVolumeBehavior,
- UserHandle.USER_CURRENT);
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ System.putIntForUser(mContentResolver,
+ getSettingsNameForDeviceVolumeBehavior(deviceType),
+ deviceVolumeBehavior,
+ UserHandle.USER_CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
@AudioManager.DeviceVolumeBehaviorState
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index f3ff02f3aedc..0eb5a5d1fb48 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -27,28 +27,82 @@ import com.android.server.audio.AudioDeviceInventory.WiredDeviceConnectionState;
public class AudioServiceEvents {
final static class PhoneStateEvent extends AudioEventLogger.Event {
+ static final int MODE_SET = 0;
+ static final int MODE_IN_COMMUNICATION_TIMEOUT = 1;
+
+ final int mOp;
final String mPackage;
final int mOwnerPid;
final int mRequesterPid;
final int mRequestedMode;
final int mActualMode;
+ /** used for MODE_SET */
PhoneStateEvent(String callingPackage, int requesterPid, int requestedMode,
int ownerPid, int actualMode) {
+ mOp = MODE_SET;
mPackage = callingPackage;
mRequesterPid = requesterPid;
mRequestedMode = requestedMode;
mOwnerPid = ownerPid;
mActualMode = actualMode;
+ logMetricEvent();
+ }
+
+ /** used for MODE_IN_COMMUNICATION_TIMEOUT */
+ PhoneStateEvent(String callingPackage, int ownerPid) {
+ mOp = MODE_IN_COMMUNICATION_TIMEOUT;
+ mPackage = callingPackage;
+ mOwnerPid = ownerPid;
+ mRequesterPid = 0;
+ mRequestedMode = 0;
+ mActualMode = 0;
+ logMetricEvent();
}
@Override
public String eventToString() {
- return new StringBuilder("setMode(").append(AudioSystem.modeToString(mRequestedMode))
- .append(") from package=").append(mPackage)
- .append(" pid=").append(mRequesterPid)
- .append(" selected mode=").append(AudioSystem.modeToString(mActualMode))
- .append(" by pid=").append(mOwnerPid).toString();
+ switch (mOp) {
+ case MODE_SET:
+ return new StringBuilder("setMode(")
+ .append(AudioSystem.modeToString(mRequestedMode))
+ .append(") from package=").append(mPackage)
+ .append(" pid=").append(mRequesterPid)
+ .append(" selected mode=")
+ .append(AudioSystem.modeToString(mActualMode))
+ .append(" by pid=").append(mOwnerPid).toString();
+ case MODE_IN_COMMUNICATION_TIMEOUT:
+ return new StringBuilder("mode IN COMMUNICATION timeout")
+ .append(" for package=").append(mPackage)
+ .append(" pid=").append(mOwnerPid).toString();
+ default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
+ }
+ }
+
+ /**
+ * Audio Analytics unique Id.
+ */
+ private static final String mMetricsId = MediaMetrics.Name.AUDIO_MODE;
+
+ private void logMetricEvent() {
+ switch (mOp) {
+ case MODE_SET:
+ new MediaMetrics.Item(mMetricsId)
+ .set(MediaMetrics.Property.EVENT, "set")
+ .set(MediaMetrics.Property.REQUESTED_MODE,
+ AudioSystem.modeToString(mRequestedMode))
+ .set(MediaMetrics.Property.MODE, AudioSystem.modeToString(mActualMode))
+ .set(MediaMetrics.Property.CALLING_PACKAGE, mPackage)
+ .record();
+ return;
+ case MODE_IN_COMMUNICATION_TIMEOUT:
+ new MediaMetrics.Item(mMetricsId)
+ .set(MediaMetrics.Property.EVENT, "inCommunicationTimeout")
+ .set(MediaMetrics.Property.CALLING_PACKAGE, mPackage)
+ .record();
+ return;
+ default: return;
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 98f409ea98e7..a5778836aa6e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -366,6 +366,23 @@ public final class PlaybackActivityMonitor
releasePlayer(piid, 0);
}
+ /**
+ * Returns true if a player belonging to the app with given uid is active.
+ *
+ * @param uid the app uid
+ * @return true if a player is active, false otherwise
+ */
+ public boolean isPlaybackActiveForUid(int uid) {
+ synchronized (mPlayerLock) {
+ for (AudioPlaybackConfiguration apc : mPlayers.values()) {
+ if (apc.isActive() && apc.getClientUid() == uid) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
protected void dump(PrintWriter pw) {
// players
pw.println("\nPlaybackActivityMonitor dump time: "
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 32c6cc32a78d..ea0107ecfd23 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -215,6 +215,25 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
dispatchCallbacks(updateSnapshot(AudioManager.RECORD_CONFIG_EVENT_RELEASE, riid, null));
}
+ /**
+ * Returns true if a recorder belonging to the app with given uid is active.
+ *
+ * @param uid the app uid
+ * @return true if a recorder is active, false otherwise
+ */
+ public boolean isRecordingActiveForUid(int uid) {
+ synchronized (mRecordStates) {
+ for (RecordingState state : mRecordStates) {
+ // Note: isActiveConfiguration() == true => state.getConfig() != null
+ if (state.isActiveConfiguration()
+ && state.getConfig().getClientUid() == uid) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private void dispatchCallbacks(List<AudioRecordingConfiguration> configs) {
if (configs == null) { // null means "no changes"
return;
diff --git a/services/core/java/com/android/server/audio/SoundEffectsHelper.java b/services/core/java/com/android/server/audio/SoundEffectsHelper.java
index cf5bc8d88c73..27d57674e0f1 100644
--- a/services/core/java/com/android/server/audio/SoundEffectsHelper.java
+++ b/services/core/java/com/android/server/audio/SoundEffectsHelper.java
@@ -90,6 +90,10 @@ class SoundEffectsHelper {
mFileName = fileName;
mSampleId = EFFECT_NOT_IN_SOUND_POOL;
}
+ void unload() {
+ mSampleId = EFFECT_NOT_IN_SOUND_POOL;
+ mLoaded = false;
+ }
}
// All the fields below are accessed by the worker thread exclusively
private final List<Resource> mResources = new ArrayList<Resource>();
@@ -230,6 +234,7 @@ class SoundEffectsHelper {
for (Resource res : mResources) {
if (res.mSampleId != EFFECT_NOT_IN_SOUND_POOL) {
mSoundPool.unload(res.mSampleId);
+ res.unload();
}
}
mSoundPool.release();
@@ -247,7 +252,7 @@ class SoundEffectsHelper {
}
Resource res = mResources.get(mEffects[effect]);
- if (res.mSampleId != EFFECT_NOT_IN_SOUND_POOL && res.mLoaded) {
+ if (mSoundPool != null && res.mSampleId != EFFECT_NOT_IN_SOUND_POOL && res.mLoaded) {
mSoundPool.play(res.mSampleId, volFloat, volFloat, 0, 0, 1.0f);
} else {
MediaPlayer mediaPlayer = new MediaPlayer();
@@ -436,11 +441,12 @@ class SoundEffectsHelper {
onUnloadSoundEffects();
break;
case MSG_PLAY_EFFECT:
+ final int effect = msg.arg1, volume = msg.arg2;
onLoadSoundEffects(new OnEffectsLoadCompleteHandler() {
@Override
public void run(boolean success) {
if (success) {
- onPlaySoundEffect(msg.arg1 /*effect*/, msg.arg2 /*volume*/);
+ onPlaySoundEffect(effect, volume);
}
}
});
@@ -511,7 +517,9 @@ class SoundEffectsHelper {
}
void onComplete(boolean success) {
- mSoundPool.setOnLoadCompleteListener(null);
+ if (mSoundPool != null) {
+ mSoundPool.setOnLoadCompleteListener(null);
+ }
for (OnEffectsLoadCompleteHandler handler : mLoadCompleteHandlers) {
handler.run(success);
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index b8c639ceb8ba..a47904c40cc3 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -28,6 +28,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IAuthService;
@@ -127,6 +128,11 @@ public class AuthService extends SystemService {
return IIrisService.Stub.asInterface(
ServiceManager.getService(Context.IRIS_SERVICE));
}
+
+ @VisibleForTesting
+ public AppOpsManager getAppOps(Context context) {
+ return context.getSystemService(AppOpsManager.class);
+ }
}
private final class AuthServiceImpl extends IAuthService.Stub {
@@ -137,6 +143,8 @@ public class AuthService extends SystemService {
// Only allow internal clients to authenticate with a different userId.
final int callingUserId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
if (userId == callingUserId) {
checkPermission();
} else {
@@ -145,6 +153,16 @@ public class AuthService extends SystemService {
checkInternalPermission();
}
+ if (!checkAppOps(callingUid, opPackageName, "authenticate()")) {
+ Slog.e(TAG, "Denied by app ops: " + opPackageName);
+ return;
+ }
+
+ if (!Utils.isForeground(callingUid, callingPid)) {
+ Slog.e(TAG, "Caller is not foreground: " + opPackageName);
+ return;
+ }
+
if (token == null || receiver == null || opPackageName == null || promptInfo == null) {
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
return;
@@ -155,8 +173,6 @@ public class AuthService extends SystemService {
checkInternalPermission();
}
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
final long identity = Binder.clearCallingIdentity();
try {
mBiometricService.authenticate(
@@ -370,4 +386,9 @@ public class AuthService extends SystemService {
"Must have USE_BIOMETRIC permission");
}
}
+
+ private boolean checkAppOps(int uid, String opPackageName, String reason) {
+ return mInjector.getAppOps(getContext()).noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid,
+ opPackageName, null /* attributionTag */, reason) == AppOpsManager.MODE_ALLOWED;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 7a4f70a72252..08a617134221 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -32,6 +32,9 @@ import android.os.RemoteException;
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -54,6 +57,8 @@ class PreAuthInfo {
static final int BIOMETRIC_NOT_ENROLLED = 7;
static final int BIOMETRIC_NOT_ENABLED_FOR_APPS = 8;
static final int CREDENTIAL_NOT_ENROLLED = 9;
+ static final int BIOMETRIC_LOCKOUT_TIMED = 10;
+ static final int BIOMETRIC_LOCKOUT_PERMANENT = 11;
@IntDef({AUTHENTICATOR_OK,
BIOMETRIC_NO_HARDWARE,
BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
@@ -62,7 +67,9 @@ class PreAuthInfo {
BIOMETRIC_HARDWARE_NOT_DETECTED,
BIOMETRIC_NOT_ENROLLED,
BIOMETRIC_NOT_ENABLED_FOR_APPS,
- CREDENTIAL_NOT_ENROLLED})
+ CREDENTIAL_NOT_ENROLLED,
+ BIOMETRIC_LOCKOUT_TIMED,
+ BIOMETRIC_LOCKOUT_PERMANENT})
@Retention(RetentionPolicy.SOURCE)
@interface AuthenticatorStatus {}
@@ -73,7 +80,7 @@ class PreAuthInfo {
// Sensors that can be used for this request (e.g. strong enough, enrolled, enabled).
final List<BiometricSensor> eligibleSensors;
// Sensors that cannot be used for this request. Pair<BiometricSensor, AuthenticatorStatus>
- private final List<Pair<BiometricSensor, Integer>> mIneligibleSensors;
+ final List<Pair<BiometricSensor, Integer>> ineligibleSensors;
final boolean credentialAvailable;
final boolean confirmationRequested;
@@ -156,6 +163,14 @@ class PreAuthInfo {
if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)) {
return BIOMETRIC_NOT_ENROLLED;
}
+
+ final @LockoutTracker.LockoutMode int lockoutMode =
+ sensor.impl.getLockoutModeForUser(userId);
+ if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
+ return BIOMETRIC_LOCKOUT_TIMED;
+ } else if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
+ return BIOMETRIC_LOCKOUT_PERMANENT;
+ }
} catch (RemoteException e) {
return BIOMETRIC_HARDWARE_NOT_DETECTED;
}
@@ -232,7 +247,7 @@ class PreAuthInfo {
this.credentialRequested = credentialRequested;
this.eligibleSensors = eligibleSensors;
- this.mIneligibleSensors = ineligibleSensors;
+ this.ineligibleSensors = ineligibleSensors;
this.credentialAvailable = credentialAvailable;
this.confirmationRequested = confirmationRequested;
}
@@ -258,9 +273,9 @@ class PreAuthInfo {
}
} else {
// Pick the first sensor error if it exists
- if (!mIneligibleSensors.isEmpty()) {
- modality |= mIneligibleSensors.get(0).first.modality;
- status = mIneligibleSensors.get(0).second;
+ if (!ineligibleSensors.isEmpty()) {
+ modality |= ineligibleSensors.get(0).first.modality;
+ status = ineligibleSensors.get(0).second;
} else {
modality |= TYPE_CREDENTIAL;
status = CREDENTIAL_NOT_ENROLLED;
@@ -274,9 +289,9 @@ class PreAuthInfo {
}
} else {
// Pick the first sensor error if it exists
- if (!mIneligibleSensors.isEmpty()) {
- modality |= mIneligibleSensors.get(0).first.modality;
- status = mIneligibleSensors.get(0).second;
+ if (!ineligibleSensors.isEmpty()) {
+ modality |= ineligibleSensors.get(0).first.modality;
+ status = ineligibleSensors.get(0).second;
} else {
modality |= TYPE_NONE;
status = BIOMETRIC_NO_HARDWARE;
@@ -293,7 +308,7 @@ class PreAuthInfo {
}
Slog.d(TAG, "getCanAuthenticateInternal Modality: " + modality
- + " AuthenticatorSatus: " + status);
+ + " AuthenticatorStatus: " + status);
return new Pair<>(modality, status);
}
@@ -325,6 +340,8 @@ class PreAuthInfo {
case BIOMETRIC_HARDWARE_NOT_DETECTED:
case BIOMETRIC_NOT_ENROLLED:
case CREDENTIAL_NOT_ENROLLED:
+ case BIOMETRIC_LOCKOUT_TIMED:
+ case BIOMETRIC_LOCKOUT_PERMANENT:
break;
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
@@ -379,7 +396,7 @@ class PreAuthInfo {
string.append("}");
string.append("\nIneligible:{");
- for (Pair<BiometricSensor, Integer> ineligible : mIneligibleSensors) {
+ for (Pair<BiometricSensor, Integer> ineligible : ineligibleSensors) {
string.append(ineligible.first).append(":").append(ineligible.second).append(" ");
}
string.append("}");
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 0d4d5893e9b1..88fd44456ff6 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -16,31 +16,57 @@
package com.android.server.biometrics;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.server.biometrics.PreAuthInfo.AUTHENTICATOR_OK;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_DISABLED_BY_DEVICE_POLICY;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_HARDWARE_NOT_DETECTED;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_INSUFFICIENT_STRENGTH;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE;
+import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_LOCKOUT_PERMANENT;
+import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_LOCKOUT_TIMED;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENABLED_FOR_APPS;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENROLLED;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
+import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.PromptInfo;
+import android.os.Binder;
import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.Slog;
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.biometrics.sensors.ClientMonitor;
+
+import java.util.List;
+
public class Utils {
+
+ private static final String TAG = "BiometricUtils";
+
public static boolean isDebugEnabled(Context context, int targetUserId) {
if (targetUserId == UserHandle.USER_NULL) {
return false;
@@ -235,6 +261,10 @@ public class Utils {
case BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
break;
+ case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
+ case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
+ biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
+ break;
default:
Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
@@ -289,6 +319,12 @@ public class Utils {
case CREDENTIAL_NOT_ENROLLED:
return BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL;
+ case BIOMETRIC_LOCKOUT_TIMED:
+ return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
+
+ case BIOMETRIC_LOCKOUT_PERMANENT:
+ return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
case BIOMETRIC_HARDWARE_NOT_DETECTED:
case BIOMETRIC_NOT_ENABLED_FOR_APPS:
@@ -319,4 +355,93 @@ public class Utils {
}
return false;
}
+
+ public static void checkPermission(Context context, String permission) {
+ context.enforceCallingOrSelfPermission(permission,
+ "Must have " + permission + " permission.");
+ }
+
+ public static boolean isCurrentUserOrProfile(Context context, int userId) {
+ UserManager um = UserManager.get(context);
+ if (um == null) {
+ Slog.e(TAG, "Unable to get UserManager");
+ return false;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Allow current user or profiles of the current user...
+ for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
+ if (profileId == userId) {
+ return true;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return false;
+ }
+
+ public static boolean isStrongBiometric(int sensorId) {
+ IBiometricService service = IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE));
+ try {
+ return Utils.isAtLeastStrength(service.getCurrentStrength(sensorId),
+ Authenticators.BIOMETRIC_STRONG);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
+ return false;
+ }
+ }
+
+ public static boolean isKeyguard(Context context, String clientPackage) {
+ final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+ == PackageManager.PERMISSION_GRANTED;
+
+ final ComponentName keyguardComponent = ComponentName.unflattenFromString(
+ context.getResources().getString(R.string.config_keyguardComponent));
+ final String keyguardPackage = keyguardComponent != null
+ ? keyguardComponent.getPackageName() : null;
+ return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
+ }
+
+ public static String getClientName(@Nullable ClientMonitor<?> client) {
+ return client != null ? client.getClass().getSimpleName() : "null";
+ }
+
+ private static boolean containsFlag(int haystack, int needle) {
+ return (haystack & needle) != 0;
+ }
+
+ public static boolean isUserEncryptedOrLockdown(@NonNull LockPatternUtils lpu, int user) {
+ final int strongAuth = lpu.getStrongAuthForUser(user);
+ final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ final boolean isLockDown = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+ || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ Slog.d(TAG, "isEncrypted: " + isEncrypted + " isLockdown: " + isLockDown);
+ return isEncrypted || isLockDown;
+ }
+
+ public static boolean isForeground(int callingUid, int callingPid) {
+ try {
+ final List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ if (procs == null) {
+ Slog.e(TAG, "No running app processes found, defaulting to true");
+ return true;
+ }
+
+ for (int i = 0; i < procs.size(); i++) {
+ ActivityManager.RunningAppProcessInfo proc = procs.get(i);
+ if (proc.pid == callingPid && proc.uid == callingUid
+ && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "am.getRunningAppProcesses() failed");
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 5357004b491d..ab48fdb0fd6e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -16,6 +16,7 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.media.AudioAttributes;
@@ -31,7 +32,7 @@ import android.util.Slog;
* Abstract {@link ClientMonitor} subclass that operations eligible/interested in acquisition
* messages should extend.
*/
-public abstract class AcquisitionClient extends ClientMonitor {
+public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements Interruptable {
private static final String TAG = "Biometrics/AcquisitionClient";
@@ -44,31 +45,98 @@ public abstract class AcquisitionClient extends ClientMonitor {
private final PowerManager mPowerManager;
private final VibrationEffect mSuccessVibrationEffect;
private final VibrationEffect mErrorVibrationEffect;
+ private boolean mShouldSendErrorToClient;
+ private boolean mAlreadyCancelled;
- AcquisitionClient(Context context, BiometricServiceBase.DaemonWrapper daemon, IBinder token,
- ClientMonitorCallbackConverter listener, int userId, int groupId, boolean restricted,
- String owner, int cookie, int sensorId, int statsModality, int statsAction,
- int statsClient) {
- super(context, daemon, token, listener, userId, groupId, restricted, owner, cookie,
- sensorId,
- statsModality, statsAction, statsClient);
+ /**
+ * Stops the HAL operation specific to the ClientMonitor subclass.
+ */
+ protected abstract void stopHalOperation();
+
+ public AcquisitionClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int cookie, int sensorId, int statsModality,
+ int statsAction, int statsClient) {
+ super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality,
+ statsAction, statsClient);
mPowerManager = context.getSystemService(PowerManager.class);
mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
}
+ @Override
+ public void unableToStart() {
+ try {
+ getListener().onError(getSensorId(), getCookie(),
+ BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ }
+
+ @Override
+ public void onError(int errorCode, int vendorCode) {
+ // Errors from the HAL always finish the client
+ onErrorInternal(errorCode, vendorCode, true /* finish */);
+ }
+
+ protected void onErrorInternal(int errorCode, int vendorCode, boolean finish) {
+ // In some cases, the framework will send an error to the caller before a true terminal
+ // case (success, failure, or error) is received from the HAL (e.g. versions of fingerprint
+ // that do not handle lockout under the HAL. In these cases, ensure that the framework only
+ // sends errors once per ClientMonitor.
+ if (!mShouldSendErrorToClient) {
+ logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
+ try {
+ if (getListener() != null) {
+ mShouldSendErrorToClient = true;
+ getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke sendError", e);
+ }
+ }
+
+ if (finish) {
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (mAlreadyCancelled) {
+ Slog.w(TAG, "Cancel was already requested");
+ return;
+ }
+
+ stopHalOperation();
+ mAlreadyCancelled = true;
+ }
+
+ @Override
+ public void cancelWithoutStarting(@NonNull FinishCallback finishCallback) {
+ final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED;
+ try {
+ if (getListener() != null) {
+ getListener().onError(getSensorId(), getCookie(), errorCode, 0 /* vendorCode */);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke sendError", e);
+ }
+ finishCallback.onClientFinished(this, true /* success */);
+ }
+
/**
* Called when we get notification from the biometric's HAL that an image has been acquired.
* Common to authenticate and enroll.
* @param acquiredInfo info about the current image acquisition
- * @return true if client should be removed
*/
- public boolean onAcquired(int acquiredInfo, int vendorCode) {
+ public void onAcquired(int acquiredInfo, int vendorCode) {
// Default is to always send acquire messages to clients.
- return onAcquiredInternal(acquiredInfo, vendorCode, true /* shouldSend */);
+ onAcquiredInternal(acquiredInfo, vendorCode, true /* shouldSend */);
}
- protected final boolean onAcquiredInternal(int acquiredInfo, int vendorCode,
+ protected final void onAcquiredInternal(int acquiredInfo, int vendorCode,
boolean shouldSend) {
super.logOnAcquired(getContext(), acquiredInfo, vendorCode, getTargetUserId());
if (DEBUG) {
@@ -76,19 +144,18 @@ public abstract class AcquisitionClient extends ClientMonitor {
+ ", shouldSend: " + shouldSend);
}
+ // Good scans will keep the device awake
+ if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+ notifyUserActivity();
+ }
+
try {
if (getListener() != null && shouldSend) {
getListener().onAcquired(getSensorId(), acquiredInfo, vendorCode);
}
- return false; // acquisition continues...
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendAcquired", e);
- return true;
- } finally {
- // Good scans will keep the device awake
- if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- notifyUserActivity();
- }
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@@ -98,7 +165,7 @@ public abstract class AcquisitionClient extends ClientMonitor {
}
- final void vibrateSuccess() {
+ protected final void vibrateSuccess() {
Vibrator vibrator = getContext().getSystemService(Vibrator.class);
if (vibrator != null) {
vibrator.vibrate(mSuccessVibrationEffect, VIBRATION_SONFICATION_ATTRIBUTES);
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 2a66073bd374..cb2321f524f6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
@@ -27,65 +29,45 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.security.KeyStore;
import android.util.Slog;
-import android.view.Surface;
import java.util.ArrayList;
/**
* A class to keep track of the authentication state for a given client.
*/
-public abstract class AuthenticationClient extends AcquisitionClient {
+public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
+ implements AuthenticationConsumer {
private static final String TAG = "Biometrics/AuthenticationClient";
private final boolean mIsStrongBiometric;
- private final long mOpId;
private final boolean mRequireConfirmation;
private final IActivityTaskManager mActivityTaskManager;
- private final TaskStackListener mTaskStackListener;
+ @Nullable private final TaskStackListener mTaskStackListener;
private final LockoutTracker mLockoutTracker;
- private final Surface mSurface;
+ private final boolean mIsRestricted;
- private long mStartTimeMs;
+ protected final long mOperationId;
- /**
- * This method is called when authentication starts.
- */
- protected void onStart() {
- try {
- mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not register task stack listener", e);
- }
- }
+ private long mStartTimeMs;
- /**
- * This method is called when a biometric is authenticated or authentication is stopped
- * (cancelled by the user, or an error such as lockout has occurred).
- */
- protected void onStop() {
- try {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not unregister task stack listener", e);
- }
- }
+ protected boolean mAuthAttempted;
- public AuthenticationClient(Context context, BiometricServiceBase.DaemonWrapper daemon,
- IBinder token, ClientMonitorCallbackConverter listener, int targetUserId, int groupId,
- long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation,
- int sensorId, boolean isStrongBiometric, int statsModality, int statsClient,
- TaskStackListener taskStackListener, LockoutTracker lockoutTracker, Surface surface) {
- super(context, daemon, token, listener, targetUserId, groupId,
- restricted, owner, cookie, sensorId, statsModality,
- BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ int targetUserId, long operationId, boolean restricted, @NonNull String owner,
+ int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric,
+ int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener,
+ @NonNull LockoutTracker lockoutTracker) {
+ super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
+ statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
mIsStrongBiometric = isStrongBiometric;
- mOpId = opId;
+ mOperationId = operationId;
mRequireConfirmation = requireConfirmation;
mActivityTaskManager = ActivityTaskManager.getService();
mTaskStackListener = taskStackListener;
mLockoutTracker = lockoutTracker;
- mSurface = surface;
+ mIsRestricted = restricted;
}
public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
@@ -119,18 +101,17 @@ public abstract class AuthenticationClient extends AcquisitionClient {
@Override
protected boolean isCryptoOperation() {
- return mOpId != 0;
+ return mOperationId != 0;
}
- public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated, ArrayList<Byte> token) {
+ @Override
+ public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
+ boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
getTargetUserId(), isBiometricPrompt());
final ClientMonitorCallbackConverter listener = getListener();
- boolean result = false;
-
try {
if (DEBUG) Slog.v(TAG, "onAuthenticated(" + authenticated + ")"
+ ", ID:" + identifier.getBiometricId()
@@ -140,18 +121,31 @@ public abstract class AuthenticationClient extends AcquisitionClient {
+ ", requireConfirmation: " + mRequireConfirmation
+ ", user: " + getTargetUserId());
+ final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
+ if (isCryptoOperation()) {
+ pm.incrementCryptoAuthForUser(getTargetUserId(), authenticated);
+ } else {
+ pm.incrementAuthForUser(getTargetUserId(), authenticated);
+ }
+
if (authenticated) {
mAlreadyDone = true;
if (listener != null) {
vibrateSuccess();
}
- result = true;
- onStop();
- final byte[] byteToken = new byte[token.size()];
- for (int i = 0; i < token.size(); i++) {
- byteToken[i] = token.get(i);
+ if (mTaskStackListener != null) {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not unregister task stack listener", e);
+ }
+ }
+
+ final byte[] byteToken = new byte[hardwareAuthToken.size()];
+ for (int i = 0; i < hardwareAuthToken.size(); i++) {
+ byteToken[i] = hardwareAuthToken.get(i);
}
if (isBiometricPrompt() && listener != null) {
// BiometricService will add the token to keystore
@@ -164,23 +158,19 @@ public abstract class AuthenticationClient extends AcquisitionClient {
Slog.d(TAG, "Skipping addAuthToken");
}
- try {
- // Explicitly have if/else here to make it super obvious in case the code is
- // touched in the future.
- if (!getIsRestricted()) {
- listener.onAuthenticationSucceeded(getSensorId(), identifier, byteToken,
- getTargetUserId(), mIsStrongBiometric);
- } else {
- listener.onAuthenticationSucceeded(getSensorId(), null /* identifier */,
- byteToken, getTargetUserId(), mIsStrongBiometric);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
+ // Explicitly have if/else here to make it super obvious in case the code is
+ // touched in the future.
+ if (!mIsRestricted) {
+ listener.onAuthenticationSucceeded(getSensorId(), identifier, byteToken,
+ getTargetUserId(), mIsStrongBiometric);
+ } else {
+ listener.onAuthenticationSucceeded(getSensorId(), null /* identifier */,
+ byteToken, getTargetUserId(), mIsStrongBiometric);
}
+
} else {
// Client not listening
Slog.w(TAG, "Client not listening");
- result = true;
}
} else {
if (listener != null) {
@@ -198,60 +188,68 @@ public abstract class AuthenticationClient extends AcquisitionClient {
listener.onAuthenticationFailed(getSensorId());
}
}
- result = lockoutMode != LockoutTracker.LOCKOUT_NONE; // in a lockout mode
}
} catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- result = true;
+ Slog.e(TAG, "Unable to notify listener, finishing", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ public void onAcquired(int acquiredInfo, int vendorCode) {
+ super.onAcquired(acquiredInfo, vendorCode);
+
+ final @LockoutTracker.LockoutMode int lockoutMode =
+ mLockoutTracker.getLockoutModeForUser(getTargetUserId());
+ if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
+ PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
+ pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
}
- return result;
}
/**
* Start authentication
*/
@Override
- public int start() {
- onStart();
- try {
- mStartTimeMs = System.currentTimeMillis();
- final int result = getDaemonWrapper().authenticate(mOpId, getGroupId(), mSurface);
- if (result != 0) {
- Slog.w(TAG, "startAuthentication failed, result=" + result);
- onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
- } catch (RemoteException e) {
- Slog.e(TAG, "startAuthentication failed", e);
- return ERROR_ESRCH;
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
+ final @LockoutTracker.LockoutMode int lockoutMode =
+ mLockoutTracker.getLockoutModeForUser(getTargetUserId());
+ if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
+ Slog.v(TAG, "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
+ int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
+ ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+ : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ onError(errorCode, 0 /* vendorCode */);
+ return;
}
- return 0; // success
- }
- @Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopAuthentication: already cancelled!");
- return 0;
+ if (mTaskStackListener != null) {
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not register task stack listener", e);
+ }
}
- onStop();
+ if (DEBUG) Slog.w(TAG, "Requesting auth for " + getOwnerString());
- try {
- final int result = getDaemonWrapper().cancel();
- if (result != 0) {
- Slog.w(TAG, "stopAuthentication failed, result=" + result);
- return result;
+ mStartTimeMs = System.currentTimeMillis();
+ mAuthAttempted = true;
+ startHalOperation();
+ }
+
+ @Override
+ public void cancel() {
+ super.cancel();
+
+ if (mTaskStackListener != null) {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not unregister task stack listener", e);
}
- if (DEBUG) Slog.w(TAG, "client " + getOwnerString() +
- " is no longer authenticating");
- } catch (RemoteException e) {
- Slog.e(TAG, "stopAuthentication failed", e);
- return ERROR_ESRCH;
}
-
- mAlreadyCancelled = true;
- return 0; // success
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationConsumer.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationConsumer.java
new file mode 100644
index 000000000000..09a577eb2825
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationConsumer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.hardware.biometrics.BiometricAuthenticator;
+
+import java.util.ArrayList;
+
+/**
+ * Interface that clients interested/eligible for authentication events should implement.
+ */
+public interface AuthenticationConsumer {
+ void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated,
+ ArrayList<Byte> hardwareAuthToken);
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
new file mode 100644
index 000000000000..0aeb7abe706e
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.IBiometricService;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Queue;
+
+/**
+ * A scheduler for biometric HAL operations. Maintains a queue of {@link ClientMonitor} operations,
+ * without caring about its implementation details. Operations may perform one or more
+ * interactions with the HAL before finishing.
+ */
+public class BiometricScheduler {
+
+ private static final String BASE_TAG = "BiometricScheduler";
+
+ /**
+ * Contains all the necessary information for a HAL operation.
+ */
+ private static final class Operation {
+
+ /**
+ * The operation is added to the list of pending operations and waiting for its turn.
+ */
+ static final int STATE_WAITING_IN_QUEUE = 0;
+
+ /**
+ * The operation is added to the list of pending operations, but a subsequent operation
+ * has been added. This state only applies to {@link Interruptable} operations. When this
+ * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
+ */
+ static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
+
+ /**
+ * The operation has reached the front of the queue and has started.
+ */
+ static final int STATE_STARTED = 2;
+
+ /**
+ * The operation was started, but is now canceling. Operations should wait for the HAL to
+ * acknowledge that the operation was canceled, at which point it finishes.
+ */
+ static final int STATE_STARTED_CANCELING = 3;
+
+ /**
+ * The operation has reached the head of the queue but is waiting for BiometricService
+ * to acknowledge and start the operation.
+ */
+ static final int STATE_WAITING_FOR_COOKIE = 4;
+
+ /**
+ * The {@link ClientMonitor.FinishCallback} has been invoked and the client is finished.
+ */
+ static final int STATE_FINISHED = 5;
+
+ @IntDef({STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_IN_QUEUE_CANCELING,
+ STATE_STARTED,
+ STATE_STARTED_CANCELING,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_FINISHED})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface OperationState {}
+
+ @NonNull final ClientMonitor<?> clientMonitor;
+ @Nullable final ClientMonitor.FinishCallback clientFinishCallback;
+ @OperationState int state;
+
+ Operation(@NonNull ClientMonitor<?> clientMonitor,
+ @Nullable ClientMonitor.FinishCallback finishCallback) {
+ this.clientMonitor = clientMonitor;
+ this.clientFinishCallback = finishCallback;
+ state = STATE_WAITING_IN_QUEUE;
+ }
+
+ @Override
+ public String toString() {
+ return clientMonitor + ", State: " + state;
+ }
+ }
+
+ /**
+ * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will
+ * kill the current operation and forcibly start the next.
+ */
+ private static final class CancellationWatchdog implements Runnable {
+ static final int DELAY_MS = 3000;
+
+ final String tag;
+ final Operation operation;
+ CancellationWatchdog(String tag, Operation operation) {
+ this.tag = tag;
+ this.operation = operation;
+ }
+
+ @Override
+ public void run() {
+ if (operation.state != Operation.STATE_FINISHED) {
+ Slog.e(tag, "[Watchdog Triggered]: " + operation);
+ operation.clientMonitor.mFinishCallback
+ .onClientFinished(operation.clientMonitor, false /* success */);
+ }
+ }
+ }
+
+ private static final class CrashState {
+ static final int NUM_ENTRIES = 10;
+ final String timestamp;
+ final String currentOperation;
+ final List<String> pendingOperations;
+
+ CrashState(String timestamp, String currentOperation, List<String> pendingOperations) {
+ this.timestamp = timestamp;
+ this.currentOperation = currentOperation;
+ this.pendingOperations = pendingOperations;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(timestamp).append(": ");
+ sb.append("Current Operation: {").append(currentOperation).append("}");
+ sb.append(", Pending Operations(").append(pendingOperations.size()).append(")");
+
+ if (!pendingOperations.isEmpty()) {
+ sb.append(": ");
+ }
+ for (int i = 0; i < pendingOperations.size(); i++) {
+ sb.append(pendingOperations.get(i));
+ if (i < pendingOperations.size() - 1) {
+ sb.append(", ");
+ }
+ }
+ return sb.toString();
+ }
+ }
+
+ @NonNull private final String mBiometricTag;
+ @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
+ @NonNull private final IBiometricService mBiometricService;
+ @NonNull private final Handler mHandler = new Handler(Looper.getMainLooper());
+ @NonNull private final InternalFinishCallback mInternalFinishCallback;
+ @NonNull private final Queue<Operation> mPendingOperations;
+ @Nullable private Operation mCurrentOperation;
+ @NonNull private final ArrayDeque<CrashState> mCrashStates;
+
+ // Internal finish callback, notified when an operation is complete. Notifies the requester
+ // that the operation is complete, before performing internal scheduler work (such as
+ // starting the next client).
+ private class InternalFinishCallback implements ClientMonitor.FinishCallback {
+ @Override
+ public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) {
+ mHandler.post(() -> {
+ if (mCurrentOperation == null) {
+ Slog.e(getTag(), "[Finishing] " + clientMonitor
+ + " but current operation is null, success: " + success
+ + ", possible lifecycle bug in clientMonitor implementation?");
+ return;
+ }
+
+ mCurrentOperation.state = Operation.STATE_FINISHED;
+
+ if (mCurrentOperation.clientFinishCallback != null) {
+ mCurrentOperation.clientFinishCallback.onClientFinished(clientMonitor, success);
+ }
+
+ if (clientMonitor != mCurrentOperation.clientMonitor) {
+ throw new IllegalStateException("Mismatched operation, "
+ + " current: " + mCurrentOperation.clientMonitor
+ + " received: " + clientMonitor);
+ }
+
+ Slog.d(getTag(), "[Finished] " + clientMonitor + ", success: " + success);
+ if (mGestureAvailabilityDispatcher != null) {
+ mGestureAvailabilityDispatcher.markSensorActive(
+ mCurrentOperation.clientMonitor.getSensorId(), false /* active */);
+ }
+
+ mCurrentOperation = null;
+ startNextOperationIfIdle();
+ });
+ }
+ }
+
+ /**
+ * Creates a new scheduler.
+ * @param tag for the specific instance of the scheduler. Should be unique.
+ * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
+ * (such as fingerprint swipe).
+ */
+ public BiometricScheduler(@NonNull String tag,
+ @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ mBiometricTag = tag;
+ mInternalFinishCallback = new InternalFinishCallback();
+ mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
+ mPendingOperations = new ArrayDeque<>();
+ mBiometricService = IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE));
+ mCrashStates = new ArrayDeque<>();
+ }
+
+ private String getTag() {
+ return BASE_TAG + "/" + mBiometricTag;
+ }
+
+ private void startNextOperationIfIdle() {
+ if (mCurrentOperation != null) {
+ Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation);
+ return;
+ }
+ if (mPendingOperations.isEmpty()) {
+ Slog.d(getTag(), "No operations, returning to idle");
+ return;
+ }
+
+ mCurrentOperation = mPendingOperations.poll();
+ final ClientMonitor<?> currentClient = mCurrentOperation.clientMonitor;
+
+ // If the operation at the front of the queue has been marked for cancellation, send
+ // ERROR_CANCELED. No need to start this client.
+ if (mCurrentOperation.state == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
+ if (!(currentClient instanceof Interruptable)) {
+ throw new IllegalStateException("Mis-implemented client or scheduler, "
+ + "trying to cancel non-interruptable operation: " + mCurrentOperation);
+ }
+
+ final Interruptable interruptable = (Interruptable) currentClient;
+ interruptable.cancelWithoutStarting(mInternalFinishCallback);
+ // Now we wait for the client to send its FinishCallback, which kicks off the next
+ // operation.
+ return;
+ }
+
+ if (mGestureAvailabilityDispatcher != null
+ && mCurrentOperation.clientMonitor instanceof AcquisitionClient) {
+ mGestureAvailabilityDispatcher.markSensorActive(
+ mCurrentOperation.clientMonitor.getSensorId(),
+ true /* active */);
+ }
+
+ // Not all operations start immediately. BiometricPrompt waits for its operation
+ // to arrive at the head of the queue, before pinging it to start.
+ final boolean shouldStartNow = currentClient.getCookie() == 0;
+ if (shouldStartNow) {
+ Slog.d(getTag(), "[Starting] " + mCurrentOperation);
+ currentClient.start(mInternalFinishCallback);
+ mCurrentOperation.state = Operation.STATE_STARTED;
+ } else {
+ try {
+ mBiometricService.onReadyForAuthentication(currentClient.getCookie());
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
+ }
+ Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
+ mCurrentOperation.state = Operation.STATE_WAITING_FOR_COOKIE;
+ }
+ }
+
+ /**
+ * Starts the {@link #mCurrentOperation} if
+ * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and
+ * 2) its cookie matches this cookie
+ *
+ * This is currently only used by {@link com.android.server.biometrics.BiometricService}, which
+ * requests sensors to prepare for authentication with a cookie. Once sensor(s) are ready (e.g.
+ * the BiometricService client becomes the current client in the scheduler), the cookie is
+ * returned to BiometricService. Once BiometricService decides that authentication can start,
+ * it invokes this code path.
+ *
+ * @param cookie of the operation to be started
+ */
+ public void startPreparedClient(int cookie) {
+ if (mCurrentOperation == null) {
+ Slog.e(getTag(), "Current operation is null");
+ return;
+ }
+ if (mCurrentOperation.state != Operation.STATE_WAITING_FOR_COOKIE) {
+ Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation
+ + ", expected STATE_WAITING_FOR_COOKIE");
+ return;
+ }
+ if (mCurrentOperation.clientMonitor.getCookie() != cookie) {
+ Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation
+ + ", received: " + cookie);
+ return;
+ }
+
+ Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
+ mCurrentOperation.state = Operation.STATE_STARTED;
+ mCurrentOperation.clientMonitor.start(mInternalFinishCallback);
+ }
+
+ /**
+ * Adds a {@link ClientMonitor} to the pending queue
+ *
+ * @param clientMonitor operation to be scheduled
+ */
+ public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor) {
+ scheduleClientMonitor(clientMonitor, null /* clientFinishCallback */);
+ }
+
+ /**
+ * Adds a {@link ClientMonitor} to the pending queue
+ *
+ * @param clientMonitor operation to be scheduled
+ * @param clientFinishCallback optional callback, invoked when the client is finished, but
+ * before it has been removed from the queue.
+ */
+ public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor,
+ @Nullable ClientMonitor.FinishCallback clientFinishCallback) {
+ // Mark any interruptable pending clients as canceling. Once they reach the head of the
+ // queue, the scheduler will send ERROR_CANCELED and skip the operation.
+ for (Operation operation : mPendingOperations) {
+ if (operation.clientMonitor instanceof Interruptable
+ && operation.state != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
+ + operation.clientMonitor);
+ operation.state = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+ }
+ }
+
+ mPendingOperations.add(new Operation(clientMonitor, clientFinishCallback));
+ Slog.d(getTag(), "[Added] " + clientMonitor
+ + ", new queue size: " + mPendingOperations.size());
+
+ // If the current operation is cancellable, start the cancellation process.
+ if (mCurrentOperation != null && mCurrentOperation.clientMonitor instanceof Interruptable
+ && mCurrentOperation.state == Operation.STATE_STARTED) {
+ Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
+ cancelInternal(mCurrentOperation);
+ }
+
+ startNextOperationIfIdle();
+ }
+
+ private void cancelInternal(Operation operation) {
+ if (operation != mCurrentOperation) {
+ Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation);
+ return;
+ }
+ if (!(operation.clientMonitor instanceof Interruptable)) {
+ Slog.w(getTag(), "Operation not interruptable: " + operation);
+ return;
+ }
+ if (operation.state == Operation.STATE_STARTED_CANCELING) {
+ Slog.w(getTag(), "Cancel already invoked for operation: " + operation);
+ return;
+ }
+ Slog.d(getTag(), "[Cancelling] Current client: " + operation.clientMonitor);
+ final Interruptable interruptable = (Interruptable) operation.clientMonitor;
+ interruptable.cancel();
+ operation.state = Operation.STATE_STARTED_CANCELING;
+
+ // Add a watchdog. If the HAL does not acknowledge within the timeout, we will
+ // forcibly finish this client.
+ mHandler.postDelayed(new CancellationWatchdog(getTag(), operation),
+ CancellationWatchdog.DELAY_MS);
+ }
+
+ /**
+ * Requests to cancel enrollment.
+ * @param token from the caller, should match the token passed in when requesting enrollment
+ */
+ public void cancelEnrollment(IBinder token) {
+ if (mCurrentOperation == null) {
+ Slog.e(getTag(), "Unable to cancel enrollment, null operation");
+ return;
+ }
+ final boolean isEnrolling = mCurrentOperation.clientMonitor instanceof EnrollClient;
+ final boolean tokenMatches = mCurrentOperation.clientMonitor.getToken() == token;
+ if (!isEnrolling || !tokenMatches) {
+ Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling
+ + " tokenMatches: " + tokenMatches);
+ return;
+ }
+
+ cancelInternal(mCurrentOperation);
+ }
+
+ /**
+ * Requests to cancel authentication.
+ * @param token from the caller, should match the token passed in when requesting authentication
+ */
+ public void cancelAuthentication(IBinder token) {
+ if (mCurrentOperation == null) {
+ Slog.e(getTag(), "Unable to cancel authentication, null operation");
+ return;
+ }
+ final boolean isAuthenticating =
+ mCurrentOperation.clientMonitor instanceof AuthenticationConsumer;
+ final boolean tokenMatches = mCurrentOperation.clientMonitor.getToken() == token;
+ if (!isAuthenticating || !tokenMatches) {
+ Slog.w(getTag(), "Not cancelling authentication, isEnrolling: " + isAuthenticating
+ + " tokenMatches: " + tokenMatches);
+ return;
+ }
+
+ cancelInternal(mCurrentOperation);
+ }
+
+ /**
+ * @return the current operation
+ */
+ public ClientMonitor<?> getCurrentClient() {
+ if (mCurrentOperation == null) {
+ return null;
+ }
+ return mCurrentOperation.clientMonitor;
+ }
+
+ public void recordCrashState() {
+ if (mCrashStates.size() >= CrashState.NUM_ENTRIES) {
+ mCrashStates.removeFirst();
+ }
+ final SimpleDateFormat dateFormat =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+ final String timestamp = dateFormat.format(new Date(System.currentTimeMillis()));
+ final List<String> pendingOperations = new ArrayList<>();
+ for (Operation operation : mPendingOperations) {
+ pendingOperations.add(operation.toString());
+ }
+
+ final CrashState crashState = new CrashState(timestamp,
+ mCurrentOperation != null ? mCurrentOperation.toString() : null,
+ pendingOperations);
+ mCrashStates.add(crashState);
+ Slog.e(getTag(), "Recorded crash state: " + crashState.toString());
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("Dump of BiometricScheduler " + getTag());
+ for (CrashState crashState : mCrashStates) {
+ pw.println("Crash State " + crashState);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
deleted file mode 100644
index f4c701bdeba4..000000000000
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
+++ /dev/null
@@ -1,1022 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.biometrics.sensors;
-
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
-import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.AppOpsManager;
-import android.app.IActivityTaskManager;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.TaskStackListener;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricManager.Authenticators;
-import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.IBiometricService;
-import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
-import android.hardware.fingerprint.Fingerprint;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IHwBinder;
-import android.os.IRemoteCallback;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Slog;
-import android.view.Surface;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.SystemService;
-import com.android.server.biometrics.Utils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Abstract base class containing all of the business logic for biometric services, e.g.
- * Fingerprint, Face, Iris.
- *
- * @hide
- */
-public abstract class BiometricServiceBase extends SystemService
- implements IHwBinder.DeathRecipient {
-
- protected static final boolean DEBUG = true;
-
- private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true;
- private static final int MSG_USER_SWITCHING = 10;
- private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
-
- private final Context mContext;
- private final String mKeyguardPackage;
- protected final IActivityTaskManager mActivityTaskManager;
- private final PowerManager mPowerManager;
- private final UserManager mUserManager;
- protected final BiometricTaskStackListener mTaskStackListener =
- new BiometricTaskStackListener();
- private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
- private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
-
- protected final IStatusBarService mStatusBarService;
- protected final Map<Integer, Long> mAuthenticatorIds =
- Collections.synchronizedMap(new HashMap<>());
- protected final AppOpsManager mAppOps;
-
- /**
- * Handler which all subclasses should post events to.
- */
- protected final Handler mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case MSG_USER_SWITCHING:
- handleUserSwitching(msg.arg1);
- break;
- default:
- Slog.w(getTag(), "Unknown message:" + msg.what);
- }
- }
- };
-
- private IBiometricService mBiometricService;
- private ClientMonitor mCurrentClient;
- private ClientMonitor mPendingClient;
- private PerformanceTracker mPerformanceTracker;
- private int mSensorId;
- protected int mCurrentUserId = UserHandle.USER_NULL;
-
- /**
- * @return the log tag.
- */
- protected abstract String getTag();
-
- /**
- * @return wrapper for the HAL
- */
- protected abstract DaemonWrapper getDaemonWrapper();
-
- /**
- * @return the biometric utilities for a specific implementation.
- */
- protected abstract BiometricUtils getBiometricUtils();
-
- /**
- * @param userId
- * @return true if the enrollment limit has been reached.
- */
- protected abstract boolean hasReachedEnrollmentLimit(int userId);
-
- /**
- * Notifies the HAL that the user has changed.
- * @param userId
- * @param clientPackage
- */
- protected abstract void updateActiveGroup(int userId, String clientPackage);
-
- /**
- * @param userId
- * @return Returns true if the user has any enrolled biometrics.
- */
- protected abstract boolean hasEnrolledBiometrics(int userId);
-
- /**
- * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
- * etc.
- */
- protected abstract String getManageBiometricPermission();
-
- /**
- * Checks if the caller has permission to use the biometric service - throws a SecurityException
- * if not.
- */
- protected abstract void checkUseBiometricPermission();
-
- /**
- * Checks if the caller passes the app ops check
- */
- protected abstract boolean checkAppOps(int uid, String opPackageName);
-
- protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
- int userId);
-
- /**
- * Notifies clients of any change in the biometric state (active / idle). This is mainly for
- * Fingerprint navigation gestures.
- * @param isActive
- */
- protected void notifyClientActiveCallbacks(boolean isActive) {}
-
- protected abstract int statsModality();
-
- /**
- * @return one of the AuthenticationClient LOCKOUT constants
- */
- protected abstract @LockoutTracker.LockoutMode int getLockoutMode(int userId);
-
- /**
- * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
- * subclasses.
- */
- public interface DaemonWrapper {
- int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
- int authenticate(long operationId, int groupId, Surface surface)
- throws RemoteException;
- int cancel() throws RemoteException;
- int remove(int groupId, int biometricId) throws RemoteException;
- int enumerate() throws RemoteException;
- int enroll(byte[] token, int groupId, int timeout,
- ArrayList<Integer> disabledFeatures, Surface surface) throws RemoteException;
- void resetLockout(byte[] token) throws RemoteException;
- }
-
- private final Runnable mOnTaskStackChangedRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- if (!(mCurrentClient instanceof AuthenticationClient)) {
- return;
- }
- final String currentClient = mCurrentClient.getOwnerString();
- if (isKeyguard(currentClient)) {
- return; // Keyguard is always allowed
- }
- List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(currentClient)
- && !mCurrentClient.isAlreadyDone()) {
- Slog.e(getTag(), "Stopping background authentication, top: "
- + topPackage + " currentClient: " + currentClient);
- mCurrentClient.stop(false /* initiatedByClient */);
- }
- }
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to get running tasks", e);
- }
- }
- };
-
- private final class BiometricTaskStackListener extends TaskStackListener {
- @Override
- public void onTaskStackChanged() {
- mHandler.post(mOnTaskStackChangedRunnable);
- }
- }
-
- private final class ResetClientStateRunnable implements Runnable {
- @Override
- public void run() {
- /**
- * Warning: if we get here, the driver never confirmed our call to cancel the current
- * operation (authenticate, enroll, remove, enumerate, etc), which is
- * really bad. The result will be a 3-second delay in starting each new client.
- * If you see this on a device, make certain the driver notifies with
- * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
- * once it has successfully switched to the IDLE state in the HAL.
- * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
- * in response to an actual cancel() call.
- */
- Slog.w(getTag(), "Client "
- + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
- + " failed to respond to cancel, starting client "
- + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
-
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
-
- ClientMonitor newClient = mPendingClient;
- mCurrentClient = null;
- mPendingClient = null;
- startClient(newClient, false);
- }
- }
-
-
-
- private final class LockoutResetMonitor implements IBinder.DeathRecipient {
- private static final long WAKELOCK_TIMEOUT_MS = 2000;
- private final IBiometricServiceLockoutResetCallback mCallback;
- private final PowerManager.WakeLock mWakeLock;
-
- public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
- mCallback = callback;
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "lockout reset callback");
- try {
- mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
- } catch (RemoteException e) {
- Slog.w(getTag(), "caught remote exception in linkToDeath", e);
- }
- }
-
- public void sendLockoutReset() {
- if (mCallback != null) {
- try {
- mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
- mCallback.onLockoutReset(new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- releaseWakelock();
- }
- });
- } catch (DeadObjectException e) {
- Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
- mHandler.post(mRemoveCallbackRunnable);
- } catch (RemoteException e) {
- Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
- releaseWakelock();
- }
- }
- }
-
- private final Runnable mRemoveCallbackRunnable = new Runnable() {
- @Override
- public void run() {
- releaseWakelock();
- removeLockoutResetCallback(LockoutResetMonitor.this);
- }
- };
-
- @Override
- public void binderDied() {
- Slog.e(getTag(), "Lockout reset callback binder died");
- mHandler.post(mRemoveCallbackRunnable);
- }
-
- private void releaseWakelock() {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
- }
-
- /**
- * Initializes the system service.
- * <p>
- * Subclasses must define a single argument constructor that accepts the context
- * and passes it to super.
- * </p>
- *
- * @param context The system server context.
- */
- public BiometricServiceBase(Context context) {
- super(context);
- mContext = context;
- mStatusBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- final ComponentName keyguardComponent = ComponentName.unflattenFromString(
- context.getResources().getString(R.string.config_keyguardComponent));
- mKeyguardPackage = keyguardComponent != null ? keyguardComponent.getPackageName() : null;
- mAppOps = context.getSystemService(AppOpsManager.class);
- mActivityTaskManager = ActivityTaskManager.getService();
- mPowerManager = mContext.getSystemService(PowerManager.class);
- mUserManager = UserManager.get(mContext);
- mPerformanceTracker = PerformanceTracker.getInstanceForSensorId(getSensorId());
- }
-
- @Override
- public void onStart() {
- listenForUserSwitches();
- }
-
- @Override
- public void serviceDied(long cookie) {
- Slog.e(getTag(), "HAL died");
- mPerformanceTracker.incrementHALDeathCount();
- mCurrentUserId = UserHandle.USER_NULL;
-
- // All client lifecycle must be managed on the handler.
- mHandler.post(() -> {
- Slog.e(getTag(), "Sending BIOMETRIC_ERROR_HW_UNAVAILABLE after HAL crash");
- handleError(BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- });
-
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- statsModality(), BiometricsProtoEnums.ISSUE_HAL_DEATH);
- }
-
- protected void initializeConfigurationInternal(int sensorId) {
- if (DEBUG) {
- Slog.d(getTag(), "initializeConfigurationInternal(" + sensorId + ")");
- }
- mSensorId = sensorId;
- }
-
- protected ClientMonitor getCurrentClient() {
- return mCurrentClient;
- }
-
- protected ClientMonitor getPendingClient() {
- return mPendingClient;
- }
-
- protected boolean isStrongBiometric() {
- IBiometricService service = IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE));
- try {
- return Utils.isAtLeastStrength(service.getCurrentStrength(mSensorId),
- Authenticators.BIOMETRIC_STRONG);
- } catch (RemoteException e) {
- Slog.e(getTag(), "RemoteException", e);
- return false;
- }
- }
-
- protected int getSensorId() {
- return mSensorId;
- }
-
- /**
- * Callback handlers from the daemon. The caller must put this on a handler.
- */
-
- protected void handleAcquired(int acquiredInfo, int vendorCode) {
- final ClientMonitor client = mCurrentClient;
- if (!(client instanceof AcquisitionClient)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleAcquired for non-acquire consumer: " + clientName);
- return;
- }
-
- final AcquisitionClient acquisitionClient = (AcquisitionClient) client;
- if (acquisitionClient.onAcquired(acquiredInfo, vendorCode)) {
- removeClient(client);
- }
-
- if (client instanceof AuthenticationClient) {
- final int userId = client.getTargetUserId();
- if (getLockoutMode(userId) == LockoutTracker.LOCKOUT_NONE) {
- mPerformanceTracker.incrementAcquireForUser(userId, client.isCryptoOperation());
- }
- }
- }
-
- protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
- ArrayList<Byte> token) {
- final ClientMonitor client = mCurrentClient;
- if (!(client instanceof AuthenticationClient)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleAuthenticated for non-authentication client: " + clientName);
- return;
- }
-
- final AuthenticationClient authenticationClient = (AuthenticationClient) client;
- final boolean authenticated = identifier.getBiometricId() != 0;
-
- final int userId = authenticationClient.getTargetUserId();
- if (authenticationClient.isCryptoOperation()) {
- mPerformanceTracker.incrementCryptoAuthForUser(userId, authenticated);
- } else {
- mPerformanceTracker.incrementAuthForUser(userId, authenticated);
- }
- if (authenticationClient.onAuthenticated(identifier, authenticated, token)) {
- removeClient(authenticationClient);
- }
- }
-
- protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
- int remaining) {
- final ClientMonitor client = mCurrentClient;
- if (!(client instanceof EnrollClient)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleEnrollResult for non-enroll client: " + clientName);
- return;
- }
-
- final EnrollClient enrollClient = (EnrollClient) client;
-
- if (enrollClient.onEnrollResult(identifier, remaining)) {
- removeClient(enrollClient);
- // When enrollment finishes, update this group's authenticator id, as the HAL has
- // already generated a new authenticator id when the new biometric is enrolled.
- if (identifier instanceof Fingerprint) {
- updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
- }
- }
- }
-
- protected void handleError(int error, int vendorCode) {
- final ClientMonitor client = mCurrentClient;
-
- if (DEBUG) Slog.v(getTag(), "handleError(client="
- + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
-
- if (client != null) {
- client.onError(error, vendorCode);
- removeClient(client);
- }
-
- if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
- mHandler.removeCallbacks(mResetClientState);
- if (mPendingClient != null) {
- if (DEBUG) Slog.v(getTag(), "start pending client " +
- mPendingClient.getOwnerString());
- startClient(mPendingClient, false);
- mPendingClient = null;
- }
- }
- }
-
- protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
- final int remaining) {
- if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
- + ", dev=" + identifier.getDeviceId()
- + ", rem=" + remaining);
-
- final ClientMonitor client = mCurrentClient;
- if (!(client instanceof RemovalConsumer)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleRemoved for non-removal consumer: " + clientName);
- return;
- }
-
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
- if (removalConsumer.onRemoved(identifier, remaining)) {
- removeClient(client);
- // When the last biometric of a group is removed, update the authenticator id
- int userId = mCurrentUserId;
- if (identifier instanceof Fingerprint) {
- userId = ((Fingerprint) identifier).getGroupId();
- }
- if (!hasEnrolledBiometrics(userId)) {
- updateActiveGroup(userId, null);
- }
- }
- }
-
- protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
- final ClientMonitor client = mCurrentClient;
- if (!(client instanceof EnumerateConsumer)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleEnumerate for non-enumerate consumer: "
- + clientName);
- return;
- }
-
- final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
- if (enumerateConsumer.onEnumerationResult(identifier, remaining)) {
- removeClient(client);
- }
- }
-
- /**
- * Calls from the Manager. These are still on the calling binder's thread.
- */
-
- protected void enrollInternal(EnrollClient client, int userId) {
- if (hasReachedEnrollmentLimit(userId)) {
- return;
- }
-
- // Group ID is arbitrarily set to parent profile user ID. It just represents
- // the default biometrics for the user.
- if (!isCurrentUserOrProfile(userId)) {
- return;
- }
-
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- protected void cancelEnrollmentInternal(IBinder token) {
- mHandler.post(() -> {
- ClientMonitor client = mCurrentClient;
- if (client instanceof EnrollClient && client.getToken() == token) {
- if (DEBUG) Slog.v(getTag(), "Cancelling enrollment");
- client.stop(client.getToken() == token);
- }
- });
- }
-
- protected void authenticateInternal(AuthenticationClient client, String opPackageName) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
- authenticateInternal(client, opPackageName, callingUid, callingPid, callingUserId);
- }
-
- protected void authenticateInternal(AuthenticationClient client,
- String opPackageName, int callingUid, int callingPid, int callingUserId) {
- if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
- return;
- }
-
- mHandler.post(() -> {
- startAuthentication(client, opPackageName);
- });
- }
-
- protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
- cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId,
- true /* fromClient */);
- }
-
- protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId, boolean fromClient) {
-
- if (DEBUG) Slog.v(getTag(), "cancelAuthentication(" + opPackageName + ")");
- if (fromClient) {
- // Only check this if cancel was called from the client (app). If cancel was called
- // from BiometricService, it means the dialog was dismissed due to user interaction.
- if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) {
- Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
- }
- return;
- }
- }
-
- mHandler.post(() -> {
- ClientMonitor client = mCurrentClient;
- if (client instanceof AuthenticationClient) {
- if (client.getToken() == token || !fromClient) {
- if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString()
- + ", fromClient: " + fromClient);
- // If cancel was from BiometricService, it means the dialog was dismissed
- // and authentication should be canceled.
- client.stop(client.getToken() == token);
- } else {
- if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
- + " since tokens don't match. fromClient: " + fromClient);
- }
- } else if (client != null) {
- if (DEBUG) Slog.v(getTag(), "Can't cancel non-authenticating client "
- + client.getOwnerString());
- }
- });
- }
-
- protected void removeInternal(RemovalClient client) {
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- protected void cleanupInternal(InternalCleanupClient client) {
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- // Should be done on a handler thread - not on the Binder's thread.
- private void startAuthentication(AuthenticationClient client, String opPackageName) {
- if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
-
- @LockoutTracker.LockoutMode int lockoutMode = getLockoutMode(client.getTargetUserId());
- if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
- Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
- int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
- ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
- : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
- client.onError(errorCode, 0 /* vendorCode */);
- return;
- }
- startClient(client, true /* initiatedByClient */);
- }
-
- protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
- if (callback == null) {
- Slog.w(getTag(), "Null LockoutResetCallback");
- return;
- }
- mHandler.post(() -> {
- final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
- if (!mLockoutMonitors.contains(monitor)) {
- mLockoutMonitors.add(monitor);
- }
- });
- }
-
- /**
- * Helper methods.
- */
-
- /**
- * @param opPackageName name of package for caller
- * @param requireForeground only allow this call while app is in the foreground
- * @return true if caller can use the biometric API
- */
- protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
- int pid, int userId) {
- checkUseBiometricPermission();
-
-
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- return true; // System process (BiometricService, etc) is always allowed
- }
- if (isKeyguard(opPackageName)) {
- return true; // Keyguard is always allowed
- }
- if (!isCurrentUserOrProfile(userId)) {
- Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
- return false;
- }
- if (!checkAppOps(uid, opPackageName)) {
- Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
- return false;
- }
-
- if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
- opPackageName))) {
- Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
- return false;
- }
- return true;
- }
-
- /**
- * @param opPackageName package of the caller
- * @return true if this is the same client currently using the biometric
- */
- private boolean isCurrentClient(String opPackageName) {
- return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
- }
-
- /**
- * @return true if this is keyguard package
- */
- public boolean isKeyguard(String clientPackage) {
- return mKeyguardPackage.equals(clientPackage);
- }
-
- private boolean isForegroundActivity(int uid, int pid) {
- try {
- final List<ActivityManager.RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- if (procs == null) {
- Slog.e(getTag(), "Processes null, defaulting to true");
- return true;
- }
-
- int N = procs.size();
- for (int i = 0; i < N; i++) {
- ActivityManager.RunningAppProcessInfo proc = procs.get(i);
- if (proc.pid == pid && proc.uid == uid
- && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
- return true;
- }
- }
- } catch (RemoteException e) {
- Slog.w(getTag(), "am.getRunningAppProcesses() failed");
- }
- return false;
- }
-
- /**
- * Calls the HAL to switch states to the new task. If there's already a current task,
- * it calls cancel() and sets mPendingClient to begin when the current task finishes
- * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
- *
- * @param newClient the new client that wants to connect
- * @param initiatedByClient true for authenticate, remove and enroll
- */
- @VisibleForTesting
- void startClient(ClientMonitor newClient, boolean initiatedByClient) {
- ClientMonitor currentClient = mCurrentClient;
- if (currentClient != null) {
- if (DEBUG) Slog.v(getTag(), "request stop current client " +
- currentClient.getOwnerString());
- // This check only matters for FingerprintService, since enumerate may call back
- // multiple times.
- if (currentClient instanceof InternalCleanupClient) {
- // This condition means we're currently running internal diagnostics to
- // remove extra templates in the hardware and/or the software
- // TODO: design an escape hatch in case client never finishes
- if (newClient != null) {
- Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- }
- } else {
- currentClient.stop(initiatedByClient);
-
- // Only post the reset runnable for non-cleanup clients. Cleanup clients should
- // never be forcibly stopped since they ensure synchronization between HAL and
- // framework. Thus, we should instead just start the pending client once cleanup
- // finishes instead of using the reset runnable.
- mHandler.removeCallbacks(mResetClientState);
- mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
- }
- mPendingClient = newClient;
- } else if (newClient != null) {
- // For BiometricPrompt clients, do not start until
- // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
- // modalities are ready before initiating authentication.
- if (newClient instanceof AuthenticationClient) {
- AuthenticationClient client = (AuthenticationClient) newClient;
- if (client.isBiometricPrompt()) {
- if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
- mCurrentClient = newClient;
- if (mBiometricService == null) {
- mBiometricService = IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE));
- }
- try {
- mBiometricService.onReadyForAuthentication(client.getCookie());
- } catch (RemoteException e) {
- Slog.e(getTag(), "Remote exception", e);
- }
- return;
- }
- }
-
- // We are not a BiometricPrompt client, start the client immediately
- mCurrentClient = newClient;
- startCurrentClient(mCurrentClient.getCookie());
- }
- }
-
- protected void startCurrentClient(int cookie) {
- if (mCurrentClient == null) {
- Slog.e(getTag(), "Trying to start null client!");
- return;
- }
-
- if (DEBUG) Slog.v(getTag(), "starting client "
- + mCurrentClient.getClass().getSimpleName()
- + "(" + mCurrentClient.getOwnerString() + ")"
- + " targetUserId: " + mCurrentClient.getTargetUserId()
- + " currentUserId: " + mCurrentUserId
- + " cookie: " + cookie + "/" + mCurrentClient.getCookie());
-
- if (cookie != mCurrentClient.getCookie()) {
- Slog.e(getTag(), "Mismatched cookie");
- return;
- }
-
- int status = mCurrentClient.start();
- if (status == 0) {
- notifyClientActiveCallbacks(true);
- } else {
- mCurrentClient.onError(BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- removeClient(mCurrentClient);
- }
- }
-
- protected void removeClient(ClientMonitor client) {
- if (client != null) {
- client.destroy();
- if (client != mCurrentClient && mCurrentClient != null) {
- Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
- + mCurrentClient.getOwnerString());
- }
- }
- if (mCurrentClient != null) {
- if (DEBUG) Slog.v(getTag(), "Done with client: "
- + mCurrentClient.getClass().getSimpleName()
- + "(" + mCurrentClient.getOwnerString() + ")");
- mCurrentClient = null;
- }
- if (mPendingClient == null) {
- notifyClientActiveCallbacks(false);
- }
- }
-
- /**
- * Populates existing authenticator ids. To be used only during the start of the service.
- */
- protected void loadAuthenticatorIds() {
- // This operation can be expensive, so keep track of the elapsed time. Might need to move to
- // background if it takes too long.
- long t = System.currentTimeMillis();
- mAuthenticatorIds.clear();
- for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
- int userId = getUserOrWorkProfileId(null, user.id);
- if (!mAuthenticatorIds.containsKey(userId)) {
- updateActiveGroup(userId, null);
- }
- }
-
- t = System.currentTimeMillis() - t;
- if (t > 1000) {
- Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
- }
- }
-
- /**
- * @param clientPackage the package of the caller
- * @return the profile id
- */
- protected int getUserOrWorkProfileId(String clientPackage, int userId) {
- if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
- return userId;
- }
- return getEffectiveUserId(userId);
- }
-
- protected boolean isRestricted() {
- // Only give privileged apps (like Settings) access to biometric info
- final boolean restricted = !hasPermission(getManageBiometricPermission());
- return restricted;
- }
-
- protected boolean hasPermission(String permission) {
- return getContext().checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- protected void checkPermission(String permission) {
- getContext().enforceCallingOrSelfPermission(permission,
- "Must have " + permission + " permission.");
- }
-
- protected boolean isCurrentUserOrProfile(int userId) {
- UserManager um = UserManager.get(mContext);
- if (um == null) {
- Slog.e(getTag(), "Unable to acquire UserManager");
- return false;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- // Allow current user or profiles of the current user...
- for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
- if (profileId == userId) {
- return true;
- }
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- return false;
- }
-
- /***
- * @return authenticator id for the calling user
- */
- protected long getAuthenticatorId(int callingUserId) {
- final int userId = getUserOrWorkProfileId(null /* clientPackage */, callingUserId);
- return mAuthenticatorIds.getOrDefault(userId, 0L);
- }
-
- /**
- * This method should be called upon connection to the daemon, and when user switches.
- * @param userId
- */
- protected void doTemplateCleanupForUser(int userId) {
- if (CLEANUP_UNKNOWN_TEMPLATES) {
- if (DEBUG) Slog.v(getTag(), "Cleaning up templates for user(" + userId + ")");
-
- final boolean restricted = !hasPermission(getManageBiometricPermission());
- final List<? extends BiometricAuthenticator.Identifier> enrolledList =
- getEnrolledTemplates(userId);
-
- InternalCleanupClient client = new InternalCleanupClient(getContext(),
- getDaemonWrapper(), null /* serviceListener */, userId, userId,
- restricted, getContext().getOpPackageName(), getSensorId(), statsModality(),
- enrolledList, getBiometricUtils());
- cleanupInternal(client);
- }
- }
-
- /**
- * This method is called when the user switches. Implementations should probably notify the
- * HAL.
- */
- protected void handleUserSwitching(int userId) {
- if (getCurrentClient() instanceof InternalCleanupClient) {
- Slog.w(getTag(), "User switched while performing cleanup");
- }
- updateActiveGroup(userId, null);
- doTemplateCleanupForUser(userId);
- }
-
- protected void notifyLockoutResetMonitors() {
- for (int i = 0; i < mLockoutMonitors.size(); i++) {
- mLockoutMonitors.get(i).sendLockoutReset();
- }
- }
-
- /**
- * @param userId
- * @return true if this is a work profile
- */
- private boolean isWorkProfile(int userId) {
- UserInfo userInfo = null;
- final long token = Binder.clearCallingIdentity();
- try {
- userInfo = mUserManager.getUserInfo(userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return userInfo != null && userInfo.isManagedProfile();
- }
-
-
- private int getEffectiveUserId(int userId) {
- UserManager um = UserManager.get(mContext);
- if (um != null) {
- final long callingIdentity = Binder.clearCallingIdentity();
- userId = um.getCredentialOwnerProfile(userId);
- Binder.restoreCallingIdentity(callingIdentity);
- } else {
- Slog.e(getTag(), "Unable to acquire UserManager");
- }
- return userId;
- }
-
-
- private void listenForUserSwitches() {
- try {
- ActivityManager.getService().registerUserSwitchObserver(
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) throws RemoteException {
- mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
- .sendToTarget();
- }
- }, getTag());
- } catch (RemoteException e) {
- Slog.w(getTag(), "Failed to listen for user switching event" ,e);
- }
- }
-
- private void removeLockoutResetCallback(
- LockoutResetMonitor monitor) {
- mLockoutMonitors.remove(monitor);
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 0f6689a15463..8b27781940ac 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -16,8 +16,10 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -29,54 +31,80 @@ import java.util.NoSuchElementException;
* the current client. Subclasses are responsible for coordinating the interaction with
* the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
*/
-public abstract class ClientMonitor extends LoggableMonitor implements IBinder.DeathRecipient {
+public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinder.DeathRecipient {
private static final String TAG = "Biometrics/ClientMonitor";
+ protected static final boolean DEBUG = true;
- static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
- protected static final boolean DEBUG = BiometricServiceBase.DEBUG;
+ // Counter used to distinguish between ClientMonitor instances to help debugging.
+ private static int sCount = 0;
- private final Context mContext;
+ /**
+ * Interface that ClientMonitor holders should use to receive callbacks.
+ */
+ public interface FinishCallback {
+ /**
+ * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
+ * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
+ * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
+ * implementation.
+ *
+ * @param clientMonitor Reference of the ClientMonitor that finished.
+ * @param success True if the operation completed successfully.
+ */
+ void onClientFinished(ClientMonitor<?> clientMonitor, boolean success);
+ }
+
+ /**
+ * Interface that allows ClientMonitor subclasses to retrieve a fresh instance to the HAL.
+ */
+ public interface LazyDaemon<T> {
+ /**
+ * @return A fresh instance to the biometric HAL
+ */
+ T getDaemon();
+ }
+
+ private final int mSequentialId;
+ @NonNull private final Context mContext;
+ @NonNull protected final LazyDaemon<T> mLazyDaemon;
private final int mTargetUserId;
- private final int mGroupId;
- // True if client does not have MANAGE_FINGERPRINT permission
- private final boolean mIsRestricted;
- private final String mOwner;
- private final BiometricServiceBase.DaemonWrapper mDaemon;
+ @NonNull private final String mOwner;
private final int mSensorId; // sensorId as configured by the framework
- private IBinder mToken;
- private ClientMonitorCallbackConverter mListener;
+ @Nullable private IBinder mToken;
+ @Nullable private ClientMonitorCallbackConverter mListener;
// Currently only used for authentication client. The cookie generated by BiometricService
// is never 0.
private final int mCookie;
-
- boolean mAlreadyCancelled;
boolean mAlreadyDone;
+ @NonNull protected FinishCallback mFinishCallback;
+
/**
- * @param context context of BiometricService
- * @param daemon interface to call back to a specific biometric's daemon
- * @param token a unique token for the client
- * @param listener recipient of related events (e.g. authentication)
- * @param userId target user id for operation
- * @param groupId groupId for the fingerprint set
- * @param restricted whether or not client has the MANAGE_* permission
- * permission
- * @param owner name of the client that owns this
+ * @param context system_server context
+ * @param lazyDaemon pointer for lazy retrieval of the HAL
+ * @param token a unique token for the client
+ * @param listener recipient of related events (e.g. authentication)
+ * @param userId target user id for operation
+ * @param owner name of the client that owns this
+ * @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon)
+ * @param sensorId ID of the sensor that the operation should be requested of
+ * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants
+ * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants
+ * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants
*/
- public ClientMonitor(Context context, BiometricServiceBase.DaemonWrapper daemon, IBinder token,
- ClientMonitorCallbackConverter listener, int userId, int groupId, boolean restricted,
- String owner, int cookie, int sensorId, int statsModality, int statsAction,
+ public ClientMonitor(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
int statsClient) {
super(statsModality, statsAction, statsClient);
+ mSequentialId = sCount++;
mContext = context;
- mDaemon = daemon;
+ mLazyDaemon = lazyDaemon;
mToken = token;
mListener = listener;
mTargetUserId = userId;
- mGroupId = groupId;
- mIsRestricted = restricted;
mOwner = owner;
mCookie = cookie;
mSensorId = sensorId;
@@ -95,36 +123,28 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
}
/**
- * Contacts the biometric's HAL to start the client.
- * @return 0 on success, errno from driver on failure
+ * Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null).
+ * If such a problem is detected, the scheduler will not invoke
+ * {@link #start(FinishCallback)}.
*/
- public abstract int start();
+ public abstract void unableToStart();
/**
- * Contacts the biometric's HAL to stop the client.
- * @param initiatedByClient whether the operation is at the request of a client
+ * Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book
+ * keeping is complete.
+ * @param finishCallback invoked when the operation is complete (succeeds, fails, etc)
*/
- public abstract int stop(boolean initiatedByClient);
-
- public boolean isAlreadyDone() {
- return mAlreadyDone;
+ public void start(@NonNull FinishCallback finishCallback) {
+ mFinishCallback = finishCallback;
}
/**
- * Called when we get notification from the biometric's HAL that an error has occurred with the
- * current operation. Common to authenticate, enroll, enumerate and remove.
- * @param error
- * @return true if client should be removed
+ * Starts the HAL operation specific to the ClientMonitor subclass.
*/
- public void onError(int error, int vendorCode) {
- super.logOnError(mContext, error, vendorCode, getTargetUserId());
- try {
- if (mListener != null) {
- mListener.onError(getSensorId(), getCookie(), error, vendorCode);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to invoke sendError", e);
- }
+ protected abstract void startHalOperation();
+
+ public boolean isAlreadyDone() {
+ return mAlreadyDone;
}
public void destroy() {
@@ -145,29 +165,19 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
binderDiedInternal(true /* clearListener */);
}
+ // TODO(b/157790417): Move this to the scheduler
void binderDiedInternal(boolean clearListener) {
// If the current client dies we should cancel the current operation.
- Slog.e(TAG, "Binder died, cancelling client");
- stop(false /* initiatedByClient */);
+ if (this instanceof Interruptable) {
+ Slog.e(TAG, "Binder died, cancelling client");
+ ((Interruptable) this).cancel();
+ }
mToken = null;
if (clearListener) {
mListener = null;
}
}
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mToken != null) {
- if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
- onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
- }
- } finally {
- super.finalize();
- }
- }
-
public final Context getContext() {
return mContext;
}
@@ -180,22 +190,10 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
return mListener;
}
- public final BiometricServiceBase.DaemonWrapper getDaemonWrapper() {
- return mDaemon;
- }
-
- public final boolean getIsRestricted() {
- return mIsRestricted;
- }
-
public final int getTargetUserId() {
return mTargetUserId;
}
- public final int getGroupId() {
- return mGroupId;
- }
-
public final IBinder getToken() {
return mToken;
}
@@ -203,4 +201,16 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
public final int getSensorId() {
return mSensorId;
}
+
+ public final T getFreshDaemon() {
+ return mLazyDaemon.getDaemon();
+ }
+
+ @Override
+ public String toString() {
+ return "{[" + mSequentialId + "] "
+ + this.getClass().getSimpleName()
+ + ", " + getOwnerString()
+ + ", " + getCookie() + "}";
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index 342fc7e54434..3cef6667b644 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -101,6 +101,15 @@ public final class ClientMonitorCallbackConverter {
// The following only apply to IFingerprintServiceReceiver and IFaceServiceReceiver
+ public void onDetected(int sensorId, int userId, boolean isStrongBiometric)
+ throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onFaceDetected(sensorId, userId, isStrongBiometric);
+ } else if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onFingerprintDetected(sensorId, userId, isStrongBiometric);
+ }
+ }
+
void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
throws RemoteException {
if (mFaceServiceReceiver != null) {
@@ -119,12 +128,24 @@ public final class ClientMonitorCallbackConverter {
}
}
- // The following are only used internally within system_server - specifically, within
- // BiometricServiceBase and their <Biometric>Service implementations.
+ public void onChallengeGenerated(long challenge) throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onChallengeGenerated(challenge);
+ } else if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onChallengeGenerated(challenge);
+ }
+ }
+
+ public void onFeatureSet(boolean success, int feature) throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onFeatureSet(success, feature);
+ }
+ }
- void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
+ public void onFeatureGet(boolean success, int feature, boolean value)
throws RemoteException {
- // Currently unused, BiometricServiceBase#handleEnumerate everything internally without
- // needing to propagate this to any receiver.
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onFeatureGet(success, feature, value);
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 41235520ea16..add5829c1342 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -16,122 +16,84 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.Surface;
-import java.util.ArrayList;
import java.util.Arrays;
/**
* A class to keep track of the enrollment state for a given client.
*/
-public class EnrollClient extends AcquisitionClient {
+public abstract class EnrollClient<T> extends AcquisitionClient<T> {
private static final String TAG = "Biometrics/EnrollClient";
- private final byte[] mCryptoToken;
- private final BiometricUtils mBiometricUtils;
- private final int[] mDisabledFeatures;
- private final int mTimeoutSec;
- private final Surface mSurface;
+ protected final byte[] mHardwareAuthToken;
+ protected final int mTimeoutSec;
+ protected final BiometricUtils mBiometricUtils;
private final boolean mShouldVibrate;
private long mEnrollmentStartTimeMs;
- public EnrollClient(Context context, BiometricServiceBase.DaemonWrapper daemon, IBinder token,
- ClientMonitorCallbackConverter listener, int userId, int groupId, byte[] cryptoToken,
- boolean restricted, String owner, BiometricUtils utils, final int[] disabledFeatures,
- int timeoutSec, int statsModality, Surface surface, int sensorId,
+ /**
+ * @return true if the user has already enrolled the maximum number of templates.
+ */
+ protected abstract boolean hasReachedEnrollmentLimit();
+
+ public EnrollClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
+ int timeoutSec, int statsModality, int sensorId,
boolean shouldVibrate) {
- super(context, daemon, token, listener, userId, groupId, restricted,
- owner, 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENROLL,
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ statsModality, BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
- mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
- mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
+ mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
mTimeoutSec = timeoutSec;
- mSurface = surface;
mShouldVibrate = shouldVibrate;
}
- public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
- int remaining) {
- if (remaining == 0) {
- mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
- logOnEnrolled(getTargetUserId(),
- System.currentTimeMillis() - mEnrollmentStartTimeMs,
- true /* enrollSuccessful */);
- }
- notifyUserActivity();
- return sendEnrollResult(identifier, remaining);
- }
-
- /*
- * @return true if we're done.
- */
- private boolean sendEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
+ public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
if (mShouldVibrate) {
vibrateSuccess();
}
+ final ClientMonitorCallbackConverter listener = getListener();
try {
- final ClientMonitorCallbackConverter listener = getListener();
if (listener != null) {
listener.onEnrollResult(identifier, remaining);
}
- return remaining == 0;
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify EnrollResult:", e);
- return true;
+ Slog.e(TAG, "Remote exception", e);
}
- }
- @Override
- public int start() {
- mEnrollmentStartTimeMs = System.currentTimeMillis();
- try {
- final ArrayList<Integer> disabledFeatures = new ArrayList<>();
- for (int i = 0; i < mDisabledFeatures.length; i++) {
- disabledFeatures.add(mDisabledFeatures[i]);
- }
-
- final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), mTimeoutSec,
- disabledFeatures, mSurface);
- if (result != 0) {
- Slog.w(TAG, "startEnroll failed, result=" + result);
- onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "startEnroll failed", e);
+ if (remaining == 0) {
+ mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
+ logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
+ true /* enrollSuccessful */);
+ mFinishCallback.onClientFinished(this, true /* success */);
}
- return 0; // success
+ notifyUserActivity();
}
@Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopEnroll: already cancelled!");
- return 0;
- }
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
- try {
- final int result = getDaemonWrapper().cancel();
- if (result != 0) {
- Slog.w(TAG, "startEnrollCancel failed, result = " + result);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "stopEnrollment failed", e);
+ if (hasReachedEnrollmentLimit()) {
+ Slog.e(TAG, "Reached enrollment limit");
+ finishCallback.onClientFinished(this, false /* success */);
+ return;
}
- mAlreadyCancelled = true;
- return 0;
+
+ mEnrollmentStartTimeMs = System.currentTimeMillis();
+ startHalOperation();
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/EnumerateClient.java
deleted file mode 100644
index 03b5fd564b90..000000000000
--- a/services/core/java/com/android/server/biometrics/sensors/EnumerateClient.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.biometrics.sensors;
-
-import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-/**
- * A class to keep track of the enumeration state for a given client.
- */
-public abstract class EnumerateClient extends ClientMonitor implements EnumerateConsumer {
-
- private static final String TAG = "Biometrics/EnumerateClient";
-
- public EnumerateClient(Context context, BiometricServiceBase.DaemonWrapper daemon,
- IBinder token, ClientMonitorCallbackConverter listener, int groupId, int userId,
- boolean restricted, String owner, int sensorId, int statsModality) {
- super(context, daemon, token, listener, userId, groupId, restricted,
- owner, 0 /* cookie */, sensorId, statsModality,
- BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
- }
-
- @Override
- public int start() {
- // The biometric template ids will be removed when we get confirmation from the HAL
- try {
- final int result = getDaemonWrapper().enumerate();
- if (result != 0) {
- Slog.w(TAG, "start enumerate for user " + getTargetUserId()
- + " failed, result=" + result);
- onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "startEnumeration failed", e);
- }
- return 0;
- }
-
- @Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopEnumerate: already cancelled!");
- return 0;
- }
-
- try {
- final int result = getDaemonWrapper().cancel();
- if (result != 0) {
- Slog.w(TAG, "stop enumeration failed, result=" + result);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "stopEnumeration failed", e);
- return ERROR_ESRCH;
- }
-
- // We don't actually stop enumerate, but inform the client that the cancel operation
- // succeeded so we can start the next operation.
- if (initiatedByClient) {
- onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, 0 /* vendorCode */);
- }
- mAlreadyCancelled = true;
- return 0; // success
- }
-
- @Override
- public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
- int remaining) {
- try {
- if (getListener() != null) {
- getListener().onEnumerated(identifier, remaining);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify enumerated:", e);
- }
- return remaining == 0;
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnumerateConsumer.java b/services/core/java/com/android/server/biometrics/sensors/EnumerateConsumer.java
index cbef0500c6ab..8ad9e6ad2ed2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnumerateConsumer.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnumerateConsumer.java
@@ -27,7 +27,6 @@ public interface EnumerateConsumer {
* @param identifier Fingerprint, face, etc template that exists in the HAL.
* @param remaining number of templates that exist but have not been reported to the
* framework yet.
- * @return true if enumerate is completed (remaining == 0)
*/
- boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier, int remaining);
+ void onEnumerationResult(BiometricAuthenticator.Identifier identifier, int remaining);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
new file mode 100644
index 000000000000..dad5cad1ed8d
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> {
+
+ private static final String TAG = "GenerateChallengeClient";
+
+ protected long mChallenge;
+
+ public GenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId,
+ BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
+ }
+
+ @Override
+ public void unableToStart() {
+ try {
+ getListener().onChallengeGenerated(0L);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
+ startHalOperation();
+ try {
+ getListener().onChallengeGenerated(mChallenge);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index b8d2401512a0..6d7b0fd3d5f1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -16,15 +16,18 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.os.IBinder;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Wraps {@link InternalEnumerateClient} and {@link RemovalClient}. Keeps track of all the
@@ -35,8 +38,8 @@ import java.util.List;
* 2) The HAL and Framework are not in sync, and
* {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/
*/
-public class InternalCleanupClient extends ClientMonitor implements EnumerateConsumer,
- RemovalConsumer {
+public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Identifier, T>
+ extends ClientMonitor<T> implements EnumerateConsumer, RemovalConsumer {
private static final String TAG = "Biometrics/InternalCleanupClient";
@@ -55,89 +58,106 @@ public class InternalCleanupClient extends ClientMonitor implements EnumerateCon
private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
private final BiometricUtils mBiometricUtils;
- private ClientMonitor mCurrentTask;
-
- InternalCleanupClient(Context context, BiometricServiceBase.DaemonWrapper daemon,
- ClientMonitorCallbackConverter listener, int userId, int groupId, boolean restricted,
- String owner, int sensorId, int statsModality,
- List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils) {
- super(context, daemon, null /* token */, listener, userId, groupId, restricted,
- owner, 0 /* cookie */, sensorId, statsModality,
- BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ private final Map<Integer, Long> mAuthenticatorIds;
+ private final List<S> mEnrolledList;
+ private ClientMonitor<T> mCurrentTask;
+
+ private final FinishCallback mEnumerateFinishCallback = (clientMonitor, success) -> {
+ final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
+ ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
+
+ if (!unknownHALTemplates.isEmpty()) {
+ Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion");
+ }
+ for (BiometricAuthenticator.Identifier unknownHALTemplate : unknownHALTemplates) {
+ mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplate,
+ mCurrentTask.getTargetUserId()));
+ }
+ if (mUnknownHALTemplates.isEmpty()) {
+ // No unknown HAL templates. Unknown framework templates are already cleaned up in
+ // InternalEnumerateClient. Finish this client.
+ mFinishCallback.onClientFinished(this, success);
+ } else {
+ startCleanupUnknownHalTemplates();
+ }
+ };
+
+ private final FinishCallback mRemoveFinishCallback = (clientMonitor, success) -> {
+ mFinishCallback.onClientFinished(this, success);
+ };
+
+ protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
+ LazyDaemon<T> lazyDaemon, IBinder token, int userId, String owner,
+ List<S> enrolledList, BiometricUtils utils, int sensorId);
+
+ protected abstract RemovalClient<T> getRemovalClient(Context context, LazyDaemon<T> lazyDaemon,
+ IBinder token, int biometricId, int userId, String owner, BiometricUtils utils,
+ int sensorId, Map<Integer, Long> authenticatorIds);
+
+ protected InternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ int userId, @NonNull String owner, int sensorId, int statsModality,
+ @NonNull List<S> enrolledList, @NonNull BiometricUtils utils,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
+ userId, owner, 0 /* cookie */, sensorId, statsModality,
+ BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
- mCurrentTask = new InternalEnumerateClient(context, daemon, getToken(),
- listener, userId, groupId, restricted, owner, enrolledList, utils, sensorId,
- statsModality);
+ mAuthenticatorIds = authenticatorIds;
+ mEnrolledList = enrolledList;
}
private void startCleanupUnknownHalTemplates() {
UserTemplate template = mUnknownHALTemplates.get(0);
mUnknownHALTemplates.remove(template);
- mCurrentTask = new RemovalClient(getContext(),
- getDaemonWrapper(), getToken(), null /* listener */,
- template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
- getIsRestricted(), getContext().getPackageName(), mBiometricUtils,
- getSensorId(), mStatsModality);
+ mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
+ template.mIdentifier.getBiometricId(), template.mUserId,
+ getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds);
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
mStatsModality,
BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
- mCurrentTask.start();
+ mCurrentTask.start(mRemoveFinishCallback);
+ }
+
+ @Override
+ public void unableToStart() {
+ // nothing to do here
}
@Override
- public int start() {
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
// Start enumeration. Removal will start if necessary, when enumeration is completed.
- return mCurrentTask.start();
+ mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
+ getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
+ mCurrentTask.start(mEnumerateFinishCallback);
}
@Override
- public int stop(boolean initiatedByClient) {
- return 0;
+ protected void startHalOperation() {
+ // Internal cleanup's start method does not require a HAL operation, but rather
+ // relies on its subtask's ClientMonitor to start the proper HAL operation.
}
@Override
- public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+ public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
if (!(mCurrentTask instanceof RemovalClient)) {
Slog.e(TAG, "onRemoved received during client: "
+ mCurrentTask.getClass().getSimpleName());
- return false;
+ return;
}
- return ((RemovalClient) mCurrentTask).onRemoved(identifier, remaining);
+ ((RemovalClient) mCurrentTask).onRemoved(identifier, remaining);
}
@Override
- public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+ public void onEnumerationResult(BiometricAuthenticator.Identifier identifier,
int remaining) {
if (!(mCurrentTask instanceof InternalEnumerateClient)) {
Slog.e(TAG, "onEnumerationResult received during client: "
+ mCurrentTask.getClass().getSimpleName());
- return false;
+ return;
}
-
((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining);
-
- if (remaining != 0) {
- return false;
- }
-
- final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
- ((InternalEnumerateClient) mCurrentTask).getUnknownHALTemplates();
-
- if (!unknownHALTemplates.isEmpty()) {
- Slog.w(TAG, "Adding " + unknownHALTemplates.size()
- + " templates for deletion");
- }
- for (int i = 0; i < unknownHALTemplates.size(); i++) {
- mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i),
- mCurrentTask.getTargetUserId()));
- }
-
- if (mUnknownHALTemplates.isEmpty()) {
- return true;
- } else {
- startCleanupUnknownHalTemplates();
- return false;
- }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 6642beb443f8..3f73cd56e6c3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -16,6 +16,7 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
@@ -30,7 +31,8 @@ import java.util.List;
/**
* Internal class to help clean up unknown templates in the HAL and Framework
*/
-public class InternalEnumerateClient extends EnumerateClient {
+public abstract class InternalEnumerateClient<T> extends ClientMonitor<T>
+ implements EnumerateConsumer {
private static final String TAG = "Biometrics/InternalEnumerateClient";
@@ -41,17 +43,42 @@ public class InternalEnumerateClient extends EnumerateClient {
// List of templates to remove from the HAL
private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
- InternalEnumerateClient(Context context, BiometricServiceBase.DaemonWrapper daemon,
- IBinder token, ClientMonitorCallbackConverter listener, int groupId, int userId,
- boolean restricted, String owner,
- List<? extends BiometricAuthenticator.Identifier> enrolledList,
- BiometricUtils utils, int sensorId, int statsModality) {
- super(context, daemon, token, listener, groupId, userId, restricted, owner, sensorId,
- statsModality);
+ protected InternalEnumerateClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, int userId, @NonNull String owner,
+ @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
+ @NonNull BiometricUtils utils, int sensorId, int statsModality) {
+ // Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove)
+ // is all done internally.
+ super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
+ 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
mEnrolledList = enrolledList;
mUtils = utils;
}
+ @Override
+ public void onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ handleEnumeratedTemplate(identifier);
+ if (remaining == 0) {
+ doTemplateCleanup();
+ mFinishCallback.onClientFinished(this, true /* success */);
+ }
+ }
+
+ @Override
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
+ // The biometric template ids will be removed when we get confirmation from the HAL
+ startHalOperation();
+ }
+
private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
if (identifier == null) {
return;
@@ -83,8 +110,7 @@ public class InternalEnumerateClient extends EnumerateClient {
for (int i = 0; i < mEnrolledList.size(); i++) {
BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i);
Slog.e(TAG, "doTemplateCleanup(): Removing dangling template from framework: "
- + identifier.getBiometricId() + " "
- + identifier.getName());
+ + identifier.getBiometricId() + " " + identifier.getName());
mUtils.removeBiometricForUser(getContext(),
getTargetUserId(), identifier.getBiometricId());
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
@@ -97,14 +123,4 @@ public class InternalEnumerateClient extends EnumerateClient {
public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
return mUnknownHALTemplates;
}
-
- @Override
- public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
- int remaining) {
- handleEnumeratedTemplate(identifier);
- if (remaining == 0) {
- doTemplateCleanup();
- }
- return remaining == 0;
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
new file mode 100644
index 000000000000..35d917747574
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+
+/**
+ * Interface that {@link ClientMonitor} subclasses eligible/interested in error callbacks should
+ * implement.
+ */
+public interface Interruptable {
+ /**
+ * Requests to end the ClientMonitor's lifecycle.
+ */
+ void cancel();
+
+ /**
+ * Notifies the client of errors from the HAL.
+ * @param errorCode defined by the HIDL interface
+ * @param vendorCode defined by the vendor
+ */
+ void onError(int errorCode, int vendorCode);
+
+ /**
+ * Notifies the client that it needs to finish before
+ * {@link ClientMonitor#start(ClientMonitor.FinishCallback)} was invoked. This usually happens
+ * if the client is still waiting in the pending queue and got notified that a subsequent
+ * operation is preempting it.
+ * @param finishCallback invoked when the operation is completed.
+ */
+ void cancelWithoutStarting(@NonNull ClientMonitor.FinishCallback finishCallback);
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
new file mode 100644
index 000000000000..f4997d4abe24
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.content.Context;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * Allows clients (such as keyguard) to register for notifications on when biometric lockout
+ * ends. This class keeps track of all client callbacks. Individual sensors should notify this
+ * when lockout for a specific sensor has been reset.
+ */
+public class LockoutResetDispatcher implements IBinder.DeathRecipient {
+
+ private static final String TAG = "LockoutResetTracker";
+
+ private final Context mContext;
+ private final ArrayList<ClientCallback> mClientCallbacks;
+
+ private static class ClientCallback {
+ private static final long WAKELOCK_TIMEOUT_MS = 2000;
+
+ private final String mOpPackageName;
+ private final IBiometricServiceLockoutResetCallback mCallback;
+ private final PowerManager.WakeLock mWakeLock;
+
+ ClientCallback(Context context, IBiometricServiceLockoutResetCallback callback,
+ String opPackageName) {
+ final PowerManager pm = context.getSystemService(PowerManager.class);
+ mOpPackageName = opPackageName;
+ mCallback = callback;
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "LockoutResetMonitor:SendLockoutReset");
+ }
+
+ void sendLockoutReset(int sensorId) {
+ if (mCallback != null) {
+ try {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+ mCallback.onLockoutReset(sensorId, new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) {
+ releaseWakelock();
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
+ releaseWakelock();
+ }
+ }
+ }
+
+ private void releaseWakelock() {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ }
+
+ public LockoutResetDispatcher(Context context) {
+ mContext = context;
+ mClientCallbacks = new ArrayList<>();
+ }
+
+ public void addCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName) {
+ if (callback == null) {
+ Slog.w(TAG, "Callback from : " + opPackageName + " is null");
+ return;
+ }
+
+ mClientCallbacks.add(new ClientCallback(mContext, callback, opPackageName));
+ try {
+ callback.asBinder().linkToDeath(this, 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death", e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // Do nothing, handled below
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
+ Slog.e(TAG, "Callback binder died: " + who);
+ for (ClientCallback callback : mClientCallbacks) {
+ if (callback.mCallback.asBinder().equals(who)) {
+ Slog.e(TAG, "Removing dead callback for: " + callback.mOpPackageName);
+ callback.releaseWakelock();
+ mClientCallbacks.remove(callback);
+ }
+ }
+ }
+
+ public void notifyLockoutResetCallbacks(int sensorId) {
+ for (ClientCallback callback : mClientCallbacks) {
+ callback.sendLockoutReset(sensorId);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
index a7c63f75d707..1a4216f9d43c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
@@ -39,10 +39,6 @@ public abstract class LoggableMonitor {
private final int mStatsClient;
private long mFirstAcquireTimeMs;
- protected long getFirstAcquireTimeMs() {
- return mFirstAcquireTimeMs;
- }
-
/**
* Only valid for AuthenticationClient.
* @return true if the client is authenticating for a crypto operation.
@@ -62,6 +58,12 @@ public abstract class LoggableMonitor {
mStatsClient = statsClient;
}
+ private boolean isAnyFieldUnknown() {
+ return mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN
+ || mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN
+ || mStatsClient == BiometricsProtoEnums.CLIENT_UNKNOWN;
+ }
+
protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
int targetUserId) {
@@ -86,6 +88,11 @@ public abstract class LoggableMonitor {
+ ", AcquiredInfo: " + acquiredInfo
+ ", VendorCode: " + vendorCode);
}
+
+ if (isAnyFieldUnknown()) {
+ return;
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
mStatsModality,
targetUserId,
@@ -114,6 +121,11 @@ public abstract class LoggableMonitor {
} else {
Slog.v(TAG, "Error latency: " + latency);
}
+
+ if (isAnyFieldUnknown()) {
+ return;
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
mStatsModality,
targetUserId,
@@ -157,6 +169,10 @@ public abstract class LoggableMonitor {
Slog.v(TAG, "Authentication latency: " + latency);
}
+ if (isAnyFieldUnknown()) {
+ return;
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
mStatsModality,
targetUserId,
@@ -179,6 +195,10 @@ public abstract class LoggableMonitor {
Slog.v(TAG, "Enroll latency: " + latency);
}
+ if (isAnyFieldUnknown()) {
+ return;
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
mStatsModality,
targetUserId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java
index fa490eef5d21..42b22b0fb519 100644
--- a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java
+++ b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java
@@ -65,7 +65,7 @@ public class PerformanceTracker {
}
}
- void incrementAuthForUser(int userId, boolean accepted) {
+ public void incrementAuthForUser(int userId, boolean accepted) {
createUserEntryIfNecessary(userId);
if (accepted) {
@@ -107,7 +107,7 @@ public class PerformanceTracker {
mAllUsersInfo.get(userId).mPermanentLockout++;
}
- void incrementHALDeathCount() {
+ public void incrementHALDeathCount() {
mHALDeathCount++;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 339b131ec7e7..1c49bcdbadf4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -16,79 +16,59 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import java.util.Map;
+
/**
* A class to keep track of the remove state for a given client.
*/
-public class RemovalClient extends ClientMonitor implements RemovalConsumer {
+public abstract class RemovalClient<T> extends ClientMonitor<T> implements RemovalConsumer {
private static final String TAG = "Biometrics/RemovalClient";
- private final int mBiometricId;
+ protected final int mBiometricId;
private final BiometricUtils mBiometricUtils;
+ private final Map<Integer, Long> mAuthenticatorIds;
- public RemovalClient(Context context, BiometricServiceBase.DaemonWrapper daemon, IBinder token,
- ClientMonitorCallbackConverter listener, int biometricId, int groupId, int userId,
- boolean restricted, String owner, BiometricUtils utils, int sensorId,
- int statsModality) {
- super(context, daemon, token, listener, userId, groupId, restricted, owner, 0 /* cookie */,
- sensorId, statsModality, BiometricsProtoEnums.ACTION_REMOVE,
+ public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+ int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ statsModality, BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricId = biometricId;
mBiometricUtils = utils;
+ mAuthenticatorIds = authenticatorIds;
}
@Override
- public int start() {
- // The biometric template ids will be removed when we get confirmation from the HAL
- try {
- final int result = getDaemonWrapper().remove(getGroupId(), mBiometricId);
- if (result != 0) {
- Slog.w(TAG, "startRemove with id = " + mBiometricId + " failed, result=" +
- result);
- onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "startRemove failed", e);
- }
- return 0;
+ public void unableToStart() {
+ // Nothing to do here
}
@Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopRemove: already cancelled!");
- return 0;
- }
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
- try {
- final int result = getDaemonWrapper().cancel();
- if (result != 0) {
- Slog.w(TAG, "stopRemoval failed, result=" + result);
- return result;
- }
- if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer removing");
- } catch (RemoteException e) {
- Slog.e(TAG, "stopRemoval failed", e);
- return ERROR_ESRCH;
- }
- mAlreadyCancelled = true;
- return 0; // success
+ // The biometric template ids will be removed when we get confirmation from the HAL
+ startHalOperation();
}
- /*
- * @return true if we're done.
- */
- private boolean sendRemoved(BiometricAuthenticator.Identifier identifier,
- int remaining) {
+ @Override
+ public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+ if (identifier.getBiometricId() != 0) {
+ mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
+ identifier.getBiometricId());
+ }
+
try {
if (getListener() != null) {
getListener().onRemoved(identifier, remaining);
@@ -96,15 +76,16 @@ public class RemovalClient extends ClientMonitor implements RemovalConsumer {
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Removed:", e);
}
- return remaining == 0;
- }
- @Override
- public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
- if (identifier.getBiometricId() != 0) {
- mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
- identifier.getBiometricId());
+ if (remaining == 0) {
+ if (mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty()) {
+ Slog.d(TAG, "Last biometric removed for user: " + getTargetUserId());
+ // When the last biometric of a group is removed, update the authenticator id.
+ // Note that multiple ClientMonitors may be cause onRemoved (e.g. internal
+ // cleanup).
+ mAuthenticatorIds.put(getTargetUserId(), 0L);
+ }
+ mFinishCallback.onClientFinished(this, true /* success */);
}
- return sendRemoved(identifier, remaining);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
index 0c8a692f0977..3d7988cf138f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
@@ -27,7 +27,6 @@ public interface RemovalConsumer {
* @param identifier Fingerprint, face, etc that was removed.
* @param remaining number of templates that still need to be removed before the operation in
* the HAL is complete (e.g. when removing all templates).
- * @return true if removal is completed (remaining == 0)
*/
- boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining);
+ void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
new file mode 100644
index 000000000000..b78ee49826f2
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.os.IBinder;
+
+public abstract class RevokeChallengeClient<T> extends ClientMonitor<T> {
+
+ public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, null /* listener */, 0 /* userId */, owner,
+ 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ }
+
+ @Override
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
+ startHalOperation();
+ mFinishCallback.onClientFinished(this, true /* success */);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
new file mode 100644
index 000000000000..6c57208c1e84
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.NotificationManager;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.UserSwitchObserver;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+import android.hardware.face.Face;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.Looper;
+import android.os.NativeHandle;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.EnumerateConsumer;
+import com.android.server.biometrics.sensors.Interruptable;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.PerformanceTracker;
+import com.android.server.biometrics.sensors.RemovalConsumer;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUpdateActiveUserClient;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Supports a single instance of the {@link android.hardware.biometrics.face.V1_0} or
+ * its extended minor versions.
+ */
+class Face10 implements IHwBinder.DeathRecipient {
+
+ private static final String TAG = "Face10";
+ private static final int ENROLL_TIMEOUT_SEC = 75;
+ static final String NOTIFICATION_TAG = "FaceService";
+ static final int NOTIFICATION_ID = 1;
+
+ @NonNull private final FaceSensorProperties mFaceSensorProperties;
+ @NonNull private final Context mContext;
+ @NonNull private final BiometricScheduler mScheduler;
+ @NonNull private final Handler mHandler;
+ @NonNull private final ClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon;
+ @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull private final LockoutHalImpl mLockoutTracker;
+ @NonNull private final UsageStats mUsageStats;
+ @NonNull private NotificationManager mNotificationManager;
+ private final int mSensorId;
+ @NonNull private final Map<Integer, Long> mAuthenticatorIds;
+
+ @Nullable private IBiometricsFace mDaemon;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+
+ private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(newUserId);
+ }
+ };
+
+ private final IBiometricsFaceClientCallback mDaemonCallback =
+ new IBiometricsFaceClientCallback.Stub() {
+ @Override
+ public void onEnrollResult(long deviceId, int faceId, int userId, int remaining) {
+ mHandler.post(() -> {
+ final CharSequence name = FaceUtils.getInstance()
+ .getUniqueName(mContext, userId);
+ final Face face = new Face(name, faceId, deviceId);
+
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FaceEnrollClient)) {
+ Slog.e(TAG, "onEnrollResult for non-enroll client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FaceEnrollClient enrollClient = (FaceEnrollClient) client;
+ enrollClient.onEnrollResult(face, remaining);
+ });
+ }
+
+ @Override
+ public void onAuthenticated(long deviceId, int faceId, int userId, ArrayList<Byte> token) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AuthenticationConsumer)) {
+ Slog.e(TAG, "onAuthenticated for non-authentication consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final AuthenticationConsumer authenticationConsumer =
+ (AuthenticationConsumer) client;
+ final boolean authenticated = faceId != 0;
+ final Face face = new Face("", faceId, deviceId);
+ authenticationConsumer.onAuthenticated(face, authenticated, token);
+ });
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int userId, int acquiredInfo, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AcquisitionClient)) {
+ Slog.e(TAG, "onAcquired for non-acquire client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
+ acquisitionClient.onAcquired(acquiredInfo, vendorCode);
+ });
+ }
+
+ @Override
+ public void onError(long deviceId, int userId, int error, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ Slog.d(TAG, "handleError"
+ + ", client: " + (client != null ? client.getOwnerString() : null)
+ + ", error: " + error
+ + ", vendorCode: " + vendorCode);
+ if (!(client instanceof Interruptable)) {
+ Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(client));
+ return;
+ }
+
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(error, vendorCode);
+
+ if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+ Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ });
+ }
+
+ @Override
+ public void onRemoved(long deviceId, ArrayList<Integer> removed, int userId) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof RemovalConsumer)) {
+ Slog.e(TAG, "onRemoved for non-removal consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final RemovalConsumer removalConsumer = (RemovalConsumer) client;
+
+ if (!removed.isEmpty()) {
+ // Convert to old fingerprint-like behavior, where remove() receives one removal
+ // at a time. This way, remove can share some more common code.
+ for (int i = 0; i < removed.size(); i++) {
+ final int id = removed.get(i);
+ final Face face = new Face("", id, deviceId);
+ final int remaining = removed.size() - i - 1;
+ Slog.d(TAG, "Removed, faceId: " + id + ", remaining: " + remaining);
+ removalConsumer.onRemoved(face, remaining);
+ }
+ } else {
+ final Face face = new Face("", 0 /* identifier */, deviceId);
+ removalConsumer.onRemoved(face, 0 /* remaining */);
+ }
+
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, UserHandle.USER_CURRENT);
+ });
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof EnumerateConsumer)) {
+ Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
+
+ if (!faceIds.isEmpty()) {
+ // Convert to old fingerprint-like behavior, where enumerate() receives one
+ // template at a time. This way, enumerate can share some more common code.
+ for (int i = 0; i < faceIds.size(); i++) {
+ final Face face = new Face("", faceIds.get(i), deviceId);
+ enumerateConsumer.onEnumerationResult(face, faceIds.size() - i - 1);
+ }
+ } else {
+ // For face, the HIDL contract is to receive an empty list when there are no
+ // templates enrolled. Send a null identifier since we don't consume them
+ // anywhere, and send remaining == 0 so this code can be shared with
+ // Fingerprint@2.1
+ enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
+ }
+ });
+ }
+
+ @Override
+ public void onLockoutChanged(long duration) {
+ mHandler.post(() -> {
+ Slog.d(TAG, "onLockoutChanged: " + duration);
+ final @LockoutTracker.LockoutMode int lockoutMode;
+ if (duration == 0) {
+ lockoutMode = LockoutTracker.LOCKOUT_NONE;
+ } else if (duration == -1 || duration == Long.MAX_VALUE) {
+ lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
+ } else {
+ lockoutMode = LockoutTracker.LOCKOUT_TIMED;
+ }
+
+ mLockoutTracker.setCurrentUserLockoutMode(lockoutMode);
+
+ if (duration == 0) {
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId);
+ }
+ });
+ }
+ };
+
+ Face10(@NonNull Context context, int sensorId,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ mFaceSensorProperties = new FaceSensorProperties(sensorId, false /* supportsFaceDetect */);
+ mContext = context;
+ mSensorId = sensorId;
+ mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */);
+ mHandler = new Handler(Looper.getMainLooper());
+ mUsageStats = new UsageStats(context);
+ mAuthenticatorIds = new HashMap<>();
+ mLazyDaemon = Face10.this::getDaemon;
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
+ mLockoutTracker = new LockoutHalImpl();
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register user switch observer");
+ }
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.e(TAG, "HAL died");
+ mHandler.post(() -> {
+ PerformanceTracker.getInstanceForSensorId(mSensorId)
+ .incrementHALDeathCount();
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+
+ mScheduler.recordCrashState();
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FACE,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ }
+ });
+ }
+
+ private synchronized IBiometricsFace getDaemon() {
+ if (mDaemon != null) {
+ return mDaemon;
+ }
+
+ Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+ + mScheduler.getCurrentClient());
+
+ try {
+ mDaemon = IBiometricsFace.getService();
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened.
+ Slog.w(TAG, "NoSuchElementException", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get face HAL", e);
+ }
+
+ if (mDaemon == null) {
+ Slog.w(TAG, "Face HAL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
+
+ // HAL ID for these HIDL versions are only used to determine if callbacks have been
+ // successfully set.
+ long halId = 0;
+ try {
+ halId = mDaemon.setCallback(mDaemonCallback).value;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set callback for face HAL", e);
+ mDaemon = null;
+ }
+
+ Slog.d(TAG, "Face HAL ready, HAL ID: " + halId);
+ if (halId != 0) {
+ scheduleLoadAuthenticatorIds();
+ scheduleInternalCleanup(ActivityManager.getCurrentUser());
+ } else {
+ Slog.e(TAG, "Unable to set callback");
+ mDaemon = null;
+ }
+
+ return mDaemon;
+ }
+
+ @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
+ return mLockoutTracker.getLockoutModeForUser(userId);
+ }
+
+ private void scheduleLoadAuthenticatorIds() {
+ // Note that this can be performed on the scheduler (as opposed to being done immediately
+ // when the HAL is (re)loaded, since
+ // 1) If this is truly the first time it's being performed (e.g. system has just started),
+ // this will be run very early and way before any applications need to generate keys.
+ // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has
+ // just been reloaded), the framework already has a cache of the authenticatorIds. This
+ // is safe because authenticatorIds only change when A) new template has been enrolled,
+ // or B) all templates are removed.
+ mHandler.post(() -> {
+ for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
+ final int targetUserId = user.id;
+ if (!mAuthenticatorIds.containsKey(targetUserId)) {
+ scheduleUpdateActiveUserWithoutHandler(targetUserId);
+ }
+ }
+ });
+ }
+
+ /**
+ * Schedules the {@link FingerprintUpdateActiveUserClient} without posting the work onto the
+ * handler. Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
+ * invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
+ * this operation on the same lambda/runnable as those operations so that the ordering is
+ * correct.
+ */
+ private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
+ final boolean hasEnrolled = !getEnrolledFaces(targetUserId).isEmpty();
+ final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
+ mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, mCurrentUserId,
+ hasEnrolled, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, (clientMonitor, success) -> {
+ if (success) {
+ mCurrentUserId = targetUserId;
+ }
+ });
+ }
+
+ void scheduleResetLockout(int userId, @NonNull byte[] hardwareAuthToken) {
+ mHandler.post(() -> {
+ if (getEnrolledFaces(userId).isEmpty()) {
+ Slog.w(TAG, "Ignoring lockout reset, no templates enrolled for user: " + userId);
+ return;
+ }
+
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
+ mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
+ hardwareAuthToken);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleSetFeature(@NonNull IBinder token, int userId, int feature, boolean enabled,
+ @NonNull byte[] hardwareAuthToken, @NonNull IFaceServiceReceiver receiver,
+ @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ final List<Face> faces = getEnrolledFaces(userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "Ignoring setFeature, no templates enrolled for user: " + userId);
+ return;
+ }
+
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final int faceId = faces.get(0).getBiometricId();
+ final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ opPackageName, mSensorId, feature, enabled, hardwareAuthToken, faceId);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleGetFeature(@NonNull IBinder token, int userId, int feature,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ final List<Face> faces = getEnrolledFaces(userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "Ignoring getFeature, no templates enrolled for user: " + userId);
+ return;
+ }
+
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final int faceId = faces.get(0).getBiometricId();
+ final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ opPackageName, mSensorId, feature, faceId);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleGenerateChallenge(@NonNull IBinder token, @NonNull IFaceServiceReceiver receiver,
+ @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
+ mSensorId);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String owner) {
+ mHandler.post(() -> {
+ final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
+ mLazyDaemon, token, owner, mSensorId);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleEnroll(@NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
+ @NonNull int[] disabledFeatures, @Nullable NativeHandle surfaceHandle) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
+ UserHandle.CURRENT);
+
+ final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
+ opPackageName, FaceUtils.getInstance(), disabledFeatures, ENROLL_TIMEOUT_SEC,
+ surfaceHandle, mSensorId);
+
+ mScheduler.scheduleClientMonitor(client, ((clientMonitor, success) -> {
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId());
+ }
+ }));
+ });
+ }
+
+ void cancelEnrollment(@NonNull IBinder token) {
+ mHandler.post(() -> {
+ mScheduler.cancelEnrollment(token);
+ });
+ }
+
+ void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId, int cookie,
+ @NonNull ClientMonitorCallbackConverter receiver, @NonNull String opPackageName,
+ boolean restricted, int statsClient) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId);
+ final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
+ mLazyDaemon, token, receiver, userId, operationId, restricted, opPackageName,
+ cookie, false /* requireConfirmation */, mSensorId, isStrongBiometric,
+ statsClient, mLockoutTracker, mUsageStats);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void startPreparedClient(int cookie) {
+ mHandler.post(() -> {
+ mScheduler.startPreparedClient(cookie);
+ });
+ }
+
+ void cancelAuthentication(@NonNull IBinder token) {
+ mHandler.post(() -> {
+ mScheduler.cancelAuthentication(token);
+ });
+ }
+
+ void scheduleRemove(@NonNull IBinder token, int faceId, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
+ FaceUtils.getInstance(), mSensorId, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ private void scheduleInternalCleanup(int userId) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final List<Face> enrolledList = getEnrolledFaces(userId);
+ final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
+ mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, enrolledList,
+ FaceUtils.getInstance(), mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ boolean isHardwareDetected() {
+ final IBiometricsFace daemon = getDaemon();
+ return daemon != null;
+ }
+
+ @NonNull FaceSensorProperties getFaceSensorProperties() {
+ return mFaceSensorProperties;
+ }
+
+ List<Face> getEnrolledFaces(int userId) {
+ return FaceUtils.getInstance().getBiometricsForUser(mContext, userId);
+ }
+
+ long getAuthenticatorId(int userId) {
+ return mAuthenticatorIds.get(userId);
+ }
+
+ public void dump(@NonNull PrintWriter pw) {
+ PerformanceTracker performanceTracker =
+ PerformanceTracker.getInstanceForSensorId(mSensorId);
+
+ JSONObject dump = new JSONObject();
+ try {
+ dump.put("service", "Face Manager");
+
+ JSONArray sets = new JSONArray();
+ for (UserInfo user : UserManager.get(mContext).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ final int N = FaceUtils.getInstance().getBiometricsForUser(mContext, userId).size();
+ JSONObject set = new JSONObject();
+ set.put("id", userId);
+ set.put("count", N);
+ set.put("accept", performanceTracker.getAcceptForUser(userId));
+ set.put("reject", performanceTracker.getRejectForUser(userId));
+ set.put("acquire", performanceTracker.getAcquireForUser(userId));
+ set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
+ set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
+ // cryptoStats measures statistics about secure face transactions
+ // (e.g. to unlock password storage, make secure purchases, etc.)
+ set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
+ set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
+ set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
+ sets.put(set);
+ }
+
+ dump.put("prints", sets);
+ } catch (JSONException e) {
+ Slog.e(TAG, "dump formatting failure", e);
+ }
+ pw.println(dump);
+ pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
+
+ mUsageStats.print(pw);
+ }
+
+ public void dumpHal(@NonNull FileDescriptor fd, @NonNull String[] args) {
+ // WARNING: CDD restricts image data from leaving TEE unencrypted on
+ // production devices:
+ // [C-1-10] MUST not allow unencrypted access to identifiable biometric
+ // data or any data derived from it (such as embeddings) to the
+ // Application Processor outside the context of the TEE.
+ // As such, this API should only be enabled for testing purposes on
+ // engineering and userdebug builds. All modules in the software stack
+ // MUST enforce final build products do NOT have this functionality.
+ // Additionally, the following check MUST NOT be removed.
+ if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
+ return;
+ }
+
+ // Additionally, this flag allows turning off face for a device
+ // (either permanently through the build or on an individual device).
+ if (SystemProperties.getBoolean("ro.face.disable_debug_data", false)
+ || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) {
+ return;
+ }
+
+ // The debug method takes two file descriptors. The first is for text
+ // output, which we will drop. The second is for binary data, which
+ // will be the protobuf data.
+ final IBiometricsFace daemon = getDaemon();
+ if (daemon != null) {
+ FileOutputStream devnull = null;
+ try {
+ devnull = new FileOutputStream("/dev/null");
+ final NativeHandle handle = new NativeHandle(
+ new FileDescriptor[] { devnull.getFD(), fd },
+ new int[0], false);
+ daemon.debug(handle, new ArrayList<String>(Arrays.asList(args)));
+ } catch (IOException | RemoteException ex) {
+ Slog.d(TAG, "error while reading face debugging data", ex);
+ } finally {
+ if (devnull != null) {
+ try {
+ devnull.close();
+ } catch (IOException ex) {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
index d6e8cc8ccc2e..21bda74bc6b9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
@@ -16,25 +16,28 @@
package com.android.server.biometrics.sensors.face;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.TaskStackListener;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.face.FaceManager;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.Slog;
import com.android.internal.R;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.BiometricServiceBase;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -44,7 +47,9 @@ import java.util.ArrayList;
* Face-specific authentication client supporting the {@link android.hardware.biometrics.face.V1_0}
* and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
*/
-class FaceAuthenticationClient extends AuthenticationClient {
+class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
+
+ private static final String TAG = "FaceAuthenticationClient";
private final NotificationManager mNotificationManager;
private final UsageStats mUsageStats;
@@ -55,20 +60,17 @@ class FaceAuthenticationClient extends AuthenticationClient {
private final int[] mKeyguardIgnoreListVendor;
private int mLastAcquire;
- // We need to track this state since it's possible for applications to request for
- // authentication while the device is already locked out. In that case, the client is created
- // but not started yet. The user shouldn't receive the error haptics in this case.
- private boolean mStarted;
- FaceAuthenticationClient(Context context, BiometricServiceBase.DaemonWrapper daemon,
- IBinder token, ClientMonitorCallbackConverter listener, int targetUserId, long opId,
+ FaceAuthenticationClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
- boolean isStrongBiometric, int statsClient, TaskStackListener taskStackListener,
- LockoutTracker lockoutTracker, UsageStats usageStats) {
- super(context, daemon, token, listener, targetUserId, 0 /* groupId */, opId,
- restricted, owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FACE, statsClient, taskStackListener,
- lockoutTracker, null /* surface */);
+ boolean isStrongBiometric, int statsClient,
+ @NonNull LockoutTracker lockoutTracker, @NonNull UsageStats usageStats) {
+ super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
+ owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
+ BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
+ lockoutTracker);
mNotificationManager = context.getSystemService(NotificationManager.class);
mUsageStats = usageStats;
@@ -84,15 +86,25 @@ class FaceAuthenticationClient extends AuthenticationClient {
}
@Override
- protected void onStart() {
- super.onStart();
- mStarted = true;
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().authenticate(mOperationId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting auth", e);
+ onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
}
@Override
- protected void onStop() {
- super.onStop();
- mStarted = false;
+ protected void stopHalOperation() {
+ try {
+ getFreshDaemon().cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
}
private boolean wasUserDetected() {
@@ -103,9 +115,9 @@ class FaceAuthenticationClient extends AuthenticationClient {
}
@Override
- public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+ public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {
- final boolean result = super.onAuthenticated(identifier, authenticated, token);
+ super.onAuthenticated(identifier, authenticated, token);
mUsageStats.addEvent(new UsageStats.AuthenticationEvent(
getStartTimeMs(),
@@ -119,7 +131,7 @@ class FaceAuthenticationClient extends AuthenticationClient {
// 1) Authenticated == true
// 2) Error occurred
// 3) Authenticated == false
- return result || !authenticated;
+ mFinishCallback.onClientFinished(this, true /* success */);
}
@Override
@@ -140,7 +152,9 @@ class FaceAuthenticationClient extends AuthenticationClient {
}
case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
- if (mStarted) {
+ if (mAuthAttempted) {
+ // Only vibrate if auth was attempted. If the user was already locked out prior
+ // to starting authentication, do not vibrate.
vibrateError();
}
break;
@@ -168,7 +182,7 @@ class FaceAuthenticationClient extends AuthenticationClient {
}
@Override
- public boolean onAcquired(int acquireInfo, int vendorCode) {
+ public void onAcquired(int acquireInfo, int vendorCode) {
mLastAcquire = acquireInfo;
@@ -205,12 +219,12 @@ class FaceAuthenticationClient extends AuthenticationClient {
.build();
mNotificationManager.createNotificationChannel(channel);
- mNotificationManager.notifyAsUser(FaceService.NOTIFICATION_TAG,
- FaceService.NOTIFICATION_ID, notification,
+ mNotificationManager.notifyAsUser(Face10.NOTIFICATION_TAG,
+ Face10.NOTIFICATION_ID, notification,
UserHandle.CURRENT);
}
final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
- return onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
+ onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index a39c4b974cdd..33244b8e61a8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -23,6 +23,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import com.android.server.biometrics.SensorConfig;
+import com.android.server.biometrics.sensors.LockoutTracker;
/**
* Shim that converts IFaceService into a common reusable IBiometricAuthenticator interface.
@@ -68,6 +69,12 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
+ public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId)
+ throws RemoteException {
+ return mFaceService.getLockoutModeForUser(userId);
+ }
+
+ @Override
public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
mFaceService.resetLockout(userId, hardwareAuthToken);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
index a8897c3a36b7..52a822675c2f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
@@ -16,34 +16,51 @@
package com.android.server.biometrics.sensors.face;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.FaceManager;
import android.os.IBinder;
-import android.os.PowerManager;
-import android.view.Surface;
+import android.os.NativeHandle;
+import android.os.RemoteException;
+import android.util.Slog;
import com.android.internal.R;
import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.sensors.BiometricServiceBase;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
+import java.util.ArrayList;
+import java.util.Arrays;
+
/**
- * Face-specific enrollment client.
+ * Face-specific enroll client supporting the {@link android.hardware.biometrics.face.V1_0}
+ * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
*/
-public class FaceEnrollClient extends EnrollClient {
-
- private final int[] mEnrollIgnoreList;
- private final int[] mEnrollIgnoreListVendor;
-
- FaceEnrollClient(Context context, BiometricServiceBase.DaemonWrapper daemon, IBinder token,
- ClientMonitorCallbackConverter listener, int userId, int groupId, byte[] cryptoToken,
- boolean restricted, String owner, BiometricUtils utils, int[] disabledFeatures,
- int timeoutSec, int statsModality, Surface surface, int sensorId) {
- super(context, daemon, token, listener, userId, groupId, cryptoToken, restricted,
- owner, utils, disabledFeatures, timeoutSec, statsModality, surface,
- sensorId, false /* shouldVibrate */);
+public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
+
+ private static final String TAG = "FaceEnrollClient";
+
+ @NonNull private final int[] mDisabledFeatures;
+ @Nullable private final NativeHandle mSurfaceHandle;
+ @NonNull private final int[] mEnrollIgnoreList;
+ @NonNull private final int[] mEnrollIgnoreListVendor;
+
+ FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
+ @NonNull int[] disabledFeatures, int timeoutSec, @Nullable NativeHandle surfaceHandle,
+ int sensorId) {
+ super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+ timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
+ false /* shouldVibrate */);
+ mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
+ mSurfaceHandle = surfaceHandle;
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
mEnrollIgnoreListVendor = getContext().getResources()
@@ -51,13 +68,71 @@ public class FaceEnrollClient extends EnrollClient {
}
@Override
- public boolean onAcquired(int acquireInfo, int vendorCode) {
+ protected boolean hasReachedEnrollmentLimit() {
+ final int limit = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
+ final int enrolled = mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId())
+ .size();
+ if (enrolled >= limit) {
+ Slog.w(TAG, "Too many faces registered, user: " + getTargetUserId());
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onAcquired(int acquireInfo, int vendorCode) {
final boolean shouldSend;
if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
shouldSend = !Utils.listContains(mEnrollIgnoreListVendor, vendorCode);
} else {
shouldSend = !Utils.listContains(mEnrollIgnoreList, acquireInfo);
}
- return onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
+ onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ final ArrayList<Byte> token = new ArrayList<>();
+ for (byte b : mHardwareAuthToken) {
+ token.add(b);
+ }
+ final ArrayList<Integer> disabledFeatures = new ArrayList<>();
+ for (int disabledFeature : mDisabledFeatures) {
+ disabledFeatures.add(disabledFeature);
+ }
+
+ android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 =
+ android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(getFreshDaemon());
+ try {
+ final int status;
+ if (daemon11 != null) {
+ status = daemon11.enroll_1_1(token, mTimeoutSec, disabledFeatures, mSurfaceHandle);
+ } else if (mSurfaceHandle == null) {
+ status = getFreshDaemon().enroll(token, mTimeoutSec, disabledFeatures);
+ } else {
+ Slog.e(TAG, "enroll(): surface is only supported in @1.1 HAL");
+ status = BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS;
+ }
+ if (status != Status.OK) {
+ onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting enroll", e);
+ onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ protected void stopHalOperation() {
+ try {
+ getFreshDaemon().cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
new file mode 100644
index 000000000000..67f2712d0b9d
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.GenerateChallengeClient;
+
+/**
+ * Face-specific generateChallenge client supporting the
+ * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
+ * HIDL interfaces.
+ */
+public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiometricsFace> {
+
+ private static final String TAG = "FaceGenerateChallengeClient";
+ private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+
+ FaceGenerateChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, listener, owner, sensorId);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ mChallenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "generateChallenge failed", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
new file mode 100644
index 000000000000..ce57cb7686ed
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalBool;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+/**
+ * Face-specific getFeature client supporting the {@link android.hardware.biometrics.face.V1_0}
+ * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ */
+public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> {
+
+ private static final String TAG = "FaceGetFeatureClient";
+
+ private final int mFeature;
+ private final int mFaceId;
+
+ FaceGetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int sensorId, int feature, int faceId) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
+ mFeature = feature;
+ mFaceId = faceId;
+
+ }
+
+ @Override
+ public void unableToStart() {
+ try {
+ getListener().onFeatureGet(false /* success */, mFeature, false /* value */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId);
+ getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to getFeature", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
new file mode 100644
index 000000000000..93f35f4c90cd
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.Face;
+import android.os.IBinder;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalCleanupClient;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+import com.android.server.biometrics.sensors.RemovalClient;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Face-specific internal cleanup client supporting the
+ * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
+ * HIDL interfaces.
+ */
+class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsFace> {
+
+ FaceInternalCleanupClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
+ int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
+ enrolledList, utils, authenticatorIds);
+ }
+
+ @Override
+ protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
+ LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
+ List<Face> enrolledList, BiometricUtils utils, int sensorId) {
+ return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+ enrolledList, utils, sensorId);
+ }
+
+ @Override
+ protected RemovalClient<IBiometricsFace> getRemovalClient(Context context,
+ LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token,
+ int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+ Map<Integer, Long> authenticatorIds) {
+ // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
+ // is all done internally.
+ return new FaceRemovalClient(context, lazyDaemon, token,
+ null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+ sensorId, authenticatorIds);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
new file mode 100644
index 000000000000..f25242ee9b85
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.Face;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+
+import java.util.List;
+
+/**
+ * Face-specific internal enumerate client supporting the
+ * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
+ * HIDL interfaces.
+ */
+class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFace> {
+ private static final String TAG = "FaceInternalEnumerateClient";
+
+ FaceInternalEnumerateClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
+ @NonNull String owner, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+ int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
+ BiometricsProtoEnums.MODALITY_FACE);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().enumerate();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting enumerate", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
new file mode 100644
index 000000000000..00d5f500b241
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.RemovalClient;
+
+import java.util.Map;
+
+/**
+ * Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0}
+ * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ */
+class FaceRemovalClient extends RemovalClient<IBiometricsFace> {
+ private static final String TAG = "FaceRemovalClient";
+
+ FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+ int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().remove(mBiometricId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting remove", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
new file mode 100644
index 000000000000..69070da0491e
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+
+import java.util.ArrayList;
+
+/**
+ * Face-specific resetLockout client supporting the {@link android.hardware.biometrics.face.V1_0}
+ * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ */
+public class FaceResetLockoutClient extends ClientMonitor<IBiometricsFace> {
+
+ private static final String TAG = "FaceResetLockoutClient";
+
+ private final ArrayList<Byte> mHardwareAuthToken;
+
+ FaceResetLockoutClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull byte[] hardwareAuthToken) {
+ super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+ 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+
+ mHardwareAuthToken = new ArrayList<>();
+ for (byte b : hardwareAuthToken) {
+ mHardwareAuthToken.add(b);
+ }
+ }
+
+ @Override
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().resetLockout(mHardwareAuthToken);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to reset lockout", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
new file mode 100644
index 000000000000..a10c573f34fc
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.RevokeChallengeClient;
+
+/**
+ * Face-specific revokeChallenge client supporting the {@link android.hardware.biometrics.face.V1_0}
+ * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ */
+public class FaceRevokeChallengeClient extends RevokeChallengeClient<IBiometricsFace> {
+
+ private static final String TAG = "FaceRevokeChallengeClient";
+
+ FaceRevokeChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
+ @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, owner, sensorId);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().revokeChallenge();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "revokeChallenge failed", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index f5f02fa6c1e4..b832a09f8f88 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -16,62 +16,33 @@
package com.android.server.biometrics.sensors.face;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.app.NotificationManager;
+import android.annotation.NonNull;
import android.content.Context;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
-import android.hardware.biometrics.face.V1_0.OptionalBool;
-import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.Face;
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
+import android.hardware.face.FaceSensorProperties;
import android.os.Binder;
-import android.os.Build;
-import android.os.Environment;
import android.os.IBinder;
import android.os.NativeHandle;
-import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
import android.util.Slog;
import android.view.Surface;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
-import com.android.server.SystemServerInitThreadPool;
-import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.BiometricServiceBase;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.SystemService;
+import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.PerformanceTracker;
-import com.android.server.biometrics.sensors.RemovalClient;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -81,178 +52,165 @@ import java.util.List;
* A service to manage multiple clients that want to access the face HAL API.
* The service is responsible for maintaining a list of clients and dispatching all
* face-related events.
- *
- * @hide
*/
-public class FaceService extends BiometricServiceBase {
+public class FaceService extends SystemService {
protected static final String TAG = "FaceService";
- private static final boolean DEBUG = true;
- private static final String FACE_DATA_DIR = "facedata";
- private static final String ACTION_LOCKOUT_RESET =
- "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
- private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
- static final String NOTIFICATION_TAG = "FaceService";
- static final int NOTIFICATION_ID = 1;
+ private Face10 mFace10;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ private final LockPatternUtils mLockPatternUtils;
/**
* Receives the incoming binder calls from FaceManager.
*/
private final class FaceServiceWrapper extends IFaceService.Stub {
- private static final int ENROLL_TIMEOUT_SEC = 75;
+ @Override // Binder call
+ public List<FaceSensorProperties> getSensorProperties(String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ final List<FaceSensorProperties> properties = new ArrayList<>();
+
+ if (mFace10 != null) {
+ properties.add(mFace10.getFaceSensorProperties());
+ }
- /**
- * The following methods contain common code which is shared in biometrics/common.
- */
+ Slog.d(TAG, "Retrieved sensor properties for: " + opPackageName
+ + ", sensors: " + properties.size());
+ return properties;
+ }
@Override // Binder call
- public long generateChallenge(IBinder token) {
- checkPermission(MANAGE_BIOMETRIC);
- return startGenerateChallenge(token);
+ public void generateChallenge(IBinder token, IFaceServiceReceiver receiver,
+ String opPackageName) {
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.scheduleGenerateChallenge(token, receiver, opPackageName);
}
@Override // Binder call
- public int revokeChallenge(IBinder token) {
- checkPermission(MANAGE_BIOMETRIC);
- mHandler.post(() -> {
- // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks.
- if (getCurrentClient() == null) {
- // if we aren't handling any other HIDL calls (mCurrentClient == null), revoke
- // the challenge right away.
- startRevokeChallenge(token);
- } else {
- // postpone revoking the challenge until we finish processing the current HIDL
- // call.
- mRevokeChallengePending = true;
- }
- });
- return Status.OK;
+ public void revokeChallenge(IBinder token, String owner) {
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.scheduleRevokeChallenge(token, owner);
}
@Override // Binder call
- public void enroll(int userId, final IBinder token, final byte[] cryptoToken,
+ public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures, Surface surface) {
- checkPermission(MANAGE_BIOMETRIC);
- updateActiveGroup(userId, opPackageName);
-
- mHandler.post(() -> {
- mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
- UserHandle.CURRENT);
- });
-
- final boolean restricted = isRestricted();
- final EnrollClient client = new FaceEnrollClient(getContext(),
- mDaemonWrapper, token, new ClientMonitorCallbackConverter(receiver),
- mCurrentUserId, 0 /* groupId */, cryptoToken, restricted, opPackageName,
- getBiometricUtils(), disabledFeatures, ENROLL_TIMEOUT_SEC, statsModality(),
- surface, getSensorId());
-
- enrollInternal(client, mCurrentUserId);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.scheduleEnroll(token, hardwareAuthToken, userId, receiver, opPackageName,
+ disabledFeatures, convertSurfaceToNativeHandle(surface));
}
@Override // Binder call
- public void enrollRemotely(int userId, final IBinder token, final byte[] cryptoToken,
+ public void enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures) {
- checkPermission(MANAGE_BIOMETRIC);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
// TODO(b/145027036): Implement this.
}
@Override // Binder call
public void cancelEnrollment(final IBinder token) {
- checkPermission(MANAGE_BIOMETRIC);
- cancelEnrollmentInternal(token);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.cancelEnrollment(token);
}
@Override // Binder call
- public void authenticate(final IBinder token, final long opId, int userId,
- final IFaceServiceReceiver receiver, final int flags,
- final String opPackageName) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- updateActiveGroup(userId, opPackageName);
- final boolean restricted = isRestricted();
- final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
+ public void authenticate(final IBinder token, final long operationId, int userId,
+ final IFaceServiceReceiver receiver, final String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+
+ // TODO(b/152413782): If the sensor supports face detect and the device is encrypted or
+ // lockdown, something wrong happened. See similar path in FingerprintService.
+
+ final boolean restricted = false; // Face APIs are private
+ final int statsClient = Utils.isKeyguard(getContext(), opPackageName)
+ ? BiometricsProtoEnums.CLIENT_KEYGUARD
: BiometricsProtoEnums.CLIENT_UNKNOWN;
- final AuthenticationClient client = new FaceAuthenticationClient(getContext(),
- mDaemonWrapper, token,
- new ClientMonitorCallbackConverter(receiver),
- mCurrentUserId, opId, restricted, opPackageName,
- 0 /* cookie */, false /* requireConfirmation */, getSensorId(),
- isStrongBiometric(), statsClient,
- mTaskStackListener, mLockoutTracker, mUsageStats);
- authenticateInternal(client, opPackageName);
+ mFace10.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */,
+ new ClientMonitorCallbackConverter(receiver), opPackageName, restricted,
+ statsClient);
}
@Override // Binder call
- public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
- int groupId, IBiometricSensorReceiver sensorReceiver,
+ public void detectFace(final IBinder token, final int userId,
+ final IFaceServiceReceiver receiver, final String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ if (!Utils.isKeyguard(getContext(), opPackageName)) {
+ Slog.w(TAG, "detectFace called from non-sysui package: " + opPackageName);
+ return;
+ }
+
+ if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
+ // If this happens, something in KeyguardUpdateMonitor is wrong. This should only
+ // ever be invoked when the user is encrypted or lockdown.
+ Slog.e(TAG, "detectFace invoked when user is not encrypted or lockdown");
+ return;
+ }
+
+ // TODO(b/152413782): Implement this once it's supported in the HAL
+ }
+
+ @Override // Binder call
+ public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
+ long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
String opPackageName, int cookie, int callingUid, int callingPid,
int callingUserId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- updateActiveGroup(groupId, opPackageName);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+
final boolean restricted = true; // BiometricPrompt is always restricted
- final AuthenticationClient client = new FaceAuthenticationClient(getContext(),
- mDaemonWrapper, token,
- new ClientMonitorCallbackConverter(sensorReceiver),
- mCurrentUserId, opId, restricted, opPackageName, cookie,
- requireConfirmation, getSensorId(), isStrongBiometric(),
- BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- mTaskStackListener, mLockoutTracker, mUsageStats);
- authenticateInternal(client, opPackageName, callingUid, callingPid,
- callingUserId);
+ mFace10.scheduleAuthenticate(token, operationId, userId, cookie,
+ new ClientMonitorCallbackConverter(sensorReceiver), opPackageName,
+ restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT);
}
@Override // Binder call
public void startPreparedClient(int cookie) {
- checkPermission(MANAGE_BIOMETRIC);
- startCurrentClient(cookie);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.startPreparedClient(cookie);
}
@Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- cancelAuthenticationInternal(token, opPackageName);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.cancelAuthentication(token);
+ }
+
+ @Override // Binder call
+ public void cancelFaceDetect(final IBinder token, final String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ if (!Utils.isKeyguard(getContext(), opPackageName)) {
+ Slog.w(TAG, "cancelFaceDetect called from non-sysui package: "
+ + opPackageName);
+ return;
+ }
+
+ // TODO(b/152413782): Implement this once it's supported in the HAL
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
int callingUid, int callingPid, int callingUserId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- // Cancellation is from system server in this case.
- cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
- callingUserId, false /* fromClient */);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.cancelAuthentication(token);
}
@Override // Binder call
public void remove(final IBinder token, final int faceId, final int userId,
final IFaceServiceReceiver receiver, final String opPackageName) {
- checkPermission(MANAGE_BIOMETRIC);
- updateActiveGroup(userId, opPackageName);
-
- if (token == null) {
- Slog.w(TAG, "remove(): token is null");
- return;
- }
-
- final boolean restricted = isRestricted();
- final RemovalClient client = new RemovalClient(getContext(),
- mDaemonWrapper, token, new ClientMonitorCallbackConverter(receiver), faceId,
- 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils(),
- getSensorId(), statsModality());
- removeInternal(client);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.scheduleRemove(token, faceId, userId, receiver, opPackageName);
}
@Override
- public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
- throws RemoteException {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- FaceService.super.addLockoutResetCallback(callback);
+ public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
+ final String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mLockoutResetDispatcher.addCallback(callback, opPackageName);
}
@Override // Binder call
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
}
@@ -260,33 +218,25 @@ public class FaceService extends BiometricServiceBase {
final long ident = Binder.clearCallingIdentity();
try {
if (args.length > 1 && "--hal".equals(args[0])) {
- dumpHal(fd, Arrays.copyOfRange(args, 1, args.length, args.getClass()));
+ mFace10.dumpHal(fd, Arrays.copyOfRange(args, 1, args.length, args.getClass()));
} else {
- dumpInternal(pw);
+ mFace10.dump(pw);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- /**
- * The following methods don't use any common code from BiometricService
- */
-
- // TODO: refactor out common code here
@Override // Binder call
public boolean isHardwareDetected(String opPackageName) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
- Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId())) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ if (mFace10 == null) {
+ Slog.wtf(TAG, "No HAL, caller: " + opPackageName);
return false;
}
-
final long token = Binder.clearCallingIdentity();
try {
- IBiometricsFace daemon = getFaceDaemon();
- return daemon != null;
+ return mFace10.isHardwareDetected();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -294,671 +244,67 @@ public class FaceService extends BiometricServiceBase {
@Override // Binder call
public List<Face> getEnrolledFaces(int userId, String opPackageName) {
- checkPermission(MANAGE_BIOMETRIC);
- if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
- Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId())) {
- return null;
- }
-
- return FaceService.this.getEnrolledTemplates(userId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return mFace10.getEnrolledFaces(userId);
}
@Override // Binder call
public boolean hasEnrolledFaces(int userId, String opPackageName) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
- Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId())) {
- return false;
- }
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return !mFace10.getEnrolledFaces(userId).isEmpty();
+ }
- return FaceService.this.hasEnrolledBiometrics(userId);
+ @Override
+ public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return mFace10.getLockoutModeForUser(userId);
}
@Override // Binder call
- public long getAuthenticatorId(int callingUserId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- return FaceService.this.getAuthenticatorId(callingUserId);
+ public long getAuthenticatorId(int userId) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return mFace10.getAuthenticatorId(userId);
}
@Override // Binder call
public void resetLockout(int userId, byte[] hardwareAuthToken) {
- checkPermission(MANAGE_BIOMETRIC);
- mHandler.post(() -> {
- if (!FaceService.this.hasEnrolledBiometrics(userId)) {
- Slog.w(TAG, "Ignoring lockout reset, no templates enrolled");
- return;
- }
-
- Slog.d(TAG, "Resetting lockout for user: " + userId);
-
- updateActiveGroup(userId, null /* opPackageName */);
- try {
- mDaemonWrapper.resetLockout(hardwareAuthToken);
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to reset lockout", e);
- }
- });
- }
-
- @Override
- public void setFeature(int userId, int feature, boolean enabled, final byte[] token,
- IFaceServiceReceiver receiver, final String opPackageName) {
- checkPermission(MANAGE_BIOMETRIC);
-
- mHandler.post(() -> {
- if (DEBUG) {
- Slog.d(TAG, "setFeature for user(" + userId + ")");
- }
- updateActiveGroup(userId, opPackageName);
- if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
- Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature);
- return;
- }
-
- final ArrayList<Byte> byteToken = new ArrayList<>();
- for (int i = 0; i < token.length; i++) {
- byteToken.add(token[i]);
- }
-
- // TODO: Support multiple faces
- final int faceId = getFirstTemplateForUser(mCurrentUserId);
-
- if (mDaemon != null) {
- try {
- final int result = mDaemon.setFeature(feature, enabled, byteToken, faceId);
- receiver.onFeatureSet(result == Status.OK, feature);
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to set feature: " + feature
- + " to enabled:" + enabled, e);
- }
- }
- });
-
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.scheduleResetLockout(userId, hardwareAuthToken);
}
@Override
- public void getFeature(int userId, int feature, IFaceServiceReceiver receiver,
+ public void setFeature(final IBinder token, int userId, int feature, boolean enabled,
+ final byte[] hardwareAuthToken, IFaceServiceReceiver receiver,
final String opPackageName) {
- checkPermission(MANAGE_BIOMETRIC);
-
- mHandler.post(() -> {
- if (DEBUG) {
- Slog.d(TAG, "getFeature for user(" + userId + ")");
- }
- updateActiveGroup(userId, opPackageName);
- // This should ideally return tri-state, but the user isn't shown settings unless
- // they are enrolled so it's fine for now.
- if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
- Slog.e(TAG, "No enrolled biometrics while getting feature: " + feature);
- return;
- }
-
- // TODO: Support multiple faces
- final int faceId = getFirstTemplateForUser(mCurrentUserId);
-
- if (mDaemon != null) {
- try {
- OptionalBool result = mDaemon.getFeature(feature, faceId);
- receiver.onFeatureGet(result.status == Status.OK, feature, result.value);
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to getRequireAttention", e);
- }
- }
- });
-
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.scheduleSetFeature(token, userId, feature, enabled, hardwareAuthToken, receiver,
+ opPackageName);
}
@Override
- public void userActivity() {
- checkPermission(MANAGE_BIOMETRIC);
-
- if (mDaemon != null) {
- try {
- mDaemon.userActivity();
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to send userActivity", e);
- }
- }
- }
-
- // TODO: Support multiple faces
- private int getFirstTemplateForUser(int user) {
- final List<Face> faces = FaceService.this.getEnrolledTemplates(user);
- if (!faces.isEmpty()) {
- return faces.get(0).getBiometricId();
- }
- return 0;
+ public void getFeature(final IBinder token, int userId, int feature,
+ IFaceServiceReceiver receiver, final String opPackageName) {
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.scheduleGetFeature(token, userId, feature, receiver, opPackageName);
}
@Override // Binder call
public void initializeConfiguration(int sensorId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- initializeConfigurationInternal(sensorId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10 = new Face10(getContext(), sensorId, mLockoutResetDispatcher);
}
}
- private final LockoutHalImpl mLockoutTracker;
-
- @GuardedBy("this")
- private IBiometricsFace mDaemon;
- private UsageStats mUsageStats;
- private boolean mRevokeChallengePending = false;
-
- private NotificationManager mNotificationManager;
-
- /**
- * Receives callbacks from the HAL.
- */
- private IBiometricsFaceClientCallback mDaemonCallback =
- new IBiometricsFaceClientCallback.Stub() {
- @Override
- public void onEnrollResult(final long deviceId, int faceId, int userId,
- int remaining) {
- mHandler.post(() -> {
- final Face face = new Face(getBiometricUtils()
- .getUniqueName(getContext(), userId), faceId, deviceId);
- FaceService.super.handleEnrollResult(face, remaining);
-
- // Enrollment changes the authenticatorId, so update it here.
- IBiometricsFace daemon = getFaceDaemon();
- if (remaining == 0 && daemon != null) {
- try {
- mAuthenticatorIds.put(userId,
- hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value
- : 0L);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to get authenticatorId", e);
- }
- }
- });
- }
-
- @Override
- public void onAcquired(final long deviceId, final int userId, final int acquiredInfo,
- final int vendorCode) {
- mHandler.post(() -> {
- FaceService.super.handleAcquired(acquiredInfo, vendorCode);
- });
- }
-
- @Override
- public void onAuthenticated(final long deviceId, final int faceId, final int userId,
- ArrayList<Byte> token) {
- mHandler.post(() -> {
- Face face = new Face("", faceId, deviceId);
- FaceService.super.handleAuthenticated(face, token);
- });
- }
-
- @Override
- public void onError(final long deviceId, final int userId, final int error,
- final int vendorCode) {
- mHandler.post(() -> {
- FaceService.super.handleError(error, vendorCode);
-
- // TODO: this chunk of code should be common to all biometric services
- if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
- // If we get HW_UNAVAILABLE, try to connect again later...
- Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
- synchronized (this) {
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
- }
- }
- });
- }
-
- @Override
- public void onRemoved(final long deviceId, ArrayList<Integer> faceIds, final int userId) {
- mHandler.post(() -> {
- if (!faceIds.isEmpty()) {
- for (int i = 0; i < faceIds.size(); i++) {
- final Face face = new Face("", faceIds.get(i), deviceId);
- // Convert to old behavior
- FaceService.super.handleRemoved(face, faceIds.size() - i - 1);
- }
- } else {
- final Face face = new Face("", 0 /* identifier */, deviceId);
- FaceService.super.handleRemoved(face, 0 /* remaining */);
- }
- Settings.Secure.putIntForUser(getContext().getContentResolver(),
- Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, UserHandle.USER_CURRENT);
- });
- }
-
- @Override
- public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
- throws RemoteException {
- mHandler.post(() -> {
- if (!faceIds.isEmpty()) {
- for (int i = 0; i < faceIds.size(); i++) {
- final Face face = new Face("", faceIds.get(i), deviceId);
- // Convert to old old behavior
- FaceService.super.handleEnumerate(face, faceIds.size() - i - 1);
- }
- } else {
- // For face, the HIDL contract is to receive an empty list when there are no
- // templates enrolled. Send a null identifier since we don't consume them
- // anywhere, and send remaining == 0 to plumb this with existing common code.
- FaceService.super.handleEnumerate(null /* identifier */, 0);
- }
- });
- }
-
- @Override
- public void onLockoutChanged(long duration) {
- Slog.d(TAG, "onLockoutChanged: " + duration);
-
- final @LockoutTracker.LockoutMode int lockoutMode;
- if (duration == 0) {
- lockoutMode = LockoutTracker.LOCKOUT_NONE;
- } else if (duration == -1 || duration == Long.MAX_VALUE) {
- lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
- } else {
- lockoutMode = LockoutTracker.LOCKOUT_TIMED;
- }
- mLockoutTracker.setCurrentUserLockoutMode(lockoutMode);
-
- mHandler.post(() -> {
- if (duration == 0) {
- notifyLockoutResetMonitors();
- }
- });
- }
- };
-
- /**
- * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
- * can be shared between the multiple biometric services.
- */
- private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
- @Override
- public int authenticate(long operationId, int groupId, Surface surface)
- throws RemoteException {
- IBiometricsFace daemon = getFaceDaemon();
- if (daemon == null) {
- Slog.w(TAG, "authenticate(): no face HAL!");
- return ERROR_ESRCH;
- }
- return daemon.authenticate(operationId);
- }
-
- @Override
- public int cancel() throws RemoteException {
- IBiometricsFace daemon = getFaceDaemon();
- if (daemon == null) {
- Slog.w(TAG, "cancel(): no face HAL!");
- return ERROR_ESRCH;
- }
- return daemon.cancel();
- }
-
- @Override
- public int remove(int groupId, int biometricId) throws RemoteException {
- IBiometricsFace daemon = getFaceDaemon();
- if (daemon == null) {
- Slog.w(TAG, "remove(): no face HAL!");
- return ERROR_ESRCH;
- }
- return daemon.remove(biometricId);
- }
-
- @Override
- public int enumerate() throws RemoteException {
- IBiometricsFace daemon = getFaceDaemon();
- if (daemon == null) {
- Slog.w(TAG, "enumerate(): no face HAL!");
- return ERROR_ESRCH;
- }
- return daemon.enumerate();
- }
-
- @Override
- public int enroll(byte[] cryptoToken, int groupId, int timeout,
- ArrayList<Integer> disabledFeatures, Surface surface) throws RemoteException {
- IBiometricsFace daemon = getFaceDaemon();
- if (daemon == null) {
- Slog.w(TAG, "enroll(): no face HAL!");
- return ERROR_ESRCH;
- }
- final ArrayList<Byte> token = new ArrayList<>();
- for (int i = 0; i < cryptoToken.length; i++) {
- token.add(cryptoToken[i]);
- }
- android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 =
- android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(
- daemon);
- if (daemon11 != null) {
- return daemon11.enroll_1_1(token, timeout, disabledFeatures,
- convertSurfaceToNativeHandle(surface));
- } else if (surface == null) {
- return daemon.enroll(token, timeout, disabledFeatures);
- } else {
- Slog.e(TAG, "enroll(): surface is only supported in @1.1 HAL");
- return ERROR_ESRCH;
- }
- }
-
- @Override
- public void resetLockout(byte[] cryptoToken) throws RemoteException {
- IBiometricsFace daemon = getFaceDaemon();
- if (daemon == null) {
- Slog.w(TAG, "resetLockout(): no face HAL!");
- return;
- }
- final ArrayList<Byte> token = new ArrayList<>();
- for (int i = 0; i < cryptoToken.length; i++) {
- token.add(cryptoToken[i]);
- }
- daemon.resetLockout(token);
- }
- };
-
-
public FaceService(Context context) {
super(context);
- mLockoutTracker = new LockoutHalImpl();
- mUsageStats = new UsageStats(context);
- mNotificationManager = getContext().getSystemService(NotificationManager.class);
- }
-
- @Override
- protected void removeClient(ClientMonitor client) {
- super.removeClient(client);
- if (mRevokeChallengePending) {
- startRevokeChallenge(null);
- mRevokeChallengePending = false;
- }
+ mLockoutResetDispatcher = new LockoutResetDispatcher(context);
+ mLockPatternUtils = new LockPatternUtils(context);
}
@Override
public void onStart() {
- super.onStart();
publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
- // Get the face daemon on FaceService's on thread so SystemServerInitThreadPool isn't
- // blocked
- SystemServerInitThreadPool.submit(() -> mHandler.post(this::getFaceDaemon),
- TAG + ".onStart");
- }
-
- @Override
- public String getTag() {
- return TAG;
- }
-
- @Override
- protected DaemonWrapper getDaemonWrapper() {
- return mDaemonWrapper;
- }
-
- @Override
- protected BiometricUtils getBiometricUtils() {
- return FaceUtils.getInstance();
- }
-
- @Override
- protected boolean hasReachedEnrollmentLimit(int userId) {
- final int limit = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
- final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
- if (enrolled >= limit) {
- Slog.w(TAG, "Too many faces registered, user: " + userId);
- return true;
- }
- return false;
- }
-
- @Override
- public void serviceDied(long cookie) {
- super.serviceDied(cookie);
- mDaemon = null;
-
- mCurrentUserId = UserHandle.USER_NULL; // Force updateActiveGroup() to re-evaluate
- }
-
- @Override
- protected void updateActiveGroup(int userId, String clientPackage) {
- IBiometricsFace daemon = getFaceDaemon();
-
- if (daemon != null) {
- try {
- userId = getUserOrWorkProfileId(clientPackage, userId);
- if (userId != mCurrentUserId) {
- final File baseDir = Environment.getDataVendorDeDirectory(userId);
- final File faceDir = new File(baseDir, FACE_DATA_DIR);
- if (!faceDir.exists()) {
- if (!faceDir.mkdir()) {
- Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
- return;
- }
- // Calling mkdir() from this process will create a directory with our
- // permissions (inherited from the containing dir). This command fixes
- // the label.
- if (!SELinux.restorecon(faceDir)) {
- Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
- return;
- }
- }
-
- daemon.setActiveUser(userId, faceDir.getAbsolutePath());
- mCurrentUserId = userId;
- mAuthenticatorIds.put(userId,
- hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value : 0L);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setActiveUser():", e);
- }
- }
- }
-
- @Override
- protected void handleUserSwitching(int userId) {
- super.handleUserSwitching(userId);
- // Will be updated when we get the callback from HAL
- mLockoutTracker.setCurrentUserLockoutMode(LockoutTracker.LOCKOUT_NONE);
- }
-
- @Override
- protected boolean hasEnrolledBiometrics(int userId) {
- if (userId != UserHandle.getCallingUserId()) {
- checkPermission(INTERACT_ACROSS_USERS);
- }
- return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
- }
-
- @Override
- protected String getManageBiometricPermission() {
- return MANAGE_BIOMETRIC;
- }
-
- @Override
- protected void checkUseBiometricPermission() {
- // noop for Face. The permission checks are all done on the incoming binder call.
- }
-
- @Override
- protected boolean checkAppOps(int uid, String opPackageName) {
- return mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
- == AppOpsManager.MODE_ALLOWED;
- }
-
- @Override
- protected List<Face> getEnrolledTemplates(int userId) {
- return getBiometricUtils().getBiometricsForUser(getContext(), userId);
- }
-
- @Override
- protected void notifyClientActiveCallbacks(boolean isActive) {
- // noop for Face.
- }
-
- @Override
- protected int statsModality() {
- return BiometricsProtoEnums.MODALITY_FACE;
- }
-
- @Override
- protected @LockoutTracker.LockoutMode int getLockoutMode(int userId) {
- return mLockoutTracker.getLockoutModeForUser(userId);
- }
-
- /** Gets the face daemon */
- private synchronized IBiometricsFace getFaceDaemon() {
- if (mDaemon == null) {
- Slog.v(TAG, "mDaemon was null, reconnect to face");
- try {
- mDaemon = IBiometricsFace.getService();
- } catch (java.util.NoSuchElementException e) {
- // Service doesn't exist or cannot be opened. Logged below.
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get biometric interface", e);
- }
- if (mDaemon == null) {
- Slog.w(TAG, "face HIDL not available");
- return null;
- }
-
- mDaemon.asBinder().linkToDeath(this, 0);
-
- long halId = 0;
- try {
- halId = mDaemon.setCallback(mDaemonCallback).value;
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to open face HAL", e);
- mDaemon = null; // try again later!
- }
-
- if (DEBUG) Slog.v(TAG, "Face HAL id: " + halId);
- if (halId != 0) {
- loadAuthenticatorIds();
- updateActiveGroup(ActivityManager.getCurrentUser(), null);
- doTemplateCleanupForUser(ActivityManager.getCurrentUser());
- } else {
- Slog.w(TAG, "Failed to open Face HAL!");
- MetricsLogger.count(getContext(), "faced_openhal_error", 1);
- mDaemon = null;
- }
- }
- return mDaemon;
- }
-
- private long startGenerateChallenge(IBinder token) {
- IBiometricsFace daemon = getFaceDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startGenerateChallenge: no face HAL!");
- return 0;
- }
- try {
- return daemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
- } catch (RemoteException e) {
- Slog.e(TAG, "startGenerateChallenge failed", e);
- }
- return 0;
- }
-
- private int startRevokeChallenge(IBinder token) {
- IBiometricsFace daemon = getFaceDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startRevokeChallenge: no face HAL!");
- return 0;
- }
- try {
- final int res = daemon.revokeChallenge();
- if (res != Status.OK) {
- Slog.e(TAG, "revokeChallenge returned " + res);
- }
- return res;
- } catch (RemoteException e) {
- Slog.e(TAG, "startRevokeChallenge failed", e);
- }
- return 0;
}
private native NativeHandle convertSurfaceToNativeHandle(Surface surface);
-
- private void dumpInternal(PrintWriter pw) {
- PerformanceTracker performanceTracker =
- PerformanceTracker.getInstanceForSensorId(getSensorId());
-
- JSONObject dump = new JSONObject();
- try {
- dump.put("service", "Face Manager");
-
- JSONArray sets = new JSONArray();
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
- final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
- JSONObject set = new JSONObject();
- set.put("id", userId);
- set.put("count", N);
- set.put("accept", performanceTracker.getAcceptForUser(userId));
- set.put("reject", performanceTracker.getRejectForUser(userId));
- set.put("acquire", performanceTracker.getAcquireForUser(userId));
- set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
- set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
- // cryptoStats measures statistics about secure face transactions
- // (e.g. to unlock password storage, make secure purchases, etc.)
- set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
- set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
- set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
- sets.put(set);
- }
-
- dump.put("prints", sets);
- } catch (JSONException e) {
- Slog.e(TAG, "dump formatting failure", e);
- }
- pw.println(dump);
- pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
-
- mUsageStats.print(pw);
- }
-
- private void dumpHal(FileDescriptor fd, String[] args) {
- // WARNING: CDD restricts image data from leaving TEE unencrypted on
- // production devices:
- // [C-1-10] MUST not allow unencrypted access to identifiable biometric
- // data or any data derived from it (such as embeddings) to the
- // Application Processor outside the context of the TEE.
- // As such, this API should only be enabled for testing purposes on
- // engineering and userdebug builds. All modules in the software stack
- // MUST enforce final build products do NOT have this functionality.
- // Additionally, the following check MUST NOT be removed.
- if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
- return;
- }
-
- // Additionally, this flag allows turning off face for a device
- // (either permanently through the build or on an individual device).
- if (SystemProperties.getBoolean("ro.face.disable_debug_data", false)
- || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) {
- return;
- }
-
- // The debug method takes two file descriptors. The first is for text
- // output, which we will drop. The second is for binary data, which
- // will be the protobuf data.
- final IBiometricsFace daemon = getFaceDaemon();
- if (daemon != null) {
- FileOutputStream devnull = null;
- try {
- devnull = new FileOutputStream("/dev/null");
- final NativeHandle handle = new NativeHandle(
- new FileDescriptor[] { devnull.getFD(), fd },
- new int[0], false);
- daemon.debug(handle, new ArrayList<String>(Arrays.asList(args)));
- } catch (IOException | RemoteException ex) {
- Slog.d(TAG, "error while reading face debugging data", ex);
- } finally {
- if (devnull != null) {
- try {
- devnull.close();
- } catch (IOException ex) {
- }
- }
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
new file mode 100644
index 000000000000..e7d041a11ccb
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import java.util.ArrayList;
+
+/**
+ * Face-specific setFeature client supporting the {@link android.hardware.biometrics.face.V1_0}
+ * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ */
+public class FaceSetFeatureClient extends ClientMonitor<IBiometricsFace> {
+
+ private static final String TAG = "FaceSetFeatureClient";
+
+ private final int mFeature;
+ private final boolean mEnabled;
+ private final ArrayList<Byte> mHardwareAuthToken;
+ private final int mFaceId;
+
+ FaceSetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int sensorId, int feature, boolean enabled,
+ byte[] hardwareAuthToken, int faceId) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
+ mFeature = feature;
+ mEnabled = enabled;
+ mFaceId = faceId;
+
+ mHardwareAuthToken = new ArrayList<>();
+ for (byte b : hardwareAuthToken) {
+ mHardwareAuthToken.add(b);
+ }
+ }
+
+ @Override
+ public void unableToStart() {
+ try {
+ getListener().onFeatureSet(false /* success */, mFeature);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ final int result = getFreshDaemon()
+ .setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId);
+ getListener().onFeatureSet(result == Status.OK, mFeature);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java
new file mode 100644
index 000000000000..bcf304e47816
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+
+import java.io.File;
+import java.util.Map;
+
+public class FaceUpdateActiveUserClient extends ClientMonitor<IBiometricsFace> {
+ private static final String TAG = "FaceUpdateActiveUserClient";
+ private static final String FACE_DATA_DIR = "facedata";
+
+ private final int mCurrentUserId;
+ private final boolean mHasEnrolledBiometrics;
+ @NonNull private final Map<Integer, Long> mAuthenticatorIds;
+
+ FaceUpdateActiveUserClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
+ int sensorId, int currentUserId, boolean hasEnrolledBIometrics,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+ 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ mCurrentUserId = currentUserId;
+ mHasEnrolledBiometrics = hasEnrolledBIometrics;
+ mAuthenticatorIds = authenticatorIds;
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
+ if (mCurrentUserId == getTargetUserId()) {
+ Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
+ try {
+ mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
+ ? getFreshDaemon().getAuthenticatorId().value : 0L);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to refresh authenticatorId", e);
+ }
+ finishCallback.onClientFinished(this, true /* success */);
+ return;
+ }
+
+ startHalOperation();
+ }
+
+ @Override
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ protected void startHalOperation() {
+ final File storePath = new File(Environment.getDataVendorDeDirectory(getTargetUserId()),
+ FACE_DATA_DIR);
+ if (!storePath.exists()) {
+ Slog.e(TAG, "vold has not created the directory?");
+ mFinishCallback.onClientFinished(this, false /* success */);
+ return;
+ }
+
+ try {
+ getFreshDaemon().setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveUser: " + e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
new file mode 100644
index 000000000000..dad038626762
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
@@ -0,0 +1,664 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.TaskStackListener;
+import android.app.UserSwitchObserver;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.Surface;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
+import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
+import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.EnumerateConsumer;
+import com.android.server.biometrics.sensors.Interruptable;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.PerformanceTracker;
+import com.android.server.biometrics.sensors.RemovalConsumer;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or
+ * its extended minor versions.
+ */
+class Fingerprint21 implements IHwBinder.DeathRecipient {
+
+ private static final String TAG = "Fingerprint21";
+ private static final int ENROLL_TIMEOUT_SEC = 60;
+
+ private final Context mContext;
+ private final IActivityTaskManager mActivityTaskManager;
+ private final FingerprintSensorProperties mSensorProperties;
+ private final BiometricScheduler mScheduler;
+ private final Handler mHandler;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ private final LockoutFrameworkImpl mLockoutTracker;
+ private final BiometricTaskStackListener mTaskStackListener;
+ private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon;
+ private final Map<Integer, Long> mAuthenticatorIds;
+
+ @Nullable private IBiometricsFingerprint mDaemon;
+ @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+
+ private final class BiometricTaskStackListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AuthenticationClient)) {
+ Slog.e(TAG, "Task stack changed for client: " + client);
+ return;
+ }
+ if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+ return; // Keyguard is always allowed
+ }
+
+ try {
+ final List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (!topPackage.contentEquals(client.getOwnerString())
+ && !client.isAlreadyDone()) {
+ Slog.e(TAG, "Stopping background authentication, top: "
+ + topPackage + " currentClient: " + client);
+ mScheduler.cancelAuthentication(client.getToken());
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to get running tasks", e);
+ }
+ });
+ }
+ }
+
+ private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback =
+ new LockoutFrameworkImpl.LockoutResetCallback() {
+ @Override
+ public void onLockoutReset(int userId) {
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
+ }
+ };
+
+ private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(newUserId);
+ }
+ };
+
+ private final IBiometricsFingerprintClientCallback mDaemonCallback =
+ new IBiometricsFingerprintClientCallback.Stub() {
+ @Override
+ public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
+ mHandler.post(() -> {
+ final CharSequence name = FingerprintUtils.getInstance()
+ .getUniqueName(mContext, mCurrentUserId);
+ final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
+
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintEnrollClient)) {
+ Slog.e(TAG, "onEnrollResult for non-enroll client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
+ enrollClient.onEnrollResult(fingerprint, remaining);
+ });
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+ onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
+ }
+
+ @Override
+ public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AcquisitionClient)) {
+ Slog.e(TAG, "onAcquired for non-acquisition client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
+ acquisitionClient.onAcquired(acquiredInfo, vendorCode);
+ });
+ }
+
+ @Override
+ public void onAuthenticated(long deviceId, int fingerId, int groupId,
+ ArrayList<Byte> token) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AuthenticationConsumer)) {
+ Slog.e(TAG, "onAuthenticated for non-authentication consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final AuthenticationConsumer authenticationConsumer =
+ (AuthenticationConsumer) client;
+ final boolean authenticated = fingerId != 0;
+ final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+ authenticationConsumer.onAuthenticated(fp, authenticated, token);
+ });
+ }
+
+ @Override
+ public void onError(long deviceId, int error, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ Slog.d(TAG, "handleError"
+ + ", client: " + (client != null ? client.getOwnerString() : null)
+ + ", error: " + error
+ + ", vendorCode: " + vendorCode);
+ if (!(client instanceof Interruptable)) {
+ Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(client));
+ return;
+ }
+
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(error, vendorCode);
+
+ if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+ Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ });
+ }
+
+ @Override
+ public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
+ mHandler.post(() -> {
+ Slog.d(TAG, "Removed, fingerId: " + fingerId + ", remaining: " + remaining);
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof RemovalConsumer)) {
+ Slog.e(TAG, "onRemoved for non-removal consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+ final RemovalConsumer removalConsumer = (RemovalConsumer) client;
+ removalConsumer.onRemoved(fp, remaining);
+ });
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof EnumerateConsumer)) {
+ Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+ final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
+ enumerateConsumer.onEnumerationResult(fp, remaining);
+ });
+ }
+ };
+
+ Fingerprint21(@NonNull Context context, int sensorId,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ mContext = context;
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mHandler = new Handler(Looper.getMainLooper());
+ mTaskStackListener = new BiometricTaskStackListener();
+ mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>());
+ mLazyDaemon = Fingerprint21.this::getDaemon;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
+ mScheduler = new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
+
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register user switch observer");
+ }
+
+ final IBiometricsFingerprint daemon = getDaemon();
+ boolean isUdfps = false;
+ android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
+ android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
+ daemon);
+ if (extension != null) {
+ try {
+ isUdfps = extension.isUdfps(sensorId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception while quering udfps", e);
+ isUdfps = false;
+ }
+ }
+
+ final @FingerprintSensorProperties.SensorType int sensorType =
+ isUdfps ? FingerprintSensorProperties.TYPE_UDFPS
+ : FingerprintSensorProperties.TYPE_REAR;
+ mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType);
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.e(TAG, "HAL died");
+ mHandler.post(() -> {
+ PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId)
+ .incrementHALDeathCount();
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+
+ mScheduler.recordCrashState();
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ }
+ });
+ }
+
+ private synchronized IBiometricsFingerprint getDaemon() {
+ if (mDaemon != null) {
+ return mDaemon;
+ }
+
+ Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+ + mScheduler.getCurrentClient());
+ try {
+ mDaemon = IBiometricsFingerprint.getService();
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened.
+ Slog.w(TAG, "NoSuchElementException", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get fingerprint HAL", e);
+ }
+
+ if (mDaemon == null) {
+ Slog.w(TAG, "Fingerprint HAL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
+
+ // HAL ID for these HIDL versions are only used to determine if callbacks have been
+ // successfully set.
+ long halId = 0;
+ try {
+ halId = mDaemon.setNotify(mDaemonCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set callback for fingerprint HAL", e);
+ mDaemon = null;
+ }
+
+ Slog.d(TAG, "Fingerprint HAL ready, HAL ID: " + halId);
+ if (halId != 0) {
+ scheduleLoadAuthenticatorIds();
+ scheduleInternalCleanup(ActivityManager.getCurrentUser());
+ } else {
+ Slog.e(TAG, "Unable to set callback");
+ mDaemon = null;
+ }
+
+ return mDaemon;
+ }
+
+ @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
+ return mLockoutTracker.getLockoutModeForUser(userId);
+ }
+
+ private void scheduleLoadAuthenticatorIds() {
+ // Note that this can be performed on the scheduler (as opposed to being done immediately
+ // when the HAL is (re)loaded, since
+ // 1) If this is truly the first time it's being performed (e.g. system has just started),
+ // this will be run very early and way before any applications need to generate keys.
+ // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has
+ // just been reloaded), the framework already has a cache of the authenticatorIds. This
+ // is safe because authenticatorIds only change when A) new template has been enrolled,
+ // or B) all templates are removed.
+ mHandler.post(() -> {
+ for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
+ final int targetUserId = user.id;
+ if (!mAuthenticatorIds.containsKey(targetUserId)) {
+ scheduleUpdateActiveUserWithoutHandler(targetUserId);
+ }
+ }
+ });
+ }
+
+ /**
+ * Schedules the {@link FingerprintUpdateActiveUserClient} without posting the work onto the
+ * handler. Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
+ * invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
+ * this operation on the same lambda/runnable as those operations so that the ordering is
+ * correct.
+ */
+ private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
+ final boolean hasEnrolled = !getEnrolledFingerprints(targetUserId).isEmpty();
+ final FingerprintUpdateActiveUserClient client =
+ new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
+ mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
+ hasEnrolled, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, (clientMonitor, success) -> {
+ if (success) {
+ mCurrentUserId = targetUserId;
+ }
+ });
+ }
+
+ void scheduleResetLockout(int userId, byte[] hardwareAuthToken) {
+ // Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
+ // thread.
+ mHandler.post(() -> {
+ mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId);
+ });
+ }
+
+ void scheduleGenerateChallenge(@NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ final FingerprintGenerateChallengeClient client =
+ new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), opPackageName,
+ mSensorProperties.sensorId);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
+ mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleEnroll(@NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
+ @Nullable Surface surface) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(),
+ ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController);
+ mScheduler.scheduleClientMonitor(client, ((clientMonitor, success) -> {
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId());
+ }
+ }));
+ });
+ }
+
+ void cancelEnrollment(@NonNull IBinder token) {
+ mHandler.post(() -> {
+ mScheduler.cancelEnrollment(token);
+ });
+ }
+
+ void scheduleFingerDetect(@NonNull IBinder token, int userId,
+ @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
+ @Nullable Surface surface, int statsClient) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
+ final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
+ mLazyDaemon, token, listener, userId, opPackageName,
+ mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric,
+ statsClient);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId, int cookie,
+ @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
+ @Nullable Surface surface, boolean restricted, int statsClient) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
+ final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
+ mContext, mLazyDaemon, token, listener, userId, operationId, restricted,
+ opPackageName, cookie, false /* requireConfirmation */,
+ mSensorProperties.sensorId, isStrongBiometric, surface, statsClient,
+ mTaskStackListener, mLockoutTracker, mUdfpsOverlayController);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void startPreparedClient(int cookie) {
+ mHandler.post(() -> {
+ mScheduler.startPreparedClient(cookie);
+ });
+ }
+
+ void cancelAuthentication(@NonNull IBinder token) {
+ mHandler.post(() -> {
+ mScheduler.cancelAuthentication(token);
+ });
+ }
+
+ void scheduleRemove(@NonNull IBinder token, @NonNull IFingerprintServiceReceiver receiver,
+ int fingerId, int userId, @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
+ userId, opPackageName, FingerprintUtils.getInstance(),
+ mSensorProperties.sensorId, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ private void scheduleInternalCleanup(int userId) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
+ final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
+ mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
+ mSensorProperties.sensorId, enrolledList, FingerprintUtils.getInstance(),
+ mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ boolean isHardwareDetected() {
+ final IBiometricsFingerprint daemon = getDaemon();
+ return daemon != null;
+ }
+
+ @NonNull FingerprintSensorProperties getFingerprintSensorProperties() {
+ return mSensorProperties;
+ }
+
+ void rename(int fingerId, int userId, String name) {
+ mHandler.post(() -> {
+ FingerprintUtils.getInstance().renameBiometricForUser(mContext, userId, fingerId, name);
+ });
+ }
+
+ List<Fingerprint> getEnrolledFingerprints(int userId) {
+ return FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId);
+ }
+
+ long getAuthenticatorId(int userId) {
+ return mAuthenticatorIds.get(userId);
+ }
+
+ void onFingerDown(int x, int y, float minor, float major) {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof Udfps)) {
+ Slog.w(TAG, "onFingerDown received during client: " + client);
+ return;
+ }
+ final Udfps udfps = (Udfps) client;
+ udfps.onFingerDown(x, y, minor, major);
+ }
+
+ void onFingerUp() {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof Udfps)) {
+ Slog.w(TAG, "onFingerDown received during client: " + client);
+ return;
+ }
+ final Udfps udfps = (Udfps) client;
+ udfps.onFingerUp();
+ }
+
+ void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ mUdfpsOverlayController = controller;
+ }
+
+ void dumpProto(FileDescriptor fd) {
+ PerformanceTracker tracker =
+ PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
+
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ for (UserInfo user : UserManager.get(mContext).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+
+ final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
+
+ proto.write(FingerprintUserStatsProto.USER_ID, userId);
+ proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
+ FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId).size());
+
+ // Normal fingerprint authentications (e.g. lockscreen)
+ long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
+ proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptForUser(userId));
+ proto.write(PerformanceStatsProto.REJECT, tracker.getRejectForUser(userId));
+ proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireForUser(userId));
+ proto.write(PerformanceStatsProto.LOCKOUT, tracker.getTimedLockoutForUser(userId));
+ proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT,
+ tracker.getPermanentLockoutForUser(userId));
+ proto.end(countsToken);
+
+ // Statistics about secure fingerprint transactions (e.g. to unlock password
+ // storage, make secure purchases, etc.)
+ countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
+ proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptCryptoForUser(userId));
+ proto.write(PerformanceStatsProto.REJECT, tracker.getRejectCryptoForUser(userId));
+ proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireCryptoForUser(userId));
+ proto.write(PerformanceStatsProto.LOCKOUT, 0); // meaningless for crypto
+ proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, 0); // meaningless for crypto
+ proto.end(countsToken);
+
+ proto.end(userToken);
+ }
+ proto.flush();
+ tracker.clear();
+ }
+
+ void dumpInternal(@NonNull PrintWriter pw) {
+ PerformanceTracker performanceTracker =
+ PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
+
+ JSONObject dump = new JSONObject();
+ try {
+ dump.put("service", "Fingerprint Manager");
+
+ JSONArray sets = new JSONArray();
+ for (UserInfo user : UserManager.get(mContext).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ final int N = FingerprintUtils.getInstance()
+ .getBiometricsForUser(mContext, userId).size();
+ JSONObject set = new JSONObject();
+ set.put("id", userId);
+ set.put("count", N);
+ set.put("accept", performanceTracker.getAcceptForUser(userId));
+ set.put("reject", performanceTracker.getRejectForUser(userId));
+ set.put("acquire", performanceTracker.getAcquireForUser(userId));
+ set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
+ set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
+ // cryptoStats measures statistics about secure fingerprint transactions
+ // (e.g. to unlock password storage, make secure purchases, etc.)
+ set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
+ set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
+ set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
+ sets.put(set);
+ }
+
+ dump.put("prints", sets);
+ } catch (JSONException e) {
+ Slog.e(TAG, "dump formatting failure", e);
+ }
+ pw.println(dump);
+ pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
+ mScheduler.dump(pw);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
index dff56af770da..1564056cfdbd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
@@ -16,17 +16,22 @@
package com.android.server.biometrics.sensors.fingerprint;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.TaskStackListener;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Slog;
import android.view.Surface;
import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.BiometricServiceBase;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -37,50 +42,60 @@ import java.util.ArrayList;
* {@link android.hardware.biometrics.fingerprint.V2_1} and
* {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
*/
-class FingerprintAuthenticationClient extends AuthenticationClient {
+class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFingerprint>
+ implements Udfps {
private static final String TAG = "Biometrics/FingerprintAuthClient";
private final LockoutFrameworkImpl mLockoutFrameworkImpl;
+ @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
- FingerprintAuthenticationClient(Context context, BiometricServiceBase.DaemonWrapper daemon,
- IBinder token, ClientMonitorCallbackConverter listener, int targetUserId, int groupId,
- long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation,
- int sensorId, boolean isStrongBiometric, Surface surface, int statsClient,
- TaskStackListener taskStackListener, LockoutFrameworkImpl lockoutTracker) {
- super(context, daemon, token, listener, targetUserId, groupId, opId,
- restricted, owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
+ FingerprintAuthenticationClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
+ boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
+ int sensorId, boolean isStrongBiometric, @Nullable Surface surface, int statsClient,
+ @NonNull TaskStackListener taskStackListener,
+ @NonNull LockoutFrameworkImpl lockoutTracker,
+ @Nullable IUdfpsOverlayController udfpsOverlayController) {
+ super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
+ owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
- lockoutTracker, surface);
-
+ lockoutTracker);
mLockoutFrameworkImpl = lockoutTracker;
+ mUdfpsOverlayController = udfpsOverlayController;
}
@Override
- public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+ public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {
- final boolean result = super.onAuthenticated(identifier, authenticated, token);
+ super.onAuthenticated(identifier, authenticated, token);
+
+ // Authentication lifecycle ends either when
+ // 1) Authenticated == true
+ // 2) Error occurred (lockout or some other error)
+ // Note that authentication doesn't end when Authenticated == false
if (authenticated) {
resetFailedAttempts(getTargetUserId());
+ UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ mFinishCallback.onClientFinished(this, true /* success */);
} else {
final @LockoutTracker.LockoutMode int lockoutMode =
mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
Slog.w(TAG, "Fingerprint locked out, lockoutMode(" + lockoutMode + ")");
- stop(false /* initiatedByClient */);
final int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
: BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
- onError(errorCode, 0 /* vendorCode */);
+ // Send the error, but do not invoke the FinishCallback yet. Since lockout is not
+ // controlled by the HAL, the framework must stop the sensor before finishing the
+ // client.
+ UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
+ cancel();
}
}
-
- // Authentication lifecycle ends either when
- // 1) Authenticated == true
- // 2) Error occurred
- // Note that authentication doesn't end when Authenticated == false
- return result;
}
private void resetFailedAttempts(int userId) {
@@ -92,4 +107,42 @@ class FingerprintAuthenticationClient extends AuthenticationClient {
mLockoutFrameworkImpl.addFailedAttemptForUser(userId);
return super.handleFailedAttempt(userId);
}
+
+ @Override
+ protected void startHalOperation() {
+ UdfpsHelper.showUdfpsOverlay(mUdfpsOverlayController);
+ try {
+ // GroupId was never used. In fact, groupId is always the same as userId.
+ getFreshDaemon().authenticate(mOperationId, getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting auth", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ protected void stopHalOperation() {
+ UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ try {
+ getFreshDaemon().cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ public void onFingerDown(int x, int y, float minor, float major) {
+ UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
+ }
+
+ @Override
+ public void onFingerUp() {
+ UdfpsHelper.onFingerUp(getFreshDaemon());
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index abfbedaa4489..3418c466aa69 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -23,6 +23,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import com.android.server.biometrics.SensorConfig;
+import com.android.server.biometrics.sensors.LockoutTracker;
/**
* Shim that converts IFingerprintService into a common reusable IBiometricAuthenticator interface.
@@ -68,6 +69,12 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
}
@Override
+ public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId)
+ throws RemoteException {
+ return mFingerprintService.getLockoutModeForUser(userId);
+ }
+
+ @Override
public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
mFingerprintService.resetLockout(userId, hardwareAuthToken);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java
new file mode 100644
index 000000000000..8b295f8f4931
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.PerformanceTracker;
+
+import java.util.ArrayList;
+
+/**
+ * Performs fingerprint detection without exposing any matching information (e.g. accept/reject
+ * have the same haptic, lockout counter is not increased).
+ */
+class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
+ implements AuthenticationConsumer, Udfps {
+
+ private static final String TAG = "FingerprintDetectClient";
+
+ private final boolean mIsStrongBiometric;
+ @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+
+ public FingerprintDetectClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
+ int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController,
+ boolean isStrongBiometric, int statsClient) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE,
+ statsClient);
+ mUdfpsOverlayController = udfpsOverlayController;
+ mIsStrongBiometric = isStrongBiometric;
+ }
+
+ @Override
+ protected void stopHalOperation() {
+ UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ try {
+ getFreshDaemon().cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ UdfpsHelper.showUdfpsOverlay(mUdfpsOverlayController);
+ try {
+ getFreshDaemon().authenticate(0 /* operationId */, getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting auth", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ public void onFingerDown(int x, int y, float minor, float major) {
+ UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
+ }
+
+ @Override
+ public void onFingerUp() {
+ UdfpsHelper.onFingerUp(getFreshDaemon());
+ }
+
+ @Override
+ public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated,
+ ArrayList<Byte> hardwareAuthToken) {
+ logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */,
+ getTargetUserId(), false /* isBiometricPrompt */);
+
+ // Do not distinguish between success/failures.
+ vibrateSuccess();
+
+ final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
+ pm.incrementAuthForUser(getTargetUserId(), authenticated);
+
+ try {
+ getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when sending onDetected", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
new file mode 100644
index 000000000000..32f8b8fe8b26
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.EnrollClient;
+
+/**
+ * Fingerprint-specific enroll client supporting the
+ * {@link android.hardware.biometrics.fingerprint.V2_1} and
+ * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
+ */
+public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint>
+ implements Udfps {
+
+ private static final String TAG = "FingerprintEnrollClient";
+
+ @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+
+ FingerprintEnrollClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
+ int timeoutSec, int sensorId,
+ @Nullable IUdfpsOverlayController udfpsOverlayController) {
+ super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+ timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
+ true /* shouldVibrate */);
+ mUdfpsOverlayController = udfpsOverlayController;
+ }
+
+ @Override
+ protected boolean hasReachedEnrollmentLimit() {
+ final int limit = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
+ final int enrolled = mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId())
+ .size();
+ if (enrolled >= limit) {
+ Slog.w(TAG, "Too many faces registered, user: " + getTargetUserId());
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void startHalOperation() {
+ UdfpsHelper.showUdfpsOverlay(mUdfpsOverlayController);
+ try {
+ // GroupId was never used. In fact, groupId is always the same as userId.
+ getFreshDaemon().enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting enroll", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ protected void stopHalOperation() {
+ UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ try {
+ getFreshDaemon().cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ public void onFingerDown(int x, int y, float minor, float major) {
+ UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
+ }
+
+ @Override
+ public void onFingerUp() {
+ UdfpsHelper.onFingerUp(getFreshDaemon());
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
new file mode 100644
index 000000000000..8fb8c992268d
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.GenerateChallengeClient;
+
+/**
+ * Fingerprint-specific generateChallenge/preEnroll client supporting the
+ * {@link android.hardware.biometrics.fingerprint.V2_1} and
+ * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
+ */
+public class FingerprintGenerateChallengeClient
+ extends GenerateChallengeClient<IBiometricsFingerprint> {
+
+ private static final String TAG = "FingerprintGenerateChallengeClient";
+
+ FingerprintGenerateChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, listener, owner, sensorId);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ mChallenge = getFreshDaemon().preEnroll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "preEnroll failed", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
new file mode 100644
index 000000000000..571d2b80fd3e
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalCleanupClient;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+import com.android.server.biometrics.sensors.RemovalClient;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Fingerprint-specific internal cleanup client supporting the
+ * {@link android.hardware.biometrics.fingerprint.V2_1} and
+ * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
+ */
+class FingerprintInternalCleanupClient
+ extends InternalCleanupClient<Fingerprint, IBiometricsFingerprint> {
+
+ FingerprintInternalCleanupClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
+ @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList,
+ @NonNull BiometricUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
+ }
+
+ @Override
+ protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
+ Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
+ int userId, String owner,
+ List<Fingerprint> enrolledList, BiometricUtils utils,
+ int sensorId) {
+ return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+ enrolledList, utils, sensorId);
+ }
+
+ @Override
+ protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context,
+ LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
+ int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+ Map<Integer, Long> authenticatorIds) {
+ // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
+ // is all done internally.
+ return new FingerprintRemovalClient(context, lazyDaemon, token,
+ null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+ sensorId, authenticatorIds);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
new file mode 100644
index 000000000000..240c3c56f75e
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+
+import java.util.List;
+
+/**
+ * Fingerprint-specific internal enumerate client supporting the
+ * {@link android.hardware.biometrics.fingerprint.V2_1} and
+ * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
+ */
+class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFingerprint> {
+ private static final String TAG = "FingerprintInternalEnumerateClient";
+
+ FingerprintInternalEnumerateClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
+ @NonNull BiometricUtils utils, int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().enumerate();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting enumerate", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
new file mode 100644
index 000000000000..a9336ef6a6c2
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.RemovalClient;
+
+import java.util.Map;
+
+/**
+ * Fingerprint-specific removal client supporting the
+ * {@link android.hardware.biometrics.fingerprint.V2_1} and
+ * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
+ */
+class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> {
+ private static final String TAG = "FingerprintRemovalClient";
+
+ FingerprintRemovalClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
+ @NonNull String owner, @NonNull BiometricUtils utils, int sensorId,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ // GroupId was never used. In fact, groupId is always the same as userId.
+ getFreshDaemon().remove(getTargetUserId(), mBiometricId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting remove", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
new file mode 100644
index 000000000000..882660e7a618
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.RevokeChallengeClient;
+
+/**
+ * Fingerprint-specific revokeChallenge client supporting the
+ * {@link android.hardware.biometrics.fingerprint.V2_1} and
+ * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
+ */
+public class FingerprintRevokeChallengeClient
+ extends RevokeChallengeClient<IBiometricsFingerprint> {
+
+ private static final String TAG = "FingerprintRevokeChallengeClient";
+
+ FingerprintRevokeChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, owner, sensorId);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().postEnroll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "revokeChallenge/postEnroll failed", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 889480702e19..e4387c9e2b81 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -24,207 +24,238 @@ import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
-import android.app.ActivityManager;
+import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
-import android.os.Build;
-import android.os.Environment;
import android.os.IBinder;
import android.os.NativeHandle;
-import android.os.RemoteException;
-import android.os.SELinux;
+import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManager;
+import android.util.EventLog;
import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
import android.view.Surface;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
-import com.android.server.SystemServerInitThreadPool;
-import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
-import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
-import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
-import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.BiometricServiceBase;
-import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.SystemService;
+import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.PerformanceTracker;
-import com.android.server.biometrics.sensors.RemovalClient;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
* The service is responsible for maintaining a list of clients and dispatching all
* fingerprint-related events.
- *
- * @hide
*/
-public class FingerprintService extends BiometricServiceBase {
+public class FingerprintService extends SystemService {
protected static final String TAG = "FingerprintService";
- private static final boolean DEBUG = true;
- private static final String FP_DATA_DIR = "fpdata";
+
+ private final AppOpsManager mAppOps;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
+ private final LockPatternUtils mLockPatternUtils;
+ private Fingerprint21 mFingerprint21;
/**
* Receives the incoming binder calls from FingerprintManager.
*/
private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
- private static final int ENROLL_TIMEOUT_SEC = 60;
+ @Override // Binder call
+ public List<FingerprintSensorProperties> getSensorProperties(String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ final List<FingerprintSensorProperties> properties = new ArrayList<>();
+
+ if (mFingerprint21 != null) {
+ properties.add(mFingerprint21.getFingerprintSensorProperties());
+ }
- /**
- * The following methods contain common code which is shared in biometrics/common.
- */
+ Slog.d(TAG, "Retrieved sensor properties for: " + opPackageName
+ + ", sensors: " + properties.size());
+ return properties;
+ }
@Override // Binder call
- public long preEnroll(IBinder token) {
- checkPermission(MANAGE_FINGERPRINT);
- return startPreEnroll(token);
+ public void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver,
+ String opPackageName) {
+ Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+ mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName);
}
@Override // Binder call
- public int postEnroll(IBinder token) {
- checkPermission(MANAGE_FINGERPRINT);
- return startPostEnroll(token);
+ public void revokeChallenge(IBinder token, String owner) {
+ Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+ mFingerprint21.scheduleRevokeChallenge(token, owner);
}
@Override // Binder call
- public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
- final IFingerprintServiceReceiver receiver, final int flags,
- final String opPackageName, Surface surface) {
- checkPermission(MANAGE_FINGERPRINT);
- updateActiveGroup(userId, opPackageName);
- final boolean restricted = isRestricted();
- final int groupId = userId; // default group for fingerprint enrollment
- final EnrollClient client = new EnrollClient(getContext(),
- mDaemonWrapper, token, new ClientMonitorCallbackConverter(receiver),
- mCurrentUserId, groupId, cryptoToken, restricted, opPackageName,
- getBiometricUtils(), new int[0] /* disabledFeatures */, ENROLL_TIMEOUT_SEC,
- statsModality(), surface, getSensorId(), true /* shouldVibrate */);
-
- enrollInternal(client, userId);
+ public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId,
+ final IFingerprintServiceReceiver receiver, final String opPackageName,
+ Surface surface) {
+ Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+ mFingerprint21.scheduleEnroll(token, hardwareAuthToken, userId, receiver, opPackageName,
+ surface);
}
@Override // Binder call
public void cancelEnrollment(final IBinder token) {
- checkPermission(MANAGE_FINGERPRINT);
- cancelEnrollmentInternal(token);
+ Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+ mFingerprint21.cancelEnrollment(token);
}
@Override // Binder call
- public void authenticate(final IBinder token, final long opId, final int groupId,
- final IFingerprintServiceReceiver receiver, final int flags,
- final String opPackageName, Surface surface) {
- updateActiveGroup(groupId, opPackageName);
- final boolean restricted = isRestricted();
- final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
+ public void authenticate(final IBinder token, final long operationId, final int userId,
+ final IFingerprintServiceReceiver receiver, final String opPackageName,
+ final Surface surface) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (!canUseFingerprint(opPackageName, true /* requireForeground */, callingUid,
+ callingPid, callingUserId)) {
+ Slog.w(TAG, "Authenticate rejecting package: " + opPackageName);
+ return;
+ }
+
+ // Keyguard check must be done on the caller's binder identity, since it also checks
+ // permission.
+ final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
+
+ // Clear calling identity when checking LockPatternUtils for StrongAuth flags.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
+ // If this happens, something in KeyguardUpdateMonitor is wrong.
+ // SafetyNet for b/79776455
+ EventLog.writeEvent(0x534e4554, "79776455");
+ Slog.e(TAG, "Authenticate invoked when user is encrypted or lockdown");
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ final boolean restricted = getContext().checkCallingPermission(MANAGE_FINGERPRINT)
+ != PackageManager.PERMISSION_GRANTED;
+ final int statsClient = isKeyguard ? BiometricsProtoEnums.CLIENT_KEYGUARD
: BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
+ mFingerprint21.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */,
+ new ClientMonitorCallbackConverter(receiver), opPackageName, surface,
+ restricted, statsClient);
+ }
- final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
- mDaemonWrapper, token,
- new ClientMonitorCallbackConverter(receiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName, 0 /* cookie */,
- false /* requireConfirmation */, getSensorId(), isStrongBiometric(), surface,
- statsClient, mTaskStackListener, mLockoutTracker);
- authenticateInternal(client, opPackageName);
+ @Override
+ public void detectFingerprint(final IBinder token, final int userId,
+ final IFingerprintServiceReceiver receiver, final String opPackageName,
+ final Surface surface) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ if (!Utils.isKeyguard(getContext(), opPackageName)) {
+ Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName);
+ return;
+ }
+
+ if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
+ // If this happens, something in KeyguardUpdateMonitor is wrong. This should only
+ // ever be invoked when the user is encrypted or lockdown.
+ Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
+ return;
+ }
+
+ mFingerprint21.scheduleFingerDetect(token, userId,
+ new ClientMonitorCallbackConverter(receiver), opPackageName, surface,
+ BiometricsProtoEnums.CLIENT_KEYGUARD);
}
@Override // Binder call
- public void prepareForAuthentication(IBinder token, long opId, int groupId,
+ public void prepareForAuthentication(IBinder token, long operationId, int userId,
IBiometricSensorReceiver sensorReceiver, String opPackageName,
int cookie, int callingUid, int callingPid, int callingUserId,
Surface surface) {
- checkPermission(MANAGE_BIOMETRIC);
- updateActiveGroup(groupId, opPackageName);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+
final boolean restricted = true; // BiometricPrompt is always restricted
- final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
- mDaemonWrapper, token,
- new ClientMonitorCallbackConverter(sensorReceiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
- false /* requireConfirmation */, getSensorId(), isStrongBiometric(), surface,
- BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
- mLockoutTracker);
- authenticateInternal(client, opPackageName, callingUid, callingPid,
- callingUserId);
+ mFingerprint21.scheduleAuthenticate(token, operationId, userId, cookie,
+ new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, surface,
+ restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT);
}
@Override // Binder call
public void startPreparedClient(int cookie) {
- checkPermission(MANAGE_BIOMETRIC);
- startCurrentClient(cookie);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFingerprint21.startPreparedClient(cookie);
}
@Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
- cancelAuthenticationInternal(token, opPackageName);
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (!canUseFingerprint(opPackageName, true /* requireForeground */, callingUid,
+ callingPid, callingUserId)) {
+ Slog.w(TAG, "cancelAuthentication rejecting package: " + opPackageName);
+ return;
+ }
+
+ mFingerprint21.cancelAuthentication(token);
+ }
+
+ @Override // Binder call
+ public void cancelFingerprintDetect(final IBinder token, final String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ if (!Utils.isKeyguard(getContext(), opPackageName)) {
+ Slog.w(TAG, "cancelFingerprintDetect called from non-sysui package: "
+ + opPackageName);
+ return;
+ }
+
+ // For IBiometricsFingerprint2.1, cancelling fingerprint detect is the same as
+ // cancelling authentication.
+ mFingerprint21.cancelAuthentication(token);
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
int callingUid, int callingPid, int callingUserId) {
- checkPermission(MANAGE_BIOMETRIC);
- // Cancellation is from system server in this case.
- cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
- callingUserId, false /* fromClient */);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFingerprint21.cancelAuthentication(token);
}
@Override // Binder call
- public void remove(final IBinder token, final int fingerId, final int groupId,
- final int userId, final IFingerprintServiceReceiver receiver,
- final String opPackageName) {
- checkPermission(MANAGE_FINGERPRINT);
- updateActiveGroup(userId, opPackageName);
-
- if (token == null) {
- Slog.w(TAG, "remove(): token is null");
- return;
- }
-
- final boolean restricted = isRestricted();
- final RemovalClient client = new RemovalClient(getContext(),
- mDaemonWrapper, token, new ClientMonitorCallbackConverter(receiver),
- fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils(),
- getSensorId(), statsModality());
- removeInternal(client);
+ public void remove(final IBinder token, final int fingerId, final int userId,
+ final IFingerprintServiceReceiver receiver, final String opPackageName) {
+ Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+ mFingerprint21.scheduleRemove(token, receiver, fingerId, userId, opPackageName);
}
@Override
- public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
- throws RemoteException {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- FingerprintService.super.addLockoutResetCallback(callback);
+ public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
+ final String opPackageName) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mLockoutResetDispatcher.addCallback(callback, opPackageName);
}
@Override // Binder call
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
}
@@ -232,23 +263,18 @@ public class FingerprintService extends BiometricServiceBase {
final long ident = Binder.clearCallingIdentity();
try {
if (args.length > 0 && "--proto".equals(args[0])) {
- dumpProto(fd);
+ mFingerprint21.dumpProto(fd);
} else {
- dumpInternal(pw);
+ mFingerprint21.dumpInternal(pw);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- /**
- * The following methods don't use any common code from BiometricService
- */
-
- // TODO: refactor out common code here
@Override // Binder call
public boolean isHardwareDetected(String opPackageName) {
- if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.getCallingUserId())) {
return false;
@@ -256,8 +282,11 @@ public class FingerprintService extends BiometricServiceBase {
final long token = Binder.clearCallingIdentity();
try {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- return daemon != null;
+ if (mFingerprint21 == null) {
+ Slog.e(TAG, "No HAL, caller: " + opPackageName);
+ return false;
+ }
+ return mFingerprint21.isHardwareDetected();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -265,351 +294,150 @@ public class FingerprintService extends BiometricServiceBase {
@Override // Binder call
public void rename(final int fingerId, final int userId, final String name) {
- checkPermission(MANAGE_FINGERPRINT);
- if (!isCurrentUserOrProfile(userId)) {
+ Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+ if (!Utils.isCurrentUserOrProfile(getContext(), userId)) {
return;
}
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- getBiometricUtils().renameBiometricForUser(getContext(), userId, fingerId,
- name);
- }
- });
+
+ mFingerprint21.rename(fingerId, userId, name);
}
@Override // Binder call
public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
- if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.getCallingUserId())) {
return Collections.emptyList();
}
- return FingerprintService.this.getEnrolledTemplates(userId);
+ if (userId != UserHandle.getCallingUserId()) {
+ Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
+ }
+ return mFingerprint21.getEnrolledFingerprints(userId);
}
@Override // Binder call
public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
- if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.getCallingUserId())) {
return false;
}
- return FingerprintService.this.hasEnrolledBiometrics(userId);
+ if (userId != UserHandle.getCallingUserId()) {
+ Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
+ }
+ return mFingerprint21.getEnrolledFingerprints(userId).size() > 0;
}
@Override // Binder call
- public long getAuthenticatorId(int callingUserId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- return FingerprintService.this.getAuthenticatorId(callingUserId);
+ public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return mFingerprint21.getLockoutModeForUser(userId);
}
@Override // Binder call
- public void resetLockout(int userId, byte [] hardwareAuthToken) throws RemoteException {
- checkPermission(RESET_FINGERPRINT_LOCKOUT);
- if (!FingerprintService.this.hasEnrolledBiometrics(userId)) {
- Slog.w(TAG, "Ignoring lockout reset, no templates enrolled for user: " + userId);
- return;
- }
+ public long getAuthenticatorId(int userId) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return mFingerprint21.getAuthenticatorId(userId);
+ }
- // TODO: confirm security token when we move timeout management into the HAL layer.
- mHandler.post(() -> {
- mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId);
- });
+ @Override // Binder call
+ public void resetLockout(int userId, byte [] hardwareAuthToken) {
+ Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT);
+ mFingerprint21.scheduleResetLockout(userId, hardwareAuthToken);
}
@Override
public boolean isClientActive() {
- checkPermission(MANAGE_FINGERPRINT);
- synchronized(FingerprintService.this) {
- return (getCurrentClient() != null) || (getPendingClient() != null);
- }
+ Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+ return mGestureAvailabilityDispatcher.isAnySensorActive();
}
@Override
public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
- checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.add(callback);
+ Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+ mGestureAvailabilityDispatcher.registerCallback(callback);
}
@Override
public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
- checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.remove(callback);
+ Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+ mGestureAvailabilityDispatcher.removeCallback(callback);
}
@Override // Binder call
public void initializeConfiguration(int sensorId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- initializeConfigurationInternal(sensorId);
- }
- }
-
- private final LockoutFrameworkImpl mLockoutTracker;
- private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
- new CopyOnWriteArrayList<>();
-
- @GuardedBy("this")
- private IBiometricsFingerprint mDaemon;
-
- private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback = userId -> {
- notifyLockoutResetMonitors();
- };
-
- /**
- * Receives callbacks from the HAL.
- */
- private IBiometricsFingerprintClientCallback mDaemonCallback =
- new IBiometricsFingerprintClientCallback.Stub() {
- @Override
- public void onEnrollResult(final long deviceId, final int fingerId, final int groupId,
- final int remaining) {
- mHandler.post(() -> {
- final Fingerprint fingerprint =
- new Fingerprint(getBiometricUtils().getUniqueName(getContext(), groupId),
- groupId, fingerId, deviceId);
- FingerprintService.super.handleEnrollResult(fingerprint, remaining);
- });
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFingerprint21 = new Fingerprint21(getContext(), sensorId, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher);
}
@Override
- public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
- onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
+ public void onFingerDown(int x, int y, float minor, float major) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFingerprint21.onFingerDown(x, y, minor, major);
}
@Override
- public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
- mHandler.post(() -> {
- FingerprintService.super.handleAcquired(acquiredInfo, vendorCode);
- });
+ public void onFingerUp() {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFingerprint21.onFingerUp();
}
@Override
- public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
- ArrayList<Byte> token) {
- mHandler.post(() -> {
- Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
- FingerprintService.super.handleAuthenticated(fp, token);
- });
+ public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFingerprint21.setUdfpsOverlayController(controller);
}
-
- @Override
- public void onError(final long deviceId, final int error, final int vendorCode) {
- mHandler.post(() -> {
- FingerprintService.super.handleError(error, vendorCode);
- // TODO: this chunk of code should be common to all biometric services
- if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
- // If we get HW_UNAVAILABLE, try to connect again later...
- Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
- synchronized (this) {
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
- }
- }
- });
- }
-
- @Override
- public void onRemoved(final long deviceId, final int fingerId, final int groupId,
- final int remaining) {
- mHandler.post(() -> {
- final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
- FingerprintService.super.handleRemoved(fp, remaining);
- });
- }
-
- @Override
- public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
- final int remaining) {
- mHandler.post(() -> {
- final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
- FingerprintService.super.handleEnumerate(fp, remaining);
- });
-
- }
- };
-
- /**
- * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
- * can be shared between the multiple biometric services.
- */
- private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
- @Override
- public int authenticate(long operationId, int groupId, Surface surface)
- throws RemoteException {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "authenticate(): no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- return daemon.authenticate(operationId, groupId);
- }
-
- @Override
- public int cancel() throws RemoteException {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "cancel(): no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- return daemon.cancel();
- }
-
- @Override
- public int remove(int groupId, int biometricId) throws RemoteException {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "remove(): no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- return daemon.remove(groupId, biometricId);
- }
-
- @Override
- public int enumerate() throws RemoteException {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "enumerate(): no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- return daemon.enumerate();
- }
-
- @Override
- public int enroll(byte[] cryptoToken, int groupId, int timeout,
- ArrayList<Integer> disabledFeatures, Surface surface) throws RemoteException {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "enroll(): no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- return daemon.enroll(cryptoToken, groupId, timeout);
- }
-
- @Override
- public void resetLockout(byte[] token) throws RemoteException {
- // TODO: confirm security token when we move timeout management into the HAL layer.
- Slog.e(TAG, "Not supported");
- return;
- }
- };
+ }
public FingerprintService(Context context) {
super(context);
- mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher();
+ mLockoutResetDispatcher = new LockoutResetDispatcher(context);
+ mLockPatternUtils = new LockPatternUtils(context);
}
@Override
public void onStart() {
- super.onStart();
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
- SystemServerInitThreadPool.submit(this::getFingerprintDaemon, TAG + ".onStart");
- }
-
- @Override
- protected String getTag() {
- return TAG;
}
- @Override
- protected DaemonWrapper getDaemonWrapper() {
- return mDaemonWrapper;
- }
-
- @Override
- protected BiometricUtils getBiometricUtils() {
- return FingerprintUtils.getInstance();
- }
+ /**
+ * Checks for public API invocations to ensure that permissions, etc are granted/correct.
+ */
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
+ int pid, int userId) {
+ if (getContext().checkCallingPermission(USE_FINGERPRINT)
+ != PackageManager.PERMISSION_GRANTED) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC);
+ }
- @Override
- protected boolean hasReachedEnrollmentLimit(int userId) {
- final int limit = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
- final int enrolled = FingerprintService.this.getEnrolledTemplates(userId).size();
- if (enrolled >= limit) {
- Slog.w(TAG, "Too many fingerprints registered");
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ return true; // System process (BiometricService, etc) is always allowed
+ }
+ if (Utils.isKeyguard(getContext(), opPackageName)) {
return true;
}
- return false;
- }
-
- @Override
- public void serviceDied(long cookie) {
- super.serviceDied(cookie);
- mDaemon = null;
- }
-
- @Override
- protected void updateActiveGroup(int userId, String clientPackage) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
-
- if (daemon != null) {
- try {
- userId = getUserOrWorkProfileId(clientPackage, userId);
- if (userId != mCurrentUserId) {
- int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
- if (firstSdkInt < Build.VERSION_CODES.BASE) {
- Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
- "at least VERSION_CODES.BASE");
- }
- File baseDir;
- if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
- baseDir = Environment.getUserSystemDirectory(userId);
- } else {
- baseDir = Environment.getDataVendorDeDirectory(userId);
- }
-
- File fpDir = new File(baseDir, FP_DATA_DIR);
- if (!fpDir.exists()) {
- if (!fpDir.mkdir()) {
- Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
- return;
- }
- // Calling mkdir() from this process will create a directory with our
- // permissions (inherited from the containing dir). This command fixes
- // the label.
- if (!SELinux.restorecon(fpDir)) {
- Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
- return;
- }
- }
-
- daemon.setActiveGroup(userId, fpDir.getAbsolutePath());
- mCurrentUserId = userId;
- }
- mAuthenticatorIds.put(userId,
- hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId() : 0L);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setActiveGroup():", e);
- }
+ if (!Utils.isCurrentUserOrProfile(getContext(), userId)) {
+ Slog.w(TAG, "Rejecting " + opPackageName + "; not a current user or profile");
+ return false;
}
- }
-
- @Override
- protected boolean hasEnrolledBiometrics(int userId) {
- if (userId != UserHandle.getCallingUserId()) {
- checkPermission(INTERACT_ACROSS_USERS);
+ if (!checkAppOps(uid, opPackageName)) {
+ Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
+ return false;
}
- return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
- }
-
- @Override
- protected String getManageBiometricPermission() {
- return MANAGE_FINGERPRINT;
- }
-
- @Override
- protected void checkUseBiometricPermission() {
- if (getContext().checkCallingPermission(USE_FINGERPRINT)
- != PackageManager.PERMISSION_GRANTED) {
- checkPermission(USE_BIOMETRIC);
+ if (requireForeground && !Utils.isForeground(uid, pid)) {
+ Slog.w(TAG, "Rejecting " + opPackageName + "; not in foreground");
+ return false;
}
+ return true;
}
- @Override
- protected boolean checkAppOps(int uid, String opPackageName) {
+ private boolean checkAppOps(int uid, String opPackageName) {
boolean appOpsOk = false;
if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
== AppOpsManager.MODE_ALLOWED) {
@@ -621,180 +449,5 @@ public class FingerprintService extends BiometricServiceBase {
return appOpsOk;
}
- @Override
- protected List<Fingerprint> getEnrolledTemplates(int userId) {
- if (userId != UserHandle.getCallingUserId()) {
- checkPermission(INTERACT_ACROSS_USERS);
- }
- return getBiometricUtils().getBiometricsForUser(getContext(), userId);
- }
-
- @Override
- protected void notifyClientActiveCallbacks(boolean isActive) {
- List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
- for (int i = 0; i < callbacks.size(); i++) {
- try {
- callbacks.get(i).onClientActiveChanged(isActive);
- } catch (RemoteException re) {
- // If the remote is dead, stop notifying it
- mClientActiveCallbacks.remove(callbacks.get(i));
- }
- }
- }
-
- @Override
- protected int statsModality() {
- return BiometricsProtoEnums.MODALITY_FINGERPRINT;
- }
-
- @Override
- protected @LockoutTracker.LockoutMode int getLockoutMode(int userId) {
- return mLockoutTracker.getLockoutModeForUser(userId);
- }
-
- /** Gets the fingerprint daemon */
- private synchronized IBiometricsFingerprint getFingerprintDaemon() {
- if (mDaemon == null) {
- Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
- try {
- mDaemon = IBiometricsFingerprint.getService();
- } catch (java.util.NoSuchElementException e) {
- // Service doesn't exist or cannot be opened. Logged below.
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get biometric interface", e);
- }
- if (mDaemon == null) {
- Slog.w(TAG, "fingerprint HIDL not available");
- return null;
- }
-
- mDaemon.asBinder().linkToDeath(this, 0);
-
- long halId = 0;
- try {
- halId = mDaemon.setNotify(mDaemonCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to open fingerprint HAL", e);
- mDaemon = null; // try again later!
- }
-
- if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + halId);
- if (halId != 0) {
- loadAuthenticatorIds();
- updateActiveGroup(ActivityManager.getCurrentUser(), null);
- doTemplateCleanupForUser(ActivityManager.getCurrentUser());
- } else {
- Slog.w(TAG, "Failed to open Fingerprint HAL!");
- MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1);
- mDaemon = null;
- }
- }
- return mDaemon;
- }
-
- private long startPreEnroll(IBinder token) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startPreEnroll: no fingerprint HAL!");
- return 0;
- }
- try {
- return daemon.preEnroll();
- } catch (RemoteException e) {
- Slog.e(TAG, "startPreEnroll failed", e);
- }
- return 0;
- }
-
- private int startPostEnroll(IBinder token) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startPostEnroll: no fingerprint HAL!");
- return 0;
- }
- try {
- return daemon.postEnroll();
- } catch (RemoteException e) {
- Slog.e(TAG, "startPostEnroll failed", e);
- }
- return 0;
- }
-
private native NativeHandle convertSurfaceToNativeHandle(Surface surface);
-
- private void dumpInternal(PrintWriter pw) {
- PerformanceTracker performanceTracker =
- PerformanceTracker.getInstanceForSensorId(getSensorId());
-
- JSONObject dump = new JSONObject();
- try {
- dump.put("service", "Fingerprint Manager");
-
- JSONArray sets = new JSONArray();
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
- final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
- JSONObject set = new JSONObject();
- set.put("id", userId);
- set.put("count", N);
- set.put("accept", performanceTracker.getAcceptForUser(userId));
- set.put("reject", performanceTracker.getRejectForUser(userId));
- set.put("acquire", performanceTracker.getAcquireForUser(userId));
- set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
- set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
- // cryptoStats measures statistics about secure fingerprint transactions
- // (e.g. to unlock password storage, make secure purchases, etc.)
- set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
- set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
- set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
- sets.put(set);
- }
-
- dump.put("prints", sets);
- } catch (JSONException e) {
- Slog.e(TAG, "dump formatting failure", e);
- }
- pw.println(dump);
- pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
- }
-
- private void dumpProto(FileDescriptor fd) {
- PerformanceTracker tracker =
- PerformanceTracker.getInstanceForSensorId(getSensorId());
-
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
-
- final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
-
- proto.write(FingerprintUserStatsProto.USER_ID, userId);
- proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
- getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
-
- // Normal fingerprint authentications (e.g. lockscreen)
- long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
- proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptForUser(userId));
- proto.write(PerformanceStatsProto.REJECT, tracker.getRejectForUser(userId));
- proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireForUser(userId));
- proto.write(PerformanceStatsProto.LOCKOUT, tracker.getTimedLockoutForUser(userId));
- proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT,
- tracker.getPermanentLockoutForUser(userId));
- proto.end(countsToken);
-
- // Statistics about secure fingerprint transactions (e.g. to unlock password
- // storage, make secure purchases, etc.)
- countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
- proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptCryptoForUser(userId));
- proto.write(PerformanceStatsProto.REJECT, tracker.getRejectCryptoForUser(userId));
- proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireCryptoForUser(userId));
- proto.write(PerformanceStatsProto.LOCKOUT, 0); // meaningless for crypto
- proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, 0); // meaningless for crypto
- proto.end(countsToken);
-
- proto.end(userToken);
- }
- proto.flush();
- tracker.clear();
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java
new file mode 100644
index 000000000000..e1082ae51575
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.Build;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+
+import java.io.File;
+import java.util.Map;
+
+/**
+ * Sets the HAL's current active user, and updates the framework's authenticatorId cache.
+ */
+public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometricsFingerprint> {
+
+ private static final String TAG = "FingerprintUpdateActiveUserClient";
+ private static final String FP_DATA_DIR = "fpdata";
+
+ private final int mCurrentUserId;
+ private final boolean mHasEnrolledBiometrics;
+ private final Map<Integer, Long> mAuthenticatorIds;
+ private File mDirectory;
+
+ FingerprintUpdateActiveUserClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
+ @NonNull String owner, int sensorId, int currentUserId, boolean hasEnrolledBiometrics,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+ 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ mCurrentUserId = currentUserId;
+ mHasEnrolledBiometrics = hasEnrolledBiometrics;
+ mAuthenticatorIds = authenticatorIds;
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
+ if (mCurrentUserId == getTargetUserId()) {
+ Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
+ try {
+ mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
+ ? getFreshDaemon().getAuthenticatorId() : 0L);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to refresh authenticatorId", e);
+ }
+ finishCallback.onClientFinished(this, true /* success */);
+ return;
+ }
+
+ int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
+ if (firstSdkInt < Build.VERSION_CODES.BASE) {
+ Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
+ "at least VERSION_CODES.BASE");
+ }
+ File baseDir;
+ if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
+ baseDir = Environment.getUserSystemDirectory(getTargetUserId());
+ } else {
+ baseDir = Environment.getDataVendorDeDirectory(getTargetUserId());
+ }
+
+ mDirectory = new File(baseDir, FP_DATA_DIR);
+ if (!mDirectory.exists()) {
+ if (!mDirectory.mkdir()) {
+ Slog.e(TAG, "Cannot make directory: " + mDirectory.getAbsolutePath());
+ finishCallback.onClientFinished(this, false /* success */);
+ return;
+ }
+ // Calling mkdir() from this process will create a directory with our
+ // permissions (inherited from the containing dir). This command fixes
+ // the label.
+ if (!SELinux.restorecon(mDirectory)) {
+ Slog.e(TAG, "Restorecons failed. Directory will have wrong label.");
+ finishCallback.onClientFinished(this, false /* success */);
+ return;
+ }
+ }
+
+ startHalOperation();
+ }
+
+ @Override
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().setActiveGroup(getTargetUserId(), mDirectory.getAbsolutePath());
+ mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
+ ? getFreshDaemon().getAuthenticatorId() : 0L);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveGroup: " + e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityDispatcher.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityDispatcher.java
new file mode 100644
index 000000000000..7bda33da8126
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityDispatcher.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Keeps track of sensor gesture availability (e.g. swipe), and notifies clients when its
+ * availability changes
+ */
+public class GestureAvailabilityDispatcher {
+ private static final String TAG = "GestureAvailabilityTracker";
+
+ private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks;
+ private final Map<Integer, Boolean> mActiveSensors;
+
+ private boolean mIsActive;
+
+ GestureAvailabilityDispatcher() {
+ mClientActiveCallbacks = new CopyOnWriteArrayList<>();
+ mActiveSensors = new HashMap<>();
+ }
+
+ /**
+ * @return true if any sensor is active.
+ */
+ public boolean isAnySensorActive() {
+ return mIsActive;
+ }
+
+ public void markSensorActive(int sensorId, boolean active) {
+ mActiveSensors.put(sensorId, active);
+
+ final boolean wasActive = mIsActive;
+ boolean isActive = false;
+ for (Boolean b : mActiveSensors.values()) {
+ if (b) {
+ isActive = true;
+ break;
+ }
+ }
+
+ if (wasActive != isActive) {
+ Slog.d(TAG, "Notifying gesture availability, active=" + mIsActive);
+ mIsActive = isActive;
+ notifyClientActiveCallbacks(mIsActive);
+ }
+ }
+
+ void registerCallback(IFingerprintClientActiveCallback callback) {
+ mClientActiveCallbacks.add(callback);
+ }
+
+ void removeCallback(IFingerprintClientActiveCallback callback) {
+ mClientActiveCallbacks.remove(callback);
+ }
+
+ private void notifyClientActiveCallbacks(boolean isActive) {
+ for (IFingerprintClientActiveCallback callback : mClientActiveCallbacks) {
+ try {
+ callback.onClientActiveChanged(isActive);
+ } catch (RemoteException re) {
+ // If the remote is dead, stop notifying it
+ mClientActiveCallbacks.remove(callback);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
new file mode 100644
index 000000000000..e0806ff0fdb0
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+/**
+ * Interface for under-display fingerprint sensors.
+ * {@link com.android.server.biometrics.sensors.ClientMonitor} subclass that require knowledge of
+ * finger position (e.g. enroll, authenticate) should implement this.
+ */
+public interface Udfps {
+ void onFingerDown(int x, int y, float minor, float major);
+ void onFingerUp();
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
new file mode 100644
index 000000000000..5e521d2fe46c
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.Nullable;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Contains helper methods for under-display fingerprint HIDL.
+ */
+public class UdfpsHelper {
+
+ private static final String TAG = "UdfpsHelper";
+
+ static void onFingerDown(IBiometricsFingerprint daemon, int x, int y, float minor,
+ float major) {
+ android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
+ android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
+ daemon);
+ if (extension == null) {
+ Slog.v(TAG, "onFingerDown | failed to cast the HIDL to V2_3");
+ return;
+ }
+
+ try {
+ extension.onFingerDown(x, y, minor, major);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "onFingerDown | RemoteException: ", e);
+ }
+ }
+
+ static void onFingerUp(IBiometricsFingerprint daemon) {
+ android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
+ android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
+ daemon);
+ if (extension == null) {
+ Slog.v(TAG, "onFingerUp | failed to cast the HIDL to V2_3");
+ return;
+ }
+
+ try {
+ extension.onFingerUp();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "onFingerUp | RemoteException: ", e);
+ }
+ }
+
+ static void showUdfpsOverlay(@Nullable IUdfpsOverlayController udfpsOverlayController) {
+ if (udfpsOverlayController == null) {
+ return;
+ }
+ try {
+ udfpsOverlayController.showUdfpsOverlay();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e);
+ }
+ }
+
+ static void hideUdfpsOverlay(@Nullable IUdfpsOverlayController udfpsOverlayController) {
+ if (udfpsOverlayController == null) {
+ return;
+ }
+ try {
+ udfpsOverlayController.hideUdfpsOverlay();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when hiding the UDFPS overlay", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index a2e51371233b..9e0405792746 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -23,6 +23,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import com.android.server.biometrics.SensorConfig;
+import com.android.server.biometrics.sensors.LockoutTracker;
/**
* TODO(b/141025588): Add JavaDoc.
@@ -63,6 +64,12 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
+ public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId)
+ throws RemoteException {
+ return LockoutTracker.LOCKOUT_NONE;
+ }
+
+ @Override
public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
index 1884ac693148..bcf63dcdd67f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
@@ -18,17 +18,12 @@ package com.android.server.biometrics.sensors.iris;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.iris.IIrisService;
-import com.android.server.biometrics.sensors.BiometricServiceBase;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintService;
-
-import java.util.List;
+import com.android.server.SystemService;
+import com.android.server.biometrics.Utils;
/**
* A service to manage multiple clients that want to access the Iris HAL API.
@@ -36,11 +31,9 @@ import java.util.List;
* iris-related events.
*
* TODO: The vendor is expected to fill in the service. See
- * {@link FingerprintService}
- *
- * @hide
+ * {@link com.android.server.biometrics.sensors.face.FaceService}
*/
-public class IrisService extends BiometricServiceBase {
+public class IrisService extends SystemService {
private static final String TAG = "IrisService";
@@ -50,87 +43,16 @@ public class IrisService extends BiometricServiceBase {
private final class IrisServiceWrapper extends IIrisService.Stub {
@Override // Binder call
public void initializeConfiguration(int sensorId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- initializeConfigurationInternal(sensorId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
}
}
- /**
- * Initializes the system service.
- * <p>
- * Subclasses must define a single argument constructor that accepts the context
- * and passes it to super.
- * </p>
- *
- * @param context The system server context.
- */
- public IrisService(Context context) {
+ public IrisService(@NonNull Context context) {
super(context);
}
@Override
public void onStart() {
- super.onStart();
publishBinderService(Context.IRIS_SERVICE, new IrisServiceWrapper());
}
-
- @Override
- protected String getTag() {
- return TAG;
- }
-
- @Override
- protected DaemonWrapper getDaemonWrapper() {
- return null;
- }
-
- @Override
- protected BiometricUtils getBiometricUtils() {
- return null;
- }
-
- @Override
- protected boolean hasReachedEnrollmentLimit(int userId) {
- return false;
- }
-
- @Override
- protected void updateActiveGroup(int userId, String clientPackage) {
-
- }
-
- @Override
- protected boolean hasEnrolledBiometrics(int userId) {
- return false;
- }
-
- @Override
- protected String getManageBiometricPermission() {
- return null;
- }
-
- @Override
- protected void checkUseBiometricPermission() {
-
- }
-
- @Override
- protected boolean checkAppOps(int uid, String opPackageName) {
- return false;
- }
-
- @Override
- protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(int userId) {
- return null;
- }
-
- @Override
- protected int statsModality() {
- return BiometricsProtoEnums.MODALITY_IRIS;
- }
-
- @Override
- protected int getLockoutMode(int userId) {
- return LockoutTracker.LOCKOUT_NONE;
- }
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index b279b370c611..ed3a223b5dd7 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -400,7 +400,7 @@ public class ClipboardService extends SystemService {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId)
+ intendingUid, intendingUserId, false)
|| isDeviceLocked(intendingUserId)) {
return null;
}
@@ -416,7 +416,7 @@ public class ClipboardService extends SystemService {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId)
+ intendingUid, intendingUserId, false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
@@ -450,7 +450,7 @@ public class ClipboardService extends SystemService {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId)
+ intendingUid, intendingUserId, false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
@@ -740,14 +740,21 @@ public class ClipboardService extends SystemService {
private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,
@UserIdInt int userId) {
- // Check the AppOp.
- if (mAppOps.noteOp(op, uid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return false;
- }
+ return clipboardAccessAllowed(op, callingPackage, uid, userId, true);
+ }
+
+ private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,
+ @UserIdInt int userId, boolean shouldNoteOp) {
+
+ boolean allowed = false;
+
+ // First, verify package ownership to ensure use below is safe.
+ mAppOps.checkPackage(uid, callingPackage);
+
// Shell can access the clipboard for testing purposes.
if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND,
callingPackage) == PackageManager.PERMISSION_GRANTED) {
- return true;
+ allowed = true;
}
// The default IME is always allowed to access the clipboard.
String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
@@ -755,7 +762,7 @@ public class ClipboardService extends SystemService {
if (!TextUtils.isEmpty(defaultIme)) {
final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();
if (imePkg.equals(callingPackage)) {
- return true;
+ allowed = true;
}
}
@@ -766,8 +773,10 @@ public class ClipboardService extends SystemService {
// at the same time. e.x. SystemUI. It needs to check the window focus of
// Binder.getCallingUid(). Without checking, the user X can't copy any thing from
// INTERNAL_SYSTEM_WINDOW to the other applications.
- boolean allowed = mWm.isUidFocused(uid)
- || isInternalSysWindowAppWithWindowFocus(callingPackage);
+ if (!allowed) {
+ allowed = mWm.isUidFocused(uid)
+ || isInternalSysWindowAppWithWindowFocus(callingPackage);
+ }
if (!allowed && mContentCaptureInternal != null) {
// ...or the Content Capture Service
// The uid parameter of mContentCaptureInternal.isContentCaptureServiceForUser
@@ -786,17 +795,28 @@ public class ClipboardService extends SystemService {
// userId must pass intending userId. i.e. user#10.
allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId);
}
- if (!allowed) {
- Slog.e(TAG, "Denying clipboard access to " + callingPackage
- + ", application is not in focus neither is a system service for "
- + "user " + userId);
- }
- return allowed;
+ break;
case AppOpsManager.OP_WRITE_CLIPBOARD:
// Writing is allowed without focus.
- return true;
+ allowed = true;
+ break;
default:
throw new IllegalArgumentException("Unknown clipboard appop " + op);
}
+ if (!allowed) {
+ Slog.e(TAG, "Denying clipboard access to " + callingPackage
+ + ", application is not in focus nor is it a system service for "
+ + "user " + userId);
+ return false;
+ }
+ // Finally, check the app op.
+ int appOpsResult;
+ if (shouldNoteOp) {
+ appOpsResult = mAppOps.noteOp(op, uid, callingPackage);
+ } else {
+ appOpsResult = mAppOps.checkOp(op, uid, callingPackage);
+ }
+
+ return appOpsResult == AppOpsManager.MODE_ALLOWED;
}
}
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index f6ce2dc68b99..de302fc01f2d 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -196,13 +196,7 @@ public class PacManager {
mPacUrl = Uri.EMPTY;
mCurrentPac = null;
if (mProxyService != null) {
- try {
- mProxyService.stopPacSystem();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to stop PAC service", e);
- } finally {
- unbind();
- }
+ unbind();
}
}
return DO_SEND_BROADCAST;
@@ -327,11 +321,6 @@ public class PacManager {
if (mProxyService == null) {
Log.e(TAG, "No proxy service");
} else {
- try {
- mProxyService.startPacSystem();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
- }
mNetThreadHandler.post(mPacDownloader);
}
}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index f0b7150dd84f..a75a80a606eb 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -72,7 +72,7 @@ import java.util.Set;
*
* @hide
*/
-public class PermissionMonitor {
+public class PermissionMonitor implements PackageManagerInternal.PackageListObserver {
private static final String TAG = "PermissionMonitor";
private static final boolean DBG = true;
protected static final Boolean SYSTEM = Boolean.TRUE;
@@ -82,6 +82,7 @@ public class PermissionMonitor {
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final INetd mNetd;
+ private final Dependencies mDeps;
// Values are User IDs.
@GuardedBy("this")
@@ -102,48 +103,30 @@ public class PermissionMonitor {
@GuardedBy("this")
private final Set<Integer> mAllApps = new HashSet<>();
- private class PackageListObserver implements PackageManagerInternal.PackageListObserver {
-
- private int getPermissionForUid(int uid) {
- int permission = 0;
- // Check all the packages for this UID. The UID has the permission if any of the
- // packages in it has the permission.
- String[] packages = mPackageManager.getPackagesForUid(uid);
- if (packages != null && packages.length > 0) {
- for (String name : packages) {
- final PackageInfo app = getPackageInfo(name);
- if (app != null && app.requestedPermissions != null) {
- permission |= getNetdPermissionMask(app.requestedPermissions,
- app.requestedPermissionsFlags);
- }
- }
- } else {
- // The last package of this uid is removed from device. Clean the package up.
- permission = INetd.PERMISSION_UNINSTALLED;
- }
- return permission;
- }
-
- @Override
- public void onPackageAdded(String packageName, int uid) {
- sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
- }
-
- @Override
- public void onPackageChanged(@NonNull String packageName, int uid) {
- sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+ /**
+ * Dependencies of PermissionMonitor, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * Get device first sdk version.
+ */
+ public int getDeviceFirstSdkInt() {
+ return Build.VERSION.FIRST_SDK_INT;
}
+ }
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
- }
+ public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) {
+ this(context, netd, new Dependencies());
}
- public PermissionMonitor(Context context, INetd netd) {
+ @VisibleForTesting
+ PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd,
+ @NonNull final Dependencies deps) {
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mNetd = netd;
+ mDeps = deps;
}
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -153,7 +136,7 @@ public class PermissionMonitor {
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
if (pmi != null) {
- pmi.getPackageList(new PackageListObserver());
+ pmi.getPackageList(this);
} else {
loge("failed to get the PackageManagerInternal service");
}
@@ -224,11 +207,6 @@ public class PermissionMonitor {
}
@VisibleForTesting
- protected int getDeviceFirstSdkInt() {
- return Build.VERSION.FIRST_SDK_INT;
- }
-
- @VisibleForTesting
boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) {
if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) {
return false;
@@ -250,7 +228,7 @@ public class PermissionMonitor {
if (app.applicationInfo != null) {
// Backward compatibility for b/114245686, on devices that launched before Q daemons
// and apps running as the system UID are exempted from this check.
- if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
+ if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) {
return true;
}
@@ -363,15 +341,38 @@ public class PermissionMonitor {
return currentPermission;
}
+ private int getPermissionForUid(final int uid) {
+ int permission = INetd.PERMISSION_NONE;
+ // Check all the packages for this UID. The UID has the permission if any of the
+ // packages in it has the permission.
+ final String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages != null && packages.length > 0) {
+ for (String name : packages) {
+ final PackageInfo app = getPackageInfo(name);
+ if (app != null && app.requestedPermissions != null) {
+ permission |= getNetdPermissionMask(app.requestedPermissions,
+ app.requestedPermissionsFlags);
+ }
+ }
+ } else {
+ // The last package of this uid is removed from device. Clean the package up.
+ permission = INetd.PERMISSION_UNINSTALLED;
+ }
+ return permission;
+ }
+
/**
- * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}.
+ * Called when a package is added.
*
* @param packageName The name of the new package.
* @param uid The uid of the new package.
*
* @hide
*/
- public synchronized void onPackageAdded(String packageName, int uid) {
+ @Override
+ public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
+ sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
@@ -398,13 +399,17 @@ public class PermissionMonitor {
}
/**
- * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}.
+ * Called when a package is removed.
*
+ * @param packageName The name of the removed package or null.
* @param uid containing the integer uid previously assigned to the package.
*
* @hide
*/
- public synchronized void onPackageRemoved(int uid) {
+ @Override
+ public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
+ sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+
// If the newly-removed package falls within some VPN's uid range, update Netd with it.
// This needs to happen before the mApps update below, since removeBypassingUids() depends
// on mApps to check if the package can bypass VPN.
@@ -449,6 +454,19 @@ public class PermissionMonitor {
}
}
+ /**
+ * Called when a package is changed.
+ *
+ * @param packageName The name of the changed package.
+ * @param uid The uid of the changed package.
+ *
+ * @hide
+ */
+ @Override
+ public synchronized void onPackageChanged(@NonNull final String packageName, final int uid) {
+ sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+ }
+
private static int getNetdPermissionMask(String[] requestedPermissions,
int[] requestedPermissionsFlags) {
int permissions = 0;
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index e715890fb211..26cc3ee165f1 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -73,6 +73,8 @@ public class ProxyTracker {
@GuardedBy("mProxyLock")
private boolean mDefaultProxyEnabled = true;
+ private final Handler mConnectivityServiceHandler;
+
// The object responsible for Proxy Auto Configuration (PAC).
@NonNull
private final PacManager mPacManager;
@@ -80,6 +82,7 @@ public class ProxyTracker {
public ProxyTracker(@NonNull final Context context,
@NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
mContext = context;
+ mConnectivityServiceHandler = connectivityServiceInternalHandler;
mPacManager = new PacManager(context, connectivityServiceInternalHandler, pacChangedEvent);
}
@@ -149,6 +152,9 @@ public class ProxyTracker {
* Read the global proxy settings and cache them in memory.
*/
public void loadGlobalProxy() {
+ if (loadDeprecatedGlobalHttpProxy()) {
+ return;
+ }
ContentResolver res = mContext.getContentResolver();
String host = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_HOST);
int port = Settings.Global.getInt(res, GLOBAL_HTTP_PROXY_PORT, 0);
@@ -157,7 +163,7 @@ public class ProxyTracker {
if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
ProxyInfo proxyProperties;
if (!TextUtils.isEmpty(pacFileUrl)) {
- proxyProperties = new ProxyInfo(pacFileUrl);
+ proxyProperties = new ProxyInfo(Uri.parse(pacFileUrl));
} else {
proxyProperties = new ProxyInfo(host, port, exclList);
}
@@ -169,20 +175,24 @@ public class ProxyTracker {
synchronized (mProxyLock) {
mGlobalProxy = proxyProperties;
}
+
+ if (!TextUtils.isEmpty(pacFileUrl)) {
+ mConnectivityServiceHandler.post(
+ () -> mPacManager.setCurrentProxyScriptUrl(proxyProperties));
+ }
}
- loadDeprecatedGlobalHttpProxy();
- // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ?
}
/**
* Read the global proxy from the deprecated Settings.Global.HTTP_PROXY setting and apply it.
+ * Returns {@code true} when global proxy was set successfully from deprecated setting.
*/
- public void loadDeprecatedGlobalHttpProxy() {
+ public boolean loadDeprecatedGlobalHttpProxy() {
final String proxy = Settings.Global.getString(mContext.getContentResolver(), HTTP_PROXY);
if (!TextUtils.isEmpty(proxy)) {
String data[] = proxy.split(":");
if (data.length == 0) {
- return;
+ return false;
}
final String proxyHost = data[0];
@@ -191,12 +201,14 @@ public class ProxyTracker {
try {
proxyPort = Integer.parseInt(data[1]);
} catch (NumberFormatException e) {
- return;
+ return false;
}
}
final ProxyInfo p = new ProxyInfo(proxyHost, proxyPort, "");
setGlobalProxy(p);
+ return true;
}
+ return false;
}
/**
diff --git a/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
index 62fb75107755..ffaf364934f6 100644
--- a/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
+++ b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
@@ -15,11 +15,11 @@
*/
package com.android.server.content;
+import android.app.ActivityManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.os.SystemClock;
import android.util.Pair;
-import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
import java.util.HashMap;
@@ -57,12 +57,7 @@ class SyncAdapterStateFetcher {
* Return UID active state.
*/
public boolean isAppActive(int uid) {
- final AppStateTracker ast =
- LocalServices.getService(AppStateTracker.class);
- if (ast == null) {
- return false;
- }
-
- return ast.isUidActive(uid);
+ final ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
+ return (ami != null) ? ami.isUidActive(uid) : false;
}
}
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 29026e8affcf..ec2b0c0a6830 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -588,8 +588,9 @@ final class ColorFade {
if (mSurfaceControl == null) {
Transaction t = new Transaction();
try {
- final SurfaceControl.Builder builder =
- new SurfaceControl.Builder(mSurfaceSession).setName("ColorFade");
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession)
+ .setName("ColorFade")
+ .setCallsite("ColorFade.createSurface");
if (mMode == MODE_FADE) {
builder.setColorLayer();
} else {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 566af3c43453..48fa1bf9f246 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -134,7 +134,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
hdrCapabilities, isDefaultDisplay);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else if (device.updateDisplayPropertiesLocked(configs, activeConfig,
+ } else if (device.updateDisplayPropertiesLocked(info, configs, activeConfig,
configSpecs, colorModes, activeColorMode, hdrCapabilities)) {
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
@@ -212,8 +212,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
mPhysicalDisplayId = physicalDisplayId;
mIsDefaultDisplay = isDefaultDisplay;
- mDisplayInfo = info;
- updateDisplayPropertiesLocked(configs, activeConfigId, configSpecs, colorModes,
+ updateDisplayPropertiesLocked(info, configs, activeConfigId, configSpecs, colorModes,
activeColorMode, hdrCapabilities);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
if (mIsDefaultDisplay) {
@@ -238,12 +237,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
/**
* Returns true if there is a change.
**/
- public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayConfig[] configs,
+ public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info,
+ SurfaceControl.DisplayConfig[] configs,
int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) {
boolean changed = updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
+ changed |= updateDisplayInfo(info);
changed |= updateColorModesLocked(colorModes, activeColorMode);
changed |= updateHdrCapabilitiesLocked(hdrCapabilities);
+
if (changed) {
mHavePendingChanges = true;
}
@@ -420,6 +422,14 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mSystemBrightnessToNits = sysToNits;
}
+ private boolean updateDisplayInfo(SurfaceControl.DisplayInfo info) {
+ if (Objects.equals(mDisplayInfo, info)) {
+ return false;
+ }
+ mDisplayInfo = info;
+ return true;
+ }
+
private boolean updateColorModesLocked(int[] colorModes, int activeColorMode) {
if (colorModes == null) {
return false;
@@ -450,7 +460,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// Determine whether the active color mode is still there.
if (!mSupportedColorModes.contains(mActiveColorMode)) {
- if (mActiveColorMode != 0) {
+ if (mActiveColorMode != Display.COLOR_MODE_DEFAULT) {
Slog.w(TAG, "Active color mode no longer available, reverting"
+ " to default mode.");
mActiveColorMode = Display.COLOR_MODE_DEFAULT;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 3ff6ec1afa41..86e6a3220507 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -244,7 +244,7 @@ abstract class HdmiCecLocalDevice {
if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
return false;
}
- // Cache incoming message. Note that it caches only white-listed one.
+ // Cache incoming message if it is included in the list of cacheable opcodes.
mCecMessageCache.cacheMessage(message);
return onMessage(message);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index aed94fc85431..64d70d6601f6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -225,7 +225,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
if (SystemProperties.getBoolean(Constants.PROPERTY_KEEP_AWAKE, true)) {
mWakeLock = new SystemWakeLock();
} else {
- // Create a dummy lock object that doesn't do anything about wake lock,
+ // Create a stub lock object that doesn't do anything about wake lock,
// hence allows the device to go to sleep even if it's the active source.
mWakeLock = new ActiveWakeLock() {
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 2c0ddaf35182..804cc92cca08 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1667,6 +1667,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (avr == null) {
return;
}
+ setArcStatus(false);
// Seq #44.
removeAction(RequestArcInitiationAction.class);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 30d7d5494a7e..a8a9a368cc71 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2290,7 +2290,7 @@ public class HdmiControlService extends SystemService {
pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
pw.println("mSystemAudioActivated: " + isSystemAudioActivated());
- pw.println("mHdmiCecVolumeControlEnabled " + mHdmiCecVolumeControlEnabled);
+ pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControlEnabled);
pw.decreaseIndent();
pw.println("mMhlController: ");
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 115899a2a518..74ed815f080a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -59,6 +59,8 @@ import android.os.Message;
import android.os.MessageQueue;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -115,6 +117,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+
/*
* Wraps the C++ InputManager and provides its callbacks.
*/
@@ -233,7 +236,7 @@ public class InputManagerService extends IInputManager.Stub
private static native void nativeSetInteractive(long ptr, boolean interactive);
private static native void nativeReloadCalibration(long ptr);
private static native void nativeVibrate(long ptr, int deviceId, long[] pattern,
- int repeat, int token);
+ int[] amplitudes, int repeat, int token);
private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
private static native void nativeReloadKeyboardLayouts(long ptr);
private static native void nativeReloadDeviceAliases(long ptr);
@@ -1713,7 +1716,7 @@ public class InputManagerService extends IInputManager.Stub
// Binder call
@Override
- public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
+ public void vibrate(int deviceId, long[] pattern, int[] amplitudes, int repeat, IBinder token) {
if (repeat >= pattern.length) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -1735,7 +1738,7 @@ public class InputManagerService extends IInputManager.Stub
synchronized (v) {
v.mVibrating = true;
- nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
+ nativeVibrate(mPtr, deviceId, pattern, amplitudes, repeat, v.mTokenValue);
}
}
@@ -2506,4 +2509,11 @@ public class InputManagerService extends IInputManager.Stub
return InputManagerService.this.transferTouchFocus(fromChannelToken, toChannelToken);
}
}
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ new InputShellCommand().exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
}
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
new file mode 100644
index 000000000000..fd5f48c91867
--- /dev/null
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -0,0 +1,409 @@
+/*
+ * 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.android.server.input;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import android.hardware.input.InputManager;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Command that sends input events to the device.
+ */
+
+public class InputShellCommand extends ShellCommand {
+ private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
+ private static final String INVALID_DISPLAY_ARGUMENTS =
+ "Error: Invalid arguments for display ID.";
+ private static final int DEFAULT_DEVICE_ID = 0;
+ private static final float DEFAULT_PRESSURE = 1.0f;
+ private static final float NO_PRESSURE = 0.0f;
+ private static final float DEFAULT_SIZE = 1.0f;
+ private static final int DEFAULT_META_STATE = 0;
+ private static final float DEFAULT_PRECISION_X = 1.0f;
+ private static final float DEFAULT_PRECISION_Y = 1.0f;
+ private static final int DEFAULT_EDGE_FLAGS = 0;
+
+ private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
+ put("keyboard", InputDevice.SOURCE_KEYBOARD);
+ put("dpad", InputDevice.SOURCE_DPAD);
+ put("gamepad", InputDevice.SOURCE_GAMEPAD);
+ put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN);
+ put("mouse", InputDevice.SOURCE_MOUSE);
+ put("stylus", InputDevice.SOURCE_STYLUS);
+ put("trackball", InputDevice.SOURCE_TRACKBALL);
+ put("touchpad", InputDevice.SOURCE_TOUCHPAD);
+ put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION);
+ put("joystick", InputDevice.SOURCE_JOYSTICK);
+ }};
+
+ private void injectKeyEvent(KeyEvent event) {
+ InputManager.getInstance().injectInputEvent(event,
+ InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+ }
+
+ private int getInputDeviceId(int inputSource) {
+ int[] devIds = InputDevice.getDeviceIds();
+ for (int devId : devIds) {
+ InputDevice inputDev = InputDevice.getDevice(devId);
+ if (inputDev.supportsSource(inputSource)) {
+ return devId;
+ }
+ }
+ return DEFAULT_DEVICE_ID;
+ }
+
+ private int getDisplayId() {
+ String displayArg = getNextArgRequired();
+ if ("INVALID_DISPLAY".equalsIgnoreCase(displayArg)) {
+ return INVALID_DISPLAY;
+ } else if ("DEFAULT_DISPLAY".equalsIgnoreCase(displayArg)) {
+ return DEFAULT_DISPLAY;
+ } else {
+ try {
+ final int displayId = Integer.parseInt(displayArg);
+ if (displayId == INVALID_DISPLAY) {
+ return INVALID_DISPLAY;
+ }
+ return Math.max(displayId, 0);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(INVALID_DISPLAY_ARGUMENTS);
+ }
+ }
+ }
+
+ /**
+ * Builds a MotionEvent and injects it into the event stream.
+ *
+ * @param inputSource the InputDevice.SOURCE_* sending the input event
+ * @param action the MotionEvent.ACTION_* for the event
+ * @param downTime the value of the ACTION_DOWN event happened
+ * @param when the value of SystemClock.uptimeMillis() at which the event happened
+ * @param x x coordinate of event
+ * @param y y coordinate of event
+ * @param pressure pressure of event
+ */
+ private void injectMotionEvent(int inputSource, int action, long downTime, long when,
+ float x, float y, float pressure, int displayId) {
+ MotionEvent event = MotionEvent.obtain(downTime, when, action, x, y, pressure,
+ DEFAULT_SIZE, DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y,
+ getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS);
+ event.setSource(inputSource);
+ if (displayId == INVALID_DISPLAY
+ && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ displayId = DEFAULT_DISPLAY;
+ }
+ event.setDisplayId(displayId);
+ InputManager.getInstance().injectInputEvent(event,
+ InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+ }
+
+ private float lerp(float a, float b, float alpha) {
+ return (b - a) * alpha + a;
+ }
+
+ private int getSource(int inputSource, int defaultSource) {
+ return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource;
+ }
+
+ @Override
+ public final int onCommand(String cmd) {
+ String arg = cmd;
+ int inputSource = InputDevice.SOURCE_UNKNOWN;
+ // Get source (optional).
+ if (SOURCES.containsKey(arg)) {
+ inputSource = SOURCES.get(arg);
+ arg = getNextArgRequired();
+ }
+
+ // Get displayId (optional).
+ int displayId = INVALID_DISPLAY;
+ if ("-d".equals(arg)) {
+ displayId = getDisplayId();
+ arg = getNextArgRequired();
+ }
+
+ try {
+ if ("text".equals(arg)) {
+ runText(inputSource, displayId);
+ } else if ("keyevent".equals(arg)) {
+ runKeyEvent(inputSource, displayId);
+ } else if ("tap".equals(arg)) {
+ runTap(inputSource, displayId);
+ } else if ("swipe".equals(arg)) {
+ runSwipe(inputSource, displayId);
+ } else if ("draganddrop".equals(arg)) {
+ runDragAndDrop(inputSource, displayId);
+ } else if ("press".equals(arg)) {
+ runPress(inputSource, displayId);
+ } else if ("roll".equals(arg)) {
+ runRoll(inputSource, displayId);
+ } else if ("motionevent".equals(arg)) {
+ runMotionEvent(inputSource, displayId);
+ } else {
+ handleDefaultCommands(arg);
+ }
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(INVALID_ARGUMENTS + arg);
+ }
+ return 0;
+ }
+
+ @Override
+ public final void onHelp() {
+ try (PrintWriter out = getOutPrintWriter();) {
+ out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]");
+ out.println();
+ out.println("The sources are: ");
+ for (String src : SOURCES.keySet()) {
+ out.println(" " + src);
+ }
+ out.println();
+ out.printf("-d: specify the display ID.\n (Default: %d for key event, "
+ + "%d for motion event if not specified.)",
+ INVALID_DISPLAY, DEFAULT_DISPLAY);
+ out.println();
+ out.println("The commands and default sources are:");
+ out.println(" text <string> (Default: touchscreen)");
+ out.println(" keyevent [--longpress] <key code number or name> ..."
+ + " (Default: keyboard)");
+ out.println(" tap <x> <y> (Default: touchscreen)");
+ out.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+ + " (Default: touchscreen)");
+ out.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
+ + " (Default: touchscreen)");
+ out.println(" press (Default: trackball)");
+ out.println(" roll <dx> <dy> (Default: trackball)");
+ out.println(" motionevent <DOWN|UP|MOVE|CANCEL> <x> <y> (Default: touchscreen)");
+ }
+ }
+
+ private void runText(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+ sendText(inputSource, getNextArgRequired(), displayId);
+ }
+
+ /**
+ * Convert the characters of string text into key event's and send to
+ * device.
+ *
+ * @param text is a string of characters you want to input to the device.
+ */
+ private void sendText(int source, final String text, int displayId) {
+ final StringBuffer buff = new StringBuffer(text);
+ boolean escapeFlag = false;
+ for (int i = 0; i < buff.length(); i++) {
+ if (escapeFlag) {
+ escapeFlag = false;
+ if (buff.charAt(i) == 's') {
+ buff.setCharAt(i, ' ');
+ buff.deleteCharAt(--i);
+ }
+ }
+ if (buff.charAt(i) == '%') {
+ escapeFlag = true;
+ }
+ }
+
+ final char[] chars = buff.toString().toCharArray();
+ final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ final KeyEvent[] events = kcm.getEvents(chars);
+ for (int i = 0; i < events.length; i++) {
+ KeyEvent e = events[i];
+ if (source != e.getSource()) {
+ e.setSource(source);
+ }
+ e.setDisplayId(displayId);
+ injectKeyEvent(e);
+ }
+ }
+
+ private void runKeyEvent(int inputSource, int displayId) {
+ String arg = getNextArgRequired();
+ final boolean longpress = "--longpress".equals(arg);
+ if (longpress) {
+ arg = getNextArgRequired();
+ }
+
+ do {
+ final int keycode = KeyEvent.keyCodeFromString(arg);
+ sendKeyEvent(inputSource, keycode, longpress, displayId);
+ } while ((arg = getNextArg()) != null);
+ }
+
+ private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
+ final long now = SystemClock.uptimeMillis();
+ int repeatCount = 0;
+
+ KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
+ 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
+ inputSource);
+ event.setDisplayId(displayId);
+
+ injectKeyEvent(event);
+ if (longpress) {
+ repeatCount++;
+ injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
+ KeyEvent.FLAG_LONG_PRESS));
+ }
+ injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+ }
+
+ private void runTap(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendTap(inputSource, Float.parseFloat(getNextArgRequired()),
+ Float.parseFloat(getNextArgRequired()), displayId);
+ }
+
+ private void sendTap(int inputSource, float x, float y, int displayId) {
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
+ displayId);
+ injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
+ }
+
+ private void runPress(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+ sendTap(inputSource, 0.0f, 0.0f, displayId);
+ }
+
+ private void runSwipe(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendSwipe(inputSource, displayId, false);
+ }
+
+ private void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
+ // Parse two points and duration.
+ final float x1 = Float.parseFloat(getNextArgRequired());
+ final float y1 = Float.parseFloat(getNextArgRequired());
+ final float x2 = Float.parseFloat(getNextArgRequired());
+ final float y2 = Float.parseFloat(getNextArgRequired());
+ String durationArg = getNextArg();
+ int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
+ if (duration < 0) {
+ duration = 300;
+ }
+
+ final long down = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
+ displayId);
+ if (isDragDrop) {
+ // long press until drag start.
+ try {
+ Thread.sleep(ViewConfiguration.getLongPressTimeout());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ long now = SystemClock.uptimeMillis();
+ final long endTime = down + duration;
+ while (now < endTime) {
+ final long elapsedTime = now - down;
+ final float alpha = (float) elapsedTime / duration;
+ injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
+ lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
+ now = SystemClock.uptimeMillis();
+ }
+ injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
+ displayId);
+ }
+
+ private void runDragAndDrop(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendSwipe(inputSource, displayId, true);
+ }
+
+ private void runRoll(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+ sendMove(inputSource, Float.parseFloat(getNextArgRequired()),
+ Float.parseFloat(getNextArgRequired()), displayId);
+ }
+
+ /**
+ * Sends a simple zero-pressure move event.
+ *
+ * @param inputSource the InputDevice.SOURCE_* sending the input event
+ * @param dx change in x coordinate due to move
+ * @param dy change in y coordinate due to move
+ */
+ private void sendMove(int inputSource, float dx, float dy, int displayId) {
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f,
+ displayId);
+ }
+
+ private int getAction() {
+ String actionString = getNextArgRequired();
+ switch (actionString.toUpperCase()) {
+ case "DOWN":
+ return MotionEvent.ACTION_DOWN;
+ case "UP":
+ return MotionEvent.ACTION_UP;
+ case "MOVE":
+ return MotionEvent.ACTION_MOVE;
+ case "CANCEL":
+ return MotionEvent.ACTION_CANCEL;
+ default:
+ throw new IllegalArgumentException("Unknown action: " + actionString);
+ }
+ }
+
+ private void runMotionEvent(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ int action = getAction();
+ float x = 0, y = 0;
+ if (action == MotionEvent.ACTION_DOWN
+ || action == MotionEvent.ACTION_MOVE
+ || action == MotionEvent.ACTION_UP) {
+ x = Float.parseFloat(getNextArgRequired());
+ y = Float.parseFloat(getNextArgRequired());
+ } else {
+ // For ACTION_CANCEL, the positions are optional
+ String xString = getNextArg();
+ String yString = getNextArg();
+ if (xString != null && yString != null) {
+ x = Float.parseFloat(xString);
+ y = Float.parseFloat(yString);
+ }
+ }
+
+ sendMotionEvent(inputSource, action, x, y, displayId);
+ }
+
+ private void sendMotionEvent(int inputSource, int action, float x, float y,
+ int displayId) {
+ float pressure = NO_PRESSURE;
+
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
+ pressure = DEFAULT_PRESSURE;
+ }
+
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId);
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 70f0399d1070..05cf40a091b6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -120,6 +120,11 @@ public abstract class InputMethodManagerInternal {
public abstract void reportImeControl(@Nullable IBinder windowToken);
/**
+ * Destroys the IME surface.
+ */
+ public abstract void removeImeSurface();
+
+ /**
* Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
*/
private static final InputMethodManagerInternal NOP =
@@ -166,6 +171,10 @@ public abstract class InputMethodManagerInternal {
@Override
public void reportImeControl(@Nullable IBinder windowToken) {
}
+
+ @Override
+ public void removeImeSurface() {
+ }
};
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9acb47538043..254285dfbd41 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -18,8 +18,6 @@ package com.android.server.inputmethod;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR;
-
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.Manifest;
@@ -211,6 +209,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_INITIALIZE_IME = 1040;
static final int MSG_CREATE_SESSION = 1050;
static final int MSG_REMOVE_IME_SURFACE = 1060;
+ static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
static final int MSG_START_INPUT = 2000;
@@ -718,11 +717,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
*/
int mImeWindowVis;
- /**
- * Checks if the client needs to start input.
- */
- private boolean mCurClientNeedStartInput = false;
-
private AlertDialog.Builder mDialogBuilder;
private AlertDialog mSwitchingDialog;
private IBinder mSwitchingDialogToken = new Binder();
@@ -2128,6 +2122,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public void onInputMethodFinishInput() throws RemoteException {
mCallback.onInputMethodFinishInput();
}
+
+ @Override
+ public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+ mCallback.onInlineSuggestionsSessionInvalidated();
+ }
}
/**
@@ -2943,7 +2942,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
vis = 0;
}
if (!mCurPerceptible) {
- vis = 0;
+ vis &= ~InputMethodService.IME_VISIBLE;
}
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
@@ -3461,20 +3460,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (mCurFocusedWindow == windowToken) {
if (DEBUG) {
Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
- + " attribute=" + attribute + ", token = " + windowToken);
- }
- // Needs to start input when the same window focus gain but not with the same editor,
- // or when the current client needs to start input (e.g. when focusing the same
- // window after device turned screen on).
- if (attribute != null && (startInputReason != WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR
- || mCurClientNeedStartInput)) {
- if (mIsInteractive) {
- mCurClientNeedStartInput = false;
- }
+ + " attribute=" + attribute + ", token = " + windowToken
+ + ", startInputReason="
+ + InputMethodDebug.startInputReasonToString(startInputReason));
+ }
+ if (attribute != null) {
return startInputUncheckedLocked(cs, inputContext, missingMethods,
attribute, startInputFlags, startInputReason);
}
-
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
null, null, null, -1, null);
@@ -4000,6 +3993,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
}
+ @Override
+ public void removeImeSurfaceFromWindow(IBinder windowToken) {
+ // No permission check, because we'll only execute the request if the calling window is
+ // also the current IME client.
+ mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
+ }
+
@BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
@@ -4047,7 +4047,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// Send it to window manager to hide IME from IME target window.
// TODO(b/139861270): send to mCurClient.client once IMMS is aware of
// actual IME target.
- mWindowManagerInternal.hideIme(mHideRequestWindowMap.get(windowToken));
+ mWindowManagerInternal.hideIme(
+ mHideRequestWindowMap.get(windowToken),
+ mCurClient.selfReportedDisplayId);
}
} else {
// Send to window manager to show IME after IME layout finishes.
@@ -4271,11 +4273,27 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return true;
}
case MSG_REMOVE_IME_SURFACE: {
- try {
- if (mEnabledSession != null && mEnabledSession.session != null) {
- mEnabledSession.session.removeImeSurface();
+ synchronized (mMethodMap) {
+ try {
+ if (mEnabledSession != null && mEnabledSession.session != null
+ && !mShowRequested) {
+ mEnabledSession.session.removeImeSurface();
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ return true;
+ }
+ case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: {
+ IBinder windowToken = (IBinder) msg.obj;
+ synchronized (mMethodMap) {
+ try {
+ if (windowToken == mCurFocusedWindow
+ && mEnabledSession != null && mEnabledSession.session != null) {
+ mEnabledSession.session.removeImeSurface();
+ }
+ } catch (RemoteException e) {
}
- } catch (RemoteException e) {
}
return true;
}
@@ -4428,9 +4446,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private void handleSetInteractive(final boolean interactive) {
synchronized (mMethodMap) {
mIsInteractive = interactive;
- if (!interactive) {
- mCurClientNeedStartInput = true;
- }
updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
// Inform the current client of the change in active status
@@ -5109,6 +5124,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public void reportImeControl(@Nullable IBinder windowToken) {
mService.reportImeControl(windowToken);
}
+
+ @Override
+ public void removeImeSurface() {
+ mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+ }
}
@BinderThread
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 23392db0690d..2516e289f099 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -226,6 +226,11 @@ public final class MultiClientInputMethodManagerService {
@Override
public void reportImeControl(@Nullable IBinder windowToken) {
}
+
+ @Override
+ public void removeImeSurface() {
+ reportNotSupported();
+ }
});
}
@@ -1482,6 +1487,12 @@ public final class MultiClientInputMethodManagerService {
@BinderThread
@Override
+ public void removeImeSurfaceFromWindow(IBinder windowToken) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
public boolean showSoftInput(
IInputMethodClient client, IBinder token, int flags,
ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/location/ConcurrentLinkedEvictingDeque.java b/services/core/java/com/android/server/location/ConcurrentLinkedEvictingDeque.java
new file mode 100644
index 000000000000..6910c353e546
--- /dev/null
+++ b/services/core/java/com/android/server/location/ConcurrentLinkedEvictingDeque.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+/**
+ * Helper class to make a ConcurrentLinkedDeque fixed-size, evicting old entries when full.
+ *
+ * @param <E> The type of elements held in this queue.
+ */
+public class ConcurrentLinkedEvictingDeque<E> extends ConcurrentLinkedDeque<E> {
+ private int mSize;
+
+ ConcurrentLinkedEvictingDeque(int size) {
+ mSize = size;
+ }
+
+ @Override
+ public boolean add(E elem) {
+ synchronized (this) {
+ if (size() == mSize) {
+ poll();
+ }
+
+ return super.add(elem);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 0f70bb8bc840..33ceeeff331f 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -36,7 +36,6 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Consumer;
/**
@@ -104,28 +103,6 @@ import java.util.function.Consumer;
public static final int ACTION_CANCELLED = 2;
/**
- * Helper class to make a ConcurrentLinkedDeque fixed-size, evicting old entries when full.
- */
- private class ConcurrentLinkedEvictingDeque<E> extends ConcurrentLinkedDeque<E> {
- private int mSize;
-
- ConcurrentLinkedEvictingDeque(int size) {
- mSize = size;
- }
-
- @Override
- public boolean add(E elem) {
- synchronized (this) {
- if (size() == mSize) {
- poll();
- }
-
- return super.add(elem);
- }
- }
- }
-
- /**
* A container class to store a record of ContextHubClient registration.
*/
private class RegistrationRecord {
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 48b22704fd07..264c611d45fe 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -43,6 +43,7 @@ import android.hardware.location.NanoAppInstanceInfo;
import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
import android.location.LocationManager;
+import android.os.Binder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -386,7 +387,7 @@ public class ContextHubService extends IContextHubService.Stub {
createLoadTransactionCallback(contextHubHandle, nanoAppBinary);
ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
- contextHubHandle, nanoAppBinary, onCompleteCallback);
+ contextHubHandle, nanoAppBinary, onCompleteCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
return 0;
@@ -411,7 +412,7 @@ public class ContextHubService extends IContextHubService.Stub {
IContextHubTransactionCallback onCompleteCallback =
createUnloadTransactionCallback(contextHubId);
ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
- contextHubId, nanoAppId, onCompleteCallback);
+ contextHubId, nanoAppId, onCompleteCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
return 0;
@@ -464,7 +465,7 @@ public class ContextHubService extends IContextHubService.Stub {
IContextHubTransactionCallback onCompleteCallback =
createQueryTransactionCallback(contextHubId);
ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
- contextHubId, onCompleteCallback);
+ contextHubId, onCompleteCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
return Result.OK;
@@ -696,7 +697,7 @@ public class ContextHubService extends IContextHubService.Stub {
}
ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
- contextHubId, nanoAppBinary, transactionCallback);
+ contextHubId, nanoAppBinary, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -720,7 +721,7 @@ public class ContextHubService extends IContextHubService.Stub {
}
ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
- contextHubId, nanoAppId, transactionCallback);
+ contextHubId, nanoAppId, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -744,7 +745,7 @@ public class ContextHubService extends IContextHubService.Stub {
}
ContextHubServiceTransaction transaction = mTransactionManager.createEnableTransaction(
- contextHubId, nanoAppId, transactionCallback);
+ contextHubId, nanoAppId, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -768,7 +769,7 @@ public class ContextHubService extends IContextHubService.Stub {
}
ContextHubServiceTransaction transaction = mTransactionManager.createDisableTransaction(
- contextHubId, nanoAppId, transactionCallback);
+ contextHubId, nanoAppId, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -790,7 +791,7 @@ public class ContextHubService extends IContextHubService.Stub {
}
ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
- contextHubId, transactionCallback);
+ contextHubId, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -822,6 +823,10 @@ public class ContextHubService extends IContextHubService.Stub {
pw.println("=================== CLIENTS ====================");
pw.println(mClientManager);
+ pw.println("");
+ pw.println("=================== TRANSACTIONS ====================");
+ pw.println(mTransactionManager);
+
// dump eventLog
}
@@ -924,4 +929,8 @@ public class ContextHubService extends IContextHubService.Stub {
mContextHubWrapper.onSettingChanged(Setting.LOCATION,
enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
}
+
+ private String getCallingPackageName() {
+ return mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
+ }
}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
index c1fc98248e08..62bd91bda1b8 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
@@ -32,14 +32,32 @@ import java.util.concurrent.TimeUnit;
@ContextHubTransaction.Type
private final int mTransactionType;
+ /* The ID of the nanoapp this transaction is targeted for, null if not applicable. */
+ private final Long mNanoAppId;
+
+ /*
+ * The host package associated with this transaction.
+ */
+ private final String mPackage;
+
/*
* true if the transaction has already completed, false otherwise
*/
private boolean mIsComplete = false;
- /* package */ ContextHubServiceTransaction(int id, int type) {
+ /* package */ ContextHubServiceTransaction(int id, int type, String packageName) {
+ mTransactionId = id;
+ mTransactionType = type;
+ mNanoAppId = null;
+ mPackage = packageName;
+ }
+
+ /* package */ ContextHubServiceTransaction(int id, int type, long nanoAppId,
+ String packageName) {
mTransactionId = id;
mTransactionType = type;
+ mNanoAppId = nanoAppId;
+ mPackage = packageName;
}
/**
@@ -129,7 +147,13 @@ import java.util.concurrent.TimeUnit;
@Override
public String toString() {
- return ContextHubTransaction.typeToString(mTransactionType, true /* upperCase */)
- + " transaction (ID = " + mTransactionId + ")";
+ String out = ContextHubTransaction.typeToString(mTransactionType, true /* upperCase */)
+ + " (";
+ if (mNanoAppId != null) {
+ out += "appId = 0x" + Long.toHexString(mNanoAppId) + ", ";
+ }
+ out += "package = " + mPackage + ")";
+
+ return out;
}
}
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
index cced781f8e1b..d3fc7058bcf2 100644
--- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -26,11 +26,15 @@ import android.hardware.location.NanoAppState;
import android.os.RemoteException;
import android.util.Log;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
import java.util.List;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -53,6 +57,11 @@ import java.util.concurrent.atomic.AtomicInteger;
private static final int MAX_PENDING_REQUESTS = 10000;
/*
+ * The DateFormat for printing TransactionRecord.
+ */
+ private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd HH:mm:ss.SSS");
+
+ /*
* The proxy to talk to the Context Hub
*/
private final IContexthub mContextHubProxy;
@@ -83,6 +92,33 @@ import java.util.concurrent.atomic.AtomicInteger;
private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
private ScheduledFuture<?> mTimeoutFuture = null;
+ /*
+ * The list of previous transaction records.
+ */
+ private static final int NUM_TRANSACTION_RECORDS = 20;
+ private final ConcurrentLinkedEvictingDeque<TransactionRecord> mTransactionRecordDeque =
+ new ConcurrentLinkedEvictingDeque<>(NUM_TRANSACTION_RECORDS);
+
+ /**
+ * A container class to store a record of transactions.
+ */
+ private class TransactionRecord {
+ private final String mTransaction;
+ private final long mTimestamp;
+
+ TransactionRecord(String transaction) {
+ mTransaction = transaction;
+ mTimestamp = System.currentTimeMillis();
+ }
+
+ // TODO: Add dump to proto here
+
+ @Override
+ public String toString() {
+ return DATE_FORMAT.format(new Date(mTimestamp)) + " " + mTransaction;
+ }
+ }
+
/* package */ ContextHubTransactionManager(
IContexthub contextHubProxy, ContextHubClientManager clientManager,
NanoAppStateManager nanoAppStateManager) {
@@ -101,11 +137,12 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
/* package */ ContextHubServiceTransaction createLoadTransaction(
int contextHubId, NanoAppBinary nanoAppBinary,
- IContextHubTransactionCallback onCompleteCallback) {
+ IContextHubTransactionCallback onCompleteCallback, String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_LOAD_NANOAPP) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_LOAD_NANOAPP,
+ nanoAppBinary.getNanoAppId(), packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
ContextHubServiceUtil.createHidlNanoAppBinary(nanoAppBinary);
try {
@@ -119,7 +156,7 @@ import java.util.concurrent.atomic.AtomicInteger;
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
if (result == ContextHubTransaction.RESULT_SUCCESS) {
// NOTE: The legacy JNI code used to do a query right after a load success
// to synchronize the service cache. Instead store the binary that was
@@ -149,11 +186,13 @@ import java.util.concurrent.atomic.AtomicInteger;
* @return the generated transaction
*/
/* package */ ContextHubServiceTransaction createUnloadTransaction(
- int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback,
+ String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_UNLOAD_NANOAPP) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_UNLOAD_NANOAPP,
+ nanoAppId, packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
try {
return mContextHubProxy.unloadNanoApp(
contextHubId, nanoAppId, this.getTransactionId());
@@ -165,7 +204,7 @@ import java.util.concurrent.atomic.AtomicInteger;
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
if (result == ContextHubTransaction.RESULT_SUCCESS) {
mNanoAppStateManager.removeNanoAppInstance(contextHubId, nanoAppId);
}
@@ -190,11 +229,13 @@ import java.util.concurrent.atomic.AtomicInteger;
* @return the generated transaction
*/
/* package */ ContextHubServiceTransaction createEnableTransaction(
- int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback,
+ String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_ENABLE_NANOAPP) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_ENABLE_NANOAPP,
+ packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
try {
return mContextHubProxy.enableNanoApp(
contextHubId, nanoAppId, this.getTransactionId());
@@ -206,7 +247,7 @@ import java.util.concurrent.atomic.AtomicInteger;
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
try {
onCompleteCallback.onTransactionComplete(result);
} catch (RemoteException e) {
@@ -225,11 +266,13 @@ import java.util.concurrent.atomic.AtomicInteger;
* @return the generated transaction
*/
/* package */ ContextHubServiceTransaction createDisableTransaction(
- int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback,
+ String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_DISABLE_NANOAPP) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_DISABLE_NANOAPP,
+ packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
try {
return mContextHubProxy.disableNanoApp(
contextHubId, nanoAppId, this.getTransactionId());
@@ -241,7 +284,7 @@ import java.util.concurrent.atomic.AtomicInteger;
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
try {
onCompleteCallback.onTransactionComplete(result);
} catch (RemoteException e) {
@@ -259,11 +302,13 @@ import java.util.concurrent.atomic.AtomicInteger;
* @return the generated transaction
*/
/* package */ ContextHubServiceTransaction createQueryTransaction(
- int contextHubId, IContextHubTransactionCallback onCompleteCallback) {
+ int contextHubId, IContextHubTransactionCallback onCompleteCallback,
+ String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_QUERY_NANOAPPS,
+ packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
try {
return mContextHubProxy.queryApps(contextHubId);
} catch (RemoteException e) {
@@ -273,12 +318,12 @@ import java.util.concurrent.atomic.AtomicInteger;
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
onQueryResponse(result, Collections.emptyList());
}
@Override
- /* package */ void onQueryResponse(
+ /* package */ void onQueryResponse(
@ContextHubTransaction.Result int result, List<NanoAppState> nanoAppStateList) {
try {
onCompleteCallback.onQueryResponse(result, nanoAppStateList);
@@ -307,6 +352,7 @@ import java.util.concurrent.atomic.AtomicInteger;
+ MAX_PENDING_REQUESTS + ")");
}
mTransactionQueue.add(transaction);
+ mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
if (mTransactionQueue.size() == 1) {
startNextTransaction();
@@ -433,4 +479,23 @@ import java.util.concurrent.atomic.AtomicInteger;
}
}
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(100);
+ TransactionRecord[] arr;
+ synchronized (this) {
+ arr = mTransactionQueue.toArray(new TransactionRecord[0]);
+ }
+ for (int i = 0; i < arr.length; i++) {
+ sb.append(i + ": " + arr[i] + "\n");
+ }
+
+ sb.append("Transaction History:\n");
+ Iterator<TransactionRecord> iterator = mTransactionRecordDeque.descendingIterator();
+ while (iterator.hasNext()) {
+ sb.append(iterator.next() + "\n");
+ }
+ return sb.toString();
+ }
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 9a6ffd07d9c5..d933c109b27d 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,13 +17,24 @@
package com.android.server.location;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_MOCK_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.location.LocationManager.EXTRA_LOCATION_ENABLED;
+import static android.location.LocationManager.EXTRA_PROVIDER_ENABLED;
+import static android.location.LocationManager.EXTRA_PROVIDER_NAME;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
+import static android.location.LocationManager.KEY_LOCATION_CHANGED;
+import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
+import static android.location.LocationManager.MODE_CHANGED_ACTION;
import static android.location.LocationManager.NETWORK_PROVIDER;
import static android.location.LocationManager.PASSIVE_PROVIDER;
+import static android.location.LocationManager.PROVIDERS_CHANGED_ACTION;
+import static android.location.LocationManager.invalidateLocalLocationEnabledCaches;
import static android.os.PowerManager.locationPowerSaveModeToString;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -55,6 +66,7 @@ import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssStatusListener;
import android.location.IGpsGeofenceHardware;
+import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.Location;
@@ -71,6 +83,7 @@ import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
@@ -110,10 +123,16 @@ import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationAttributionHelper;
+import com.android.server.location.util.LocationPermissionsHelper;
+import com.android.server.location.util.LocationPowerSaveModeHelper;
import com.android.server.location.util.LocationUsageLogger;
+import com.android.server.location.util.ScreenInteractiveHelper;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.SystemAppForegroundHelper;
import com.android.server.location.util.SystemAppOpsHelper;
+import com.android.server.location.util.SystemLocationPermissionsHelper;
+import com.android.server.location.util.SystemLocationPowerSaveModeHelper;
+import com.android.server.location.util.SystemScreenInteractiveHelper;
import com.android.server.location.util.SystemSettingsHelper;
import com.android.server.location.util.SystemUserInfoHelper;
import com.android.server.location.util.UserInfoHelper;
@@ -134,6 +153,7 @@ import java.util.Map.Entry;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
/**
* The service class that manages LocationProviders and issues location
@@ -242,9 +262,9 @@ public class LocationManagerService extends ILocationManager.Stub {
// time
private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
- private static final String ATTRIBUTION_TAG = "LocationService";
+ private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30000;
- private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
+ private static final String ATTRIBUTION_TAG = "LocationService";
private final Object mLock = new Object();
@@ -481,8 +501,8 @@ public class LocationManagerService extends ILocationManager.Stub {
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
}
- Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
- .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
+ Intent intent = new Intent(MODE_CHANGED_ACTION)
+ .putExtra(EXTRA_LOCATION_ENABLED, enabled)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
@@ -630,7 +650,8 @@ public class LocationManagerService extends ILocationManager.Stub {
manager = new LocationProviderManager(name);
mProviderManagers.add(manager);
}
- manager.setMockProvider(new MockProvider(properties));
+ manager.setMockProvider(
+ new MockProvider(properties, CallerIdentity.fromContext(mContext)));
}
}
@@ -1007,9 +1028,9 @@ public class LocationManagerService extends ILocationManager.Stub {
if (wasEnabled != null) {
// fused and passive provider never get public updates for legacy reasons
if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
- Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
- .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
- .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled)
+ Intent intent = new Intent(PROVIDERS_CHANGED_ACTION)
+ .putExtra(EXTRA_PROVIDER_NAME, mName)
+ .putExtra(EXTRA_PROVIDER_ENABLED, enabled)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
@@ -1209,21 +1230,10 @@ public class LocationManagerService extends ILocationManager.Stub {
false);
// Now update monitoring of high power requests only.
- boolean wasHighPowerMonitoring = mOpHighPowerMonitoring;
mOpHighPowerMonitoring = updateMonitoring(
requestingHighPowerLocation,
mOpHighPowerMonitoring,
true);
- if (mOpHighPowerMonitoring != wasHighPowerMonitoring) {
- long identity = Binder.clearCallingIdentity();
- try {
- // Send an intent to notify that a high power request has been added/removed.
- Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
}
private boolean updateMonitoring(boolean allowMonitoring, boolean currentlyMonitoring,
@@ -1231,19 +1241,20 @@ public class LocationManagerService extends ILocationManager.Stub {
if (!currentlyMonitoring) {
if (allowMonitoring) {
if (!highPower) {
- return mAppOpsHelper.startLocationMonitoring(mCallerIdentity);
+ return mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, mCallerIdentity);
} else {
- return mAppOpsHelper.startHighPowerLocationMonitoring(mCallerIdentity);
+ return mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ mCallerIdentity);
}
}
} else {
- if (!allowMonitoring || !mAppOpsHelper.checkLocationAccess(mCallerIdentity,
+ if (!allowMonitoring || !mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
LocationPermissions.getPermissionLevel(mContext, mCallerIdentity.getUid(),
- mCallerIdentity.getPid()))) {
+ mCallerIdentity.getPid())), mCallerIdentity)) {
if (!highPower) {
- mAppOpsHelper.stopLocationMonitoring(mCallerIdentity);
+ mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, mCallerIdentity);
} else {
- mAppOpsHelper.stopHighPowerLocationMonitoring(mCallerIdentity);
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, mCallerIdentity);
}
return false;
}
@@ -1271,7 +1282,14 @@ public class LocationManagerService extends ILocationManager.Stub {
LocationRequest locationRequest) {
if (mListener != null) {
try {
- mListener.onLocationChanged(new Location(location));
+ mListener.onLocationChanged(new Location(location), new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) {
+ synchronized (mLock) {
+ decrementPendingBroadcastsLocked();
+ }
+ }
+ });
// call this after broadcasting so we do not increment
// if we throw an exception.
incrementPendingBroadcastsLocked();
@@ -1280,8 +1298,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
} else {
Intent locationChanged = new Intent();
- locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED,
- new Location(location));
+ locationChanged.putExtra(KEY_LOCATION_CHANGED, new Location(location));
try {
mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
LocationPermissions.asPermission(
@@ -1306,29 +1323,19 @@ public class LocationManagerService extends ILocationManager.Stub {
if (mListener != null) {
try {
- if (enabled) {
- mListener.onProviderEnabled(provider);
- } else {
- mListener.onProviderDisabled(provider);
- }
- // call this after broadcasting so we do not increment
- // if we throw an exception.
- incrementPendingBroadcastsLocked();
+ mListener.onProviderEnabledChanged(provider, enabled);
} catch (RemoteException e) {
return false;
}
} else {
Intent providerIntent = new Intent();
- providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
+ providerIntent.putExtra(KEY_PROVIDER_ENABLED, enabled);
try {
- mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
+ mPendingIntent.send(mContext, 0, providerIntent, null, mHandler,
LocationPermissions.asPermission(
locationRequest.isCoarse() ? PERMISSION_COARSE
: PERMISSION_FINE),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
- // call this after broadcasting so we do not increment
- // if we throw an exception.
- incrementPendingBroadcastsLocked();
} catch (PendingIntent.CanceledException e) {
return false;
}
@@ -1336,16 +1343,6 @@ public class LocationManagerService extends ILocationManager.Stub {
return true;
}
- public void callRemovedLocked() {
- if (mListener != null) {
- try {
- mListener.onRemoved();
- } catch (RemoteException e) {
- // doesn't matter
- }
- }
- }
-
@Override
public void binderDied() {
synchronized (mLock) {
@@ -1406,20 +1403,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public void locationCallbackFinished(ILocationListener listener) {
- //Do not use getReceiverLocked here as that will add the ILocationListener to
- //the receiver list if it is not found. If it is not found then the
- //LocationListener was removed when it had a pending broadcast and should
- //not be added back.
- synchronized (mLock) {
- Receiver receiver = mReceivers.get(listener.asBinder());
- if (receiver != null) {
- receiver.decrementPendingBroadcastsLocked();
- }
- }
- }
-
- @Override
public int getGnssYearOfHardware() {
return mGnssManagerService == null ? 0 : mGnssManagerService.getGnssYearOfHardware();
}
@@ -1616,8 +1599,9 @@ public class LocationManagerService extends ILocationManager.Stub {
continue;
}
- if (!mAppOpsHelper.checkLocationAccess(identity,
- record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE)) {
+ if (!mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
+ record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE),
+ identity)) {
continue;
}
final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
@@ -2121,10 +2105,6 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public Location getLastLocation(LocationRequest request, String packageName,
String attributionTag) {
- if (request == null) {
- request = DEFAULT_LOCATION_REQUEST;
- }
-
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext);
@@ -2149,7 +2129,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// appops check should always be right before delivery
- if (!mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+ if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ identity)) {
return null;
}
@@ -2161,42 +2142,79 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public boolean getCurrentLocation(LocationRequest locationRequest,
- ICancellationSignal remoteCancellationSignal, ILocationListener listener,
+ public void getCurrentLocation(LocationRequest request,
+ ICancellationSignal remoteCancellationSignal, ILocationCallback callback,
String packageName, String attributionTag, String listenerId) {
- // side effect of validating locationRequest and packageName
- Location lastLocation = getLastLocation(locationRequest, packageName, attributionTag);
+ // unsafe is ok because app ops will verify the package name
+ CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag,
+ listenerId);
+ int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext);
+ LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel,
+ PERMISSION_COARSE);
+
+ request = createSanitizedRequest(request, false, permissionLevel);
+ request.setNumUpdates(1);
+ if (request.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ }
+
+ GetCurrentLocationTransport transport = new GetCurrentLocationTransport(callback);
+
+ if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
+ identity.getPackageName())) {
+ transport.deliverResult(null);
+ return;
+ }
+ if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
+ transport.deliverResult(null);
+ return;
+ }
+
+ Location lastLocation;
+ synchronized (mLock) {
+ LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+ if (manager == null) {
+ transport.deliverResult(null);
+ return;
+ }
+ if (!manager.isEnabled(identity.getUserId()) && !request.isLocationSettingsIgnored()) {
+ transport.deliverResult(null);
+ return;
+ }
+
+ lastLocation = manager.getLastLocation(identity.getUserId(), permissionLevel);
+ }
+
if (lastLocation != null) {
long locationAgeMs = NANOSECONDS.toMillis(
SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos());
if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
- try {
- listener.onLocationChanged(lastLocation);
- return true;
- } catch (RemoteException e) {
- Log.w(TAG, e);
- return false;
+ // appops check should always be right before delivery
+ if (mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ identity)) {
+ transport.deliverResult(lastLocation);
+ } else {
+ transport.deliverResult(null);
}
+ return;
}
if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())) {
if (locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) {
// not allowed to request new locations, so we can't return anything
- return false;
+ transport.deliverResult(null);
+ return;
}
}
}
- registerLocationListener(locationRequest, listener, packageName, attributionTag,
- listenerId);
+ registerLocationListener(request, transport, packageName, attributionTag, listenerId);
CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
remoteCancellationSignal);
if (cancellationSignal != null) {
- cancellationSignal.setOnCancelListener(
- () -> unregisterLocationListener(listener));
+ cancellationSignal.setOnCancelListener(() -> unregisterLocationListener(transport));
}
- return true;
}
@Override
@@ -2324,7 +2342,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+ public void sendExtraCommand(String provider, String command, Bundle extras) {
LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_COARSE);
mContext.enforceCallingOrSelfPermission(
permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
@@ -2345,8 +2363,6 @@ public class LocationManagerService extends ILocationManager.Stub {
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_SEND_EXTRA_COMMAND,
provider);
-
- return true;
}
@Override
@@ -2435,7 +2451,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
- LocationManager.invalidateLocalLocationEnabledCaches();
+ invalidateLocalLocationEnabledCaches();
mSettingsHelper.setLocationEnabled(enabled, userId);
}
@@ -2548,7 +2564,8 @@ public class LocationManagerService extends ILocationManager.Stub {
r.mLastFixBroadcast = location;
// appops check should always be right before delivery
- if (!mAppOpsHelper.noteLocationAccess(receiver.mCallerIdentity, permissionLevel)) {
+ if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ receiver.mCallerIdentity)) {
continue;
}
@@ -2560,8 +2577,6 @@ public class LocationManagerService extends ILocationManager.Stub {
// track expired records
if (r.mRealRequest.getNumUpdates() <= 0 || r.mExpirationRealtimeMs < now) {
- // notify the client it can remove this listener
- r.mReceiver.callRemovedLocked();
if (deadUpdateRecords == null) {
deadUpdateRecords = new ArrayList<>();
}
@@ -2641,7 +2656,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2652,7 +2667,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mProviderManagers.add(manager);
}
- manager.setMockProvider(new MockProvider(properties));
+ manager.setMockProvider(new MockProvider(properties, identity));
}
}
@@ -2661,7 +2676,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2684,7 +2699,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2705,7 +2720,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2839,6 +2854,47 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
+ private class GetCurrentLocationTransport extends ILocationListener.Stub {
+
+ private final Executor mExecutor;
+ private final ILocationCallback mCallback;
+
+ GetCurrentLocationTransport(ILocationCallback callback) {
+ mExecutor = FgThread.getExecutor();
+ mCallback = callback;
+ }
+
+ @Override
+ public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
+ mExecutor.execute(() -> {
+ deliverResult(location);
+ try {
+ onCompleteCallback.sendResult(null);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ unregisterLocationListener(this);
+ }
+
+ @Override
+ public void onProviderEnabledChanged(String provider, boolean enabled)
+ throws RemoteException {
+ if (!enabled) {
+ deliverResult(null);
+ unregisterLocationListener(this);
+ }
+ }
+
+ public void deliverResult(@Nullable Location location) {
+ try {
+ mCallback.onLocation(location);
+ } catch (RemoteException e) {
+ // do nothing
+ }
+ }
+ }
+
private class LocalService extends LocationManagerInternal {
LocalService() {}
@@ -2882,24 +2938,36 @@ public class LocationManagerService extends ILocationManager.Stub {
private final UserInfoHelper mUserInfoHelper;
private final SystemAppOpsHelper mAppOpsHelper;
+ private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
private final SystemSettingsHelper mSettingsHelper;
private final SystemAppForegroundHelper mAppForegroundHelper;
- private final LocationUsageLogger mLocationUsageLogger;
+ private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+ private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
+ private final LocationUsageLogger mLocationUsageLogger;
+ private final LocationRequestStatistics mLocationRequestStatistics;
SystemInjector(Context context, UserInfoHelper userInfoHelper) {
mUserInfoHelper = userInfoHelper;
mAppOpsHelper = new SystemAppOpsHelper(context);
+ mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
+ mAppOpsHelper);
mSettingsHelper = new SystemSettingsHelper(context);
mAppForegroundHelper = new SystemAppForegroundHelper(context);
- mLocationUsageLogger = new LocationUsageLogger();
+ mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context);
+ mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+ mLocationUsageLogger = new LocationUsageLogger();
+ mLocationRequestStatistics = new LocationRequestStatistics();
}
void onSystemReady() {
mAppOpsHelper.onSystemReady();
+ mLocationPermissionsHelper.onSystemReady();
mSettingsHelper.onSystemReady();
mAppForegroundHelper.onSystemReady();
+ mLocationPowerSaveModeHelper.onSystemReady();
+ mScreenInteractiveHelper.onSystemReady();
}
@Override
@@ -2913,6 +2981,11 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public LocationPermissionsHelper getLocationPermissionsHelper() {
+ return mLocationPermissionsHelper;
+ }
+
+ @Override
public SettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@@ -2928,8 +3001,23 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+ return mLocationPowerSaveModeHelper;
+ }
+
+ @Override
+ public ScreenInteractiveHelper getScreenInteractiveHelper() {
+ return mScreenInteractiveHelper;
+ }
+
+ @Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
+
+ @Override
+ public LocationRequestStatistics getLocationRequestStatistics() {
+ return mLocationRequestStatistics;
+ }
}
}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 8843e522f9a1..2bcee2faa813 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.location.Location;
import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -128,27 +129,38 @@ public class LocationProviderProxy extends AbstractLocationProvider {
mServiceWatcher.dump(fd, pw, args);
}
+ private static String guessPackageName(Context context, int uid) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null || packageNames.length == 0) {
+ // illegal state exception will propagate back through binders
+ throw new IllegalStateException(
+ "location provider from uid " + uid + " has no package information");
+ } else {
+ return packageNames[0];
+ }
+ }
+
private class Proxy extends ILocationProviderManager.Stub {
Proxy() {}
// executed on binder thread
@Override
- public void onSetAttributionTag(String attributionTag) {
+ public void onSetIdentity(@Nullable String packageName, @Nullable String attributionTag) {
synchronized (mLock) {
if (mProxy != this) {
return;
}
- String packageName = mServiceWatcher.getBoundService().getPackageName();
+ CallerIdentity identity;
if (packageName == null) {
- return;
+ packageName = guessPackageName(mContext, Binder.getCallingUid());
+ // unsafe is ok since the package is coming direct from the package manager here
+ identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
+ } else {
+ identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
}
- // we don't need to verify the package name because we're getting it straight from
- // the service watcher
- CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
- attributionTag);
setIdentity(identity);
}
}
@@ -163,12 +175,9 @@ public class LocationProviderProxy extends AbstractLocationProvider {
// if no identity is set yet, set it now
if (getIdentity() == null) {
- String packageName = mServiceWatcher.getBoundService().getPackageName();
- if (packageName != null) {
- // we don't need to verify the package name because we're getting it
- // straight from the service watcher
- setIdentity(CallerIdentity.fromBinderUnsafe(packageName, null));
- }
+ String packageName = guessPackageName(mContext, Binder.getCallingUid());
+ // unsafe is ok since the package is coming direct from the package manager here
+ setIdentity(CallerIdentity.fromBinderUnsafe(packageName, null));
}
setProperties(properties);
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 160e641aee9e..c5bd5e6fed8c 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.annotation.Nullable;
import android.location.Location;
+import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
import com.android.internal.location.ProviderProperties;
@@ -37,10 +38,11 @@ public class MockProvider extends AbstractLocationProvider {
@Nullable private Location mLocation;
- public MockProvider(ProviderProperties properties) {
+ public MockProvider(ProviderProperties properties, CallerIdentity identity) {
// using a direct executor is ok because this class has no locks that could deadlock
super(DIRECT_EXECUTOR);
setProperties(properties);
+ setIdentity(identity);
}
/** Sets the allowed state of this mock provider. */
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index f37992a456ac..f6896b86f9b9 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -50,14 +50,10 @@ public class PassiveProvider extends AbstractLocationProvider {
Criteria.POWER_LOW,
Criteria.ACCURACY_COARSE);
- private volatile boolean mReportLocation;
-
public PassiveProvider(Context context) {
// using a direct executor is ok because this class has no locks that could deadlock
super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context));
- mReportLocation = false;
-
setProperties(PROPERTIES);
setAllowed(true);
}
@@ -66,15 +62,11 @@ public class PassiveProvider extends AbstractLocationProvider {
* Pass a location into the passive provider.
*/
public void updateLocation(Location location) {
- if (mReportLocation) {
- reportLocation(location);
- }
+ reportLocation(location);
}
@Override
- public void onSetRequest(ProviderRequest request) {
- mReportLocation = request.reportLocation;
- }
+ public void onSetRequest(ProviderRequest request) {}
@Override
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 33e2bfae5e0e..2d9734ef0553 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -16,7 +16,6 @@
package com.android.server.location.geofence;
-import static android.Manifest.permission;
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
@@ -44,8 +43,8 @@ import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.PendingIntentListenerRegistration;
-import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.UserInfoHelper;
@@ -86,7 +85,7 @@ public class GeofenceManager extends
// we store these values because we don't trust the listeners not to give us dupes, not to
// spam us, and because checking the values may be more expensive
- private boolean mAppOpsAllowed;
+ private boolean mPermitted;
private @Nullable Location mCachedLocation;
private float mCachedLocationDistanceM;
@@ -101,7 +100,7 @@ public class GeofenceManager extends
mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- TAG + ":" + identity.getPackageName());
+ TAG + ":" + identity.getPackageName());
mWakeLock.setReferenceCounted(true);
mWakeLock.setWorkSource(identity.addToWorkSource(null));
}
@@ -112,10 +111,10 @@ public class GeofenceManager extends
}
@Override
- protected boolean onPendingIntentRegister(Object key) {
+ protected void onPendingIntentListenerRegister() {
mGeofenceState = STATE_UNKNOWN;
- mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
- return true;
+ mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
}
@Override
@@ -128,18 +127,32 @@ public class GeofenceManager extends
}
}
- boolean isAppOpsAllowed() {
- return mAppOpsAllowed;
+ boolean isPermitted() {
+ return mPermitted;
}
- boolean onAppOpsChanged(String packageName) {
+ boolean onLocationPermissionsChanged(String packageName) {
if (getIdentity().getPackageName().equals(packageName)) {
- boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
- PERMISSION_FINE);
- if (appOpsAllowed != mAppOpsAllowed) {
- mAppOpsAllowed = appOpsAllowed;
- return true;
- }
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ boolean onLocationPermissionsChanged(int uid) {
+ if (getIdentity().getUid() == uid) {
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ private boolean onLocationPermissionsChanged() {
+ boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
+ if (permitted != mPermitted) {
+ mPermitted = permitted;
+ return true;
}
return false;
@@ -187,10 +200,10 @@ public class GeofenceManager extends
mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
try {
- pendingIntent.send(mContext, 0, intent,
- (pI, i, rC, rD, rE) -> mWakeLock.release(),
- null, permission.ACCESS_FINE_LOCATION,
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ // send() only enforces permissions for broadcast intents, but since clients can
+ // select any kind of pending intent we do not rely on send() to enforce permissions
+ pendingIntent.send(mContext, 0, intent, (pI, i, rC, rD, rE) -> mWakeLock.release(),
+ null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
} catch (PendingIntent.CanceledException e) {
mWakeLock.release();
removeRegistration(new GeofenceKey(pendingIntent, getRequest()), this);
@@ -203,7 +216,7 @@ public class GeofenceManager extends
builder.append(getIdentity());
ArraySet<String> flags = new ArraySet<>(1);
- if (!mAppOpsAllowed) {
+ if (!mPermitted) {
flags.add("na");
}
if (!flags.isEmpty()) {
@@ -225,10 +238,22 @@ public class GeofenceManager extends
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
this::onLocationPackageBlacklistChanged;
- private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+ private final LocationPermissionsHelper.LocationPermissionsListener
+ mLocationPermissionsListener =
+ new LocationPermissionsHelper.LocationPermissionsListener() {
+ @Override
+ public void onLocationPermissionsChanged(String packageName) {
+ GeofenceManager.this.onLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ public void onLocationPermissionsChanged(int uid) {
+ GeofenceManager.this.onLocationPermissionsChanged(uid);
+ }
+ };
protected final UserInfoHelper mUserInfoHelper;
- protected final AppOpsHelper mAppOpsHelper;
+ protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final SettingsHelper mSettingsHelper;
protected final LocationUsageLogger mLocationUsageLogger;
@@ -242,7 +267,7 @@ public class GeofenceManager extends
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
- mAppOpsHelper = injector.getAppOpsHelper();
+ mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
}
@@ -282,7 +307,7 @@ public class GeofenceManager extends
@Override
protected boolean isActive(GeofenceRegistration registration) {
CallerIdentity identity = registration.getIdentity();
- return registration.isAppOpsAllowed()
+ return registration.isPermitted()
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
&& !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
@@ -295,7 +320,7 @@ public class GeofenceManager extends
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.addListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
}
@Override
@@ -304,7 +329,7 @@ public class GeofenceManager extends
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.removeListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
}
@Override
@@ -435,7 +460,11 @@ public class GeofenceManager extends
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onAppOpsChanged(String packageName) {
- updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+ private void onLocationPermissionsChanged(String packageName) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+ }
+
+ private void onLocationPermissionsChanged(int uid) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 564b90ddd10a..0b7968be484b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -31,8 +31,8 @@ import com.android.server.LocalServices;
import com.android.server.location.listeners.BinderListenerRegistration;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.util.AppForegroundHelper;
-import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.UserInfoHelper;
import com.android.server.location.util.UserInfoHelper.UserListener;
@@ -65,7 +65,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
// we store these values because we don't trust the listeners not to give us dupes, not to
// spam us, and because checking the values may be more expensive
private boolean mForeground;
- private boolean mAppOpsAllowed;
+ private boolean mPermitted;
protected GnssListenerRegistration(@Nullable TRequest request,
CallerIdentity callerIdentity, TListener listener) {
@@ -84,25 +84,39 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
return mForeground;
}
- boolean isAppOpsAllowed() {
- return mAppOpsAllowed;
+ boolean isPermitted() {
+ return mPermitted;
}
@Override
- protected boolean onBinderRegister(Object key) {
- mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+ protected void onBinderListenerRegister() {
+ mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
- return true;
}
- boolean onAppOpsChanged(String packageName) {
+ boolean onLocationPermissionsChanged(String packageName) {
if (getIdentity().getPackageName().equals(packageName)) {
- boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
- PERMISSION_FINE);
- if (appOpsAllowed != mAppOpsAllowed) {
- mAppOpsAllowed = appOpsAllowed;
- return true;
- }
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ boolean onLocationPermissionsChanged(int uid) {
+ if (getIdentity().getUid() == uid) {
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ private boolean onLocationPermissionsChanged() {
+ boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
+ if (permitted != mPermitted) {
+ mPermitted = permitted;
+ return true;
}
return false;
@@ -126,7 +140,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
if (!mForeground) {
flags.add("bg");
}
- if (!mAppOpsAllowed) {
+ if (!mPermitted) {
flags.add("na");
}
if (!flags.isEmpty()) {
@@ -142,7 +156,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
protected final UserInfoHelper mUserInfoHelper;
protected final SettingsHelper mSettingsHelper;
- protected final AppOpsHelper mAppOpsHelper;
+ protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final AppForegroundHelper mAppForegroundHelper;
protected final LocationManagerInternal mLocationManagerInternal;
@@ -155,14 +169,26 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
this::onLocationPackageBlacklistChanged;
- private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+ private final LocationPermissionsHelper.LocationPermissionsListener
+ mLocationPermissionsListener =
+ new LocationPermissionsHelper.LocationPermissionsListener() {
+ @Override
+ public void onLocationPermissionsChanged(String packageName) {
+ GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ public void onLocationPermissionsChanged(int uid) {
+ GnssListenerMultiplexer.this.onLocationPermissionsChanged(uid);
+ }
+ };
private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
this::onAppForegroundChanged;
protected GnssListenerMultiplexer(Injector injector) {
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
- mAppOpsHelper = injector.getAppOpsHelper();
+ mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mAppForegroundHelper = injector.getAppForegroundHelper();
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
@@ -209,7 +235,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
CallerIdentity identity = registration.getIdentity();
// TODO: this should be checking if the gps provider is enabled, not if location is enabled,
// but this is the same for now.
- return registration.isAppOpsAllowed()
+ return registration.isPermitted()
&& (registration.isForeground() || isBackgroundRestrictionExempt(identity))
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
@@ -242,7 +268,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.addListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
mAppForegroundHelper.addListener(mAppForegroundChangedListener);
}
@@ -258,7 +284,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.removeListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
}
@@ -280,8 +306,12 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onAppOpsChanged(String packageName) {
- updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+ private void onLocationPermissionsChanged(String packageName) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+ }
+
+ private void onLocationPermissionsChanged(int uid) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
private void onAppForegroundChanged(int uid, boolean foreground) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 77fefb4440d9..8004ec70aaf3 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -844,9 +844,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
if (data == null) {
// try again later
- // since this is delayed and not urgent we do not hold a wake lock her
+ // since this is delayed and not urgent we do not hold a wake lock here
+ // the arg2 below should not be 1 otherwise the wakelock will be under-locked.
mHandler.sendMessageDelayed(
- mHandler.obtainMessage(DOWNLOAD_PSDS_DATA, psdsType, 1, null),
+ mHandler.obtainMessage(DOWNLOAD_PSDS_DATA, psdsType, 0, null),
mPsdsBackOff.nextBackoffMillis());
}
@@ -2007,9 +2008,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private final class FusedLocationListener extends LocationChangeListener {
@Override
public void onLocationChanged(Location location) {
- if (LocationManager.FUSED_PROVIDER.equals(location.getProvider())) {
- injectBestLocation(location);
- }
+ injectBestLocation(location);
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 58e725ca152d..8e81f29550d6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -18,10 +18,9 @@ package com.android.server.location.gnss;
import static android.location.LocationManager.GPS_PROVIDER;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
import android.Manifest;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.Context;
import android.location.GnssAntennaInfo;
import android.location.GnssMeasurementCorrections;
@@ -47,10 +46,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
-import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
-import com.android.server.location.util.SettingsHelper;
import java.io.FileDescriptor;
import java.util.List;
@@ -68,9 +65,7 @@ public class GnssManagerService implements GnssNative.Callbacks {
}
private final Context mContext;
- private final SettingsHelper mSettingsHelper;
private final AppOpsHelper mAppOpsHelper;
- private final AppForegroundHelper mAppForegroundHelper;
private final LocationManagerInternal mLocationManagerInternal;
private final GnssLocationProvider mGnssLocationProvider;
@@ -109,9 +104,7 @@ public class GnssManagerService implements GnssNative.Callbacks {
GnssNative.initialize();
mContext = context.createAttributionContext(ATTRIBUTION_ID);
- mSettingsHelper = injector.getSettingsHelper();
mAppOpsHelper = injector.getAppOpsHelper();
- mAppForegroundHelper = injector.getAppForegroundHelper();
mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
if (gnssLocationProvider == null) {
@@ -192,9 +185,10 @@ public class GnssManagerService implements GnssNative.Callbacks {
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
String attributionTag) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
- if (!mAppOpsHelper.checkLocationAccess(identity, PERMISSION_FINE)) {
+ if (!mAppOpsHelper.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity)) {
return false;
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 9227a177f861..0815d46a735d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssMeasurementsEvent;
import android.location.GnssRequest;
import android.location.IGnssMeasurementsListener;
@@ -30,6 +30,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.SettingsHelper;
@@ -47,6 +48,7 @@ public class GnssMeasurementsProvider extends
GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements
SettingsHelper.GlobalSettingChangedListener {
+ private final AppOpsHelper mAppOpsHelper;
private final LocationUsageLogger mLogger;
private final GnssMeasurementProviderNative mNative;
@@ -57,6 +59,7 @@ public class GnssMeasurementsProvider extends
@VisibleForTesting
public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mLogger = injector.getLocationUsageLogger();
mNative = aNative;
}
@@ -163,7 +166,8 @@ public class GnssMeasurementsProvider extends
*/
public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onGnssMeasurementsReceived(event);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index a07fbe41c975..7dcffc664f52 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssNavigationMessage;
import android.location.IGnssNavigationMessageListener;
import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
/**
@@ -39,6 +40,7 @@ import com.android.server.location.util.Injector;
public class GnssNavigationMessageProvider extends
GnssListenerMultiplexer<Void, IGnssNavigationMessageListener, Void> {
+ private final AppOpsHelper mAppOpsHelper;
private final GnssNavigationMessageProviderNative mNative;
public GnssNavigationMessageProvider(Injector injector) {
@@ -49,6 +51,7 @@ public class GnssNavigationMessageProvider extends
public GnssNavigationMessageProvider(Injector injector,
GnssNavigationMessageProviderNative aNative) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mNative = aNative;
}
@@ -90,7 +93,8 @@ public class GnssNavigationMessageProvider extends
*/
public void onNavigationMessageAvailable(GnssNavigationMessage event) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onGnssNavigationMessageReceived(event);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index d33b05866877..19f79273c992 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssStatus;
import android.location.IGnssStatusListener;
import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@ import android.os.IBinder;
import android.stats.location.LocationStatsEnums;
import android.util.Log;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
@@ -35,10 +36,12 @@ import com.android.server.location.util.LocationUsageLogger;
*/
public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatusListener, Void> {
+ private final AppOpsHelper mAppOpsHelper;
private final LocationUsageLogger mLogger;
public GnssStatusProvider(Injector injector) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mLogger = injector.getLocationUsageLogger();
}
@@ -113,7 +116,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu
*/
public void onSvStatusChanged(GnssStatus gnssStatus) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onSvStatusChanged(gnssStatus);
} else {
return null;
@@ -126,7 +130,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu
*/
public void onNmeaReceived(long timestamp, String nmea) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onNmeaReceived(timestamp, nmea);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
index 70d83a9dd7dd..631dbbf0f1fd 100644
--- a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
@@ -27,7 +27,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.location.LocationManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -582,17 +581,9 @@ class GnssVisibilityControl {
mAppOps.finishOp(AppOpsManager.OP_MONITOR_LOCATION, uid, proxyAppPkgName);
mAppOps.finishOp(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, uid, proxyAppPkgName);
}
- sendHighPowerMonitoringBroadcast();
return true;
}
- private void sendHighPowerMonitoringBroadcast() {
- // Send an intent to notify that a high power request has been added/removed so that
- // the SystemUi checks the state of AppOps and updates the location icon accordingly.
- Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
private void handleEmergencyNfwNotification(NfwNotification nfwNotification) {
boolean isPermissionMismatched = false;
if (!nfwNotification.isRequestAccepted()) {
diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
index c853ceed351f..bd8bce8f6d52 100644
--- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
@@ -49,41 +49,34 @@ public abstract class BinderListenerRegistration<TRequest, TListener> extends
super(tag, request, callerIdentity, listener);
}
- /**
- * May be overridden in place of {@link #onRegister(Object)}. Should return true if registration
- * is successful, and false otherwise.
- */
- protected boolean onBinderRegister(Object key) {
- return true;
- }
-
- /**
- * May be overridden in place of {@link #onUnregister()}.
- */
- protected void onBinderUnregister(Object key) {}
-
@Override
- protected final boolean onRemovableRegister(Object key) {
- IBinder binder = getBinderFromKey(key);
+ protected final void onRemovableListenerRegister() {
+ IBinder binder = getBinderFromKey(getKey());
try {
binder.linkToDeath(this, 0);
- if (!onBinderRegister(key)) {
- binder.unlinkToDeath(this, 0);
- return false;
- }
- return true;
} catch (RemoteException e) {
- return false;
+ remove();
}
+
+ onBinderListenerRegister();
}
@Override
- protected final void onRemovableUnregister(Object key) {
- IBinder binder = getBinderFromKey(key);
- onBinderUnregister(key);
- binder.unlinkToDeath(this, 0);
+ protected final void onRemovableListenerUnregister() {
+ onBinderListenerUnregister();
+ getBinderFromKey(getKey()).unlinkToDeath(this, 0);
}
+ /**
+ * May be overridden in place of {@link #onRemovableListenerRegister()}.
+ */
+ protected void onBinderListenerRegister() {}
+
+ /**
+ * May be overridden in place of {@link #onRemovableListenerUnregister()}.
+ */
+ protected void onBinderListenerUnregister() {}
+
@Override
public void binderDied() {
try {
@@ -98,7 +91,7 @@ public abstract class BinderListenerRegistration<TRequest, TListener> extends
}
}
- private IBinder getBinderFromKey(Object key) {
+ private static IBinder getBinderFromKey(Object key) {
if (key instanceof IBinder) {
return (IBinder) key;
} else if (key instanceof BinderKey) {
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index f6ef85902c4f..528cf8acd5b3 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -21,12 +21,13 @@ import android.annotation.Nullable;
import android.os.Binder;
import android.os.Build;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.internal.util.Preconditions;
-import com.android.server.location.listeners.ListenerRegistration.ListenerOperation;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -40,8 +41,8 @@ import java.util.function.Predicate;
* A base class to multiplex client listener registrations within system server. Registrations are
* divided into two categories, active registrations and inactive registrations, as defined by
* {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
- * {@link #updateRegistrations(Predicate)} or {@link #updateRegistration(Object, Predicate)} must be
- * invoked and return true for any registration whose active state may have changed.
+ * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
+ * whose active state may have changed.
*
* Callbacks invoked for various changes will always be ordered according to this lifecycle list:
*
@@ -186,9 +187,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
/**
* Adds a new registration with the given key. Registration may fail if
- * {@link ListenerRegistration#onRegister(Object)} returns false, in which case the
- * registration will not be added. This method cannot be called to add a registration
- * re-entrantly.
+ * {@link ListenerRegistration#onRegister(Object)} returns false, in which case the registration
+ * will not be added. This method cannot be called to add a registration re-entrantly.
*/
protected final void addRegistration(@NonNull TKey key, @NonNull TRegistration registration) {
Objects.requireNonNull(key);
@@ -203,22 +203,12 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
// callbacks. further, we buffer service updates since adding a registration may
// involve removing a prior registration. note that try-with-resources ordering is
// meaningful here as well. we want to close the reentrancy guard first, as this may
- // generate addition service updates, then close the update service buffer.
+ // generate additional service updates, then close the update service buffer.
long identity = Binder.clearCallingIdentity();
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
- if (mRegistrations.isEmpty()) {
- onRegister();
- }
-
- if (!registration.onRegister(key)) {
- if (mRegistrations.isEmpty()) {
- onUnregister();
- }
-
- return;
- }
+ boolean wasEmpty = mRegistrations.isEmpty();
int index = mRegistrations.indexOfKey(key);
if (index >= 0) {
@@ -228,6 +218,10 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
mRegistrations.put(key, registration);
}
+ if (wasEmpty) {
+ onRegister();
+ }
+ registration.onRegister(key);
onRegistrationAdded(key, registration);
onRegistrationActiveChanged(registration);
} finally {
@@ -270,11 +264,12 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
// callbacks. further, we buffer service updates since chains of removeLater()
// invocations could result in multiple service updates. note that try-with-resources
// ordering is meaningful here as well. we want to close the reentrancy guard first, as
- // this may generate addition service updates, then close the update service buffer.
+ // this may generate additional service updates, then close the update service buffer.
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TKey key = mRegistrations.keyAt(i);
if (predicate.test(key)) {
removeRegistration(key, mRegistrations.valueAt(i));
@@ -293,7 +288,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
* completely at some later time.
*/
protected final void removeRegistration(@NonNull Object key,
- @NonNull ListenerRegistration registration) {
+ @NonNull ListenerRegistration<?, ?> registration) {
synchronized (mRegistrations) {
int index = mRegistrations.indexOfKey(key);
if (index < 0) {
@@ -327,8 +322,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
// callbacks themselves do not re-enter, as this could lead to out-of-order callbacks.
// further, we buffer service updates since chains of removeLater() invocations could result
// in multiple service updates. note that try-with-resources ordering is meaningful here as
- // well. we want to close the reentrancy guard first, as this may generate addition service
- // updates, then close the update service buffer.
+ // well. we want to close the reentrancy guard first, as this may generate additional
+ // service updates, then close the update service buffer.
long identity = Binder.clearCallingIdentity();
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
@@ -359,7 +354,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
ArrayList<TRegistration> actives = new ArrayList<>(mRegistrations.size());
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
actives.add(registration);
@@ -393,6 +389,29 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
/**
+ * Evaluates the given predicate for all registrations, and forces an {@link #updateService()}
+ * if any predicate returns true for an active registration. The predicate will always be
+ * evaluated for all registrations, even inactive registrations, or if it has already returned
+ * true for a prior registration.
+ */
+ protected final void updateService(Predicate<TRegistration> predicate) {
+ synchronized (mRegistrations) {
+ boolean updateService = false;
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
+ TRegistration registration = mRegistrations.valueAt(i);
+ if (predicate.test(registration) && registration.isActive()) {
+ updateService = true;
+ }
+ }
+
+ if (updateService) {
+ updateService();
+ }
+ }
+ }
+
+ /**
* Begins buffering calls to {@link #updateService()} until {@link UpdateServiceLock#close()}
* is called. This is useful to prevent extra work when combining multiple calls (for example,
* buffering {@code updateService()} until after multiple adds/removes/updates occur.
@@ -412,13 +431,14 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
// since updating a registration can invoke a variety of callbacks, we need to ensure
// those callbacks themselves do not re-enter, as this could lead to out-of-order
// callbacks. note that try-with-resources ordering is meaningful here as well. we want
- // to close the reentrancy guard first, as this may generate addition service updates,
+ // to close the reentrancy guard first, as this may generate additional service updates,
// then close the update service buffer.
long identity = Binder.clearCallingIdentity();
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (predicate.test(registration)) {
onRegistrationActiveChanged(registration);
@@ -430,33 +450,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
}
- /**
- * Evaluates the predicate on the registration with the given key. The predicate should return
- * true if the active state of the registration may have changed as a result. Any
- * {@link #updateService()} invocations made while this method is executing will be deferred
- * until after the method is complete so as to avoid redundant work.
- */
- protected final void updateRegistration(TKey key, @NonNull Predicate<TRegistration> predicate) {
- synchronized (mRegistrations) {
- // since updating a registration can invoke a variety of callbacks, we need to ensure
- // those callbacks themselves do not re-enter, as this could lead to out-of-order
- // callbacks. note that try-with-resources ordering is meaningful here as well. we want
- // to close the reentrancy guard first, as this may generate addition service updates,
- // then close the update service buffer.
- long identity = Binder.clearCallingIdentity();
- try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
- ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
-
- TRegistration registration = mRegistrations.get(key);
- if (registration != null && predicate.test(registration)) {
- onRegistrationActiveChanged(registration);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
@GuardedBy("mRegistrations")
private void onRegistrationActiveChanged(TRegistration registration) {
if (Build.IS_DEBUGGABLE) {
@@ -495,7 +488,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
synchronized (mRegistrations) {
long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
ListenerOperation<TListener> operation = function.apply(registration);
@@ -511,8 +505,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
/**
- * Executes the given delivery operation for all active listeners. This is a convenience
- * function equivalent to:
+ * Executes the given operation for all active listeners. This is a convenience function
+ * equivalent to:
* <pre>
* deliverToListeners(registration -> operation);
* </pre>
@@ -521,7 +515,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
synchronized (mRegistrations) {
long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
execute(registration, operation);
@@ -555,7 +550,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
ipw.println("listeners:");
ipw.increaseIndent();
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
ipw.print(registration);
if (!registration.isActive()) {
@@ -596,23 +592,33 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
*/
private final class ReentrancyGuard implements AutoCloseable {
+ @GuardedBy("mRegistrations")
private int mGuardCount;
- private @Nullable ArrayList<Pair<Object, ListenerRegistration>> mScheduledRemovals;
+ @GuardedBy("mRegistrations")
+ private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
ReentrancyGuard() {
mGuardCount = 0;
mScheduledRemovals = null;
}
+ @GuardedBy("mRegistrations")
boolean isReentrant() {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mRegistrations));
+ }
return mGuardCount != 0;
}
- void markForRemoval(Object key, ListenerRegistration registration) {
+ @GuardedBy("mRegistrations")
+ void markForRemoval(Object key, ListenerRegistration<?, ?> registration) {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mRegistrations));
+ }
Preconditions.checkState(isReentrant());
if (mScheduledRemovals == null) {
- mScheduledRemovals = new ArrayList<>(mRegistrations.size());
+ mScheduledRemovals = new ArraySet<>(mRegistrations.size());
}
mScheduledRemovals.add(new Pair<>(key, registration));
}
@@ -624,7 +630,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
@Override
public void close() {
- ArrayList<Pair<Object, ListenerRegistration>> scheduledRemovals = null;
+ ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
Preconditions.checkState(mGuardCount > 0);
if (--mGuardCount == 0) {
@@ -634,8 +640,10 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
if (scheduledRemovals != null) {
try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
- for (int i = 0; i < scheduledRemovals.size(); i++) {
- Pair<Object, ListenerRegistration> pair = scheduledRemovals.get(i);
+ final int size = scheduledRemovals.size();
+ for (int i = 0; i < size; i++) {
+ Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
+ i);
removeRegistration(pair.first, pair.second);
}
}
diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
index 2a77c8112776..0bdd1316d265 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.location.util.identity.CallerIdentity;
import android.os.Process;
+import com.android.internal.listeners.ListenerExecutor;
import com.android.server.FgThread;
import java.util.Objects;
@@ -36,33 +37,21 @@ import java.util.concurrent.Executor;
* @param <TRequest> request type
* @param <TListener> listener type
*/
-public class ListenerRegistration<TRequest, TListener> {
-
- /**
- * An listener operation to perform.
- *
- * @param <TListener> listener type
- */
- public interface ListenerOperation<TListener> {
- /**
- * Performs the operation on the given listener
- */
- void operate(TListener listener) throws Exception;
- }
+public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor {
private final Executor mExecutor;
private final @Nullable TRequest mRequest;
- private final CallerIdentity mCallerIdentity;
+ private final CallerIdentity mIdentity;
private boolean mActive;
private volatile @Nullable TListener mListener;
- protected ListenerRegistration(@Nullable TRequest request, CallerIdentity callerIdentity,
+ protected ListenerRegistration(@Nullable TRequest request, CallerIdentity identity,
TListener listener) {
// if a client is in the same process as us, binder calls will execute synchronously and
// we shouldn't run callbacks directly since they might be run under lock and deadlock
- if (callerIdentity.getPid() == Process.myPid()) {
+ if (identity.getPid() == Process.myPid()) {
// there's a slight loophole here for pending intents - pending intent callbacks can
// always be run on the direct executor since they're always asynchronous, but honestly
// you shouldn't be using pending intent callbacks within the same process anyways
@@ -72,11 +61,15 @@ public class ListenerRegistration<TRequest, TListener> {
}
mRequest = request;
- mCallerIdentity = Objects.requireNonNull(callerIdentity);
+ mIdentity = Objects.requireNonNull(identity);
mActive = false;
mListener = Objects.requireNonNull(listener);
}
+ protected final Executor getExecutor() {
+ return mExecutor;
+ }
+
/**
* Returns the request associated with this listener, or null if one wasn't supplied.
*/
@@ -88,17 +81,13 @@ public class ListenerRegistration<TRequest, TListener> {
* Returns the listener identity.
*/
public final CallerIdentity getIdentity() {
- return mCallerIdentity;
+ return mIdentity;
}
/**
- * May be overridden by subclasses. Invoked when registration occurs. If this returns true,
- * then registration will complete successfully. If this returns false, registration will fail,
- * and {@link #onUnregister()} will not be called.
+ * May be overridden by subclasses. Invoked when registration occurs.
*/
- protected boolean onRegister(Object key) {
- return true;
- }
+ protected void onRegister(Object key) {}
/**
* May be overridden by subclasses. Invoked when unregistration occurs.
@@ -137,35 +126,19 @@ public class ListenerRegistration<TRequest, TListener> {
final void unregisterInternal() {
mListener = null;
- }
-
- final void executeInternal(@NonNull ListenerOperation<TListener> operation) {
- Objects.requireNonNull(operation);
- mExecutor.execute(() -> {
- TListener listener = mListener;
- if (listener == null) {
- return;
- }
-
- try {
- operation.operate(listener);
- } catch (Exception e) {
- onOperationFailure(operation, e);
- }
- });
+ onListenerUnregister();
}
/**
- * Invoked when an operation throws an exception, and run on the same executor as the operation.
+ * May be overridden by subclasses, however should rarely be needed. Invoked when the listener
+ * associated with this registration is unregistered, which may occur before the registration
+ * itself is unregistered. This immediately prevents the listener from being further invoked
+ * even if the various bookkeeping associated with unregistration has not occurred yet.
*/
- protected void onOperationFailure(@NonNull ListenerOperation<TListener> operation,
- @NonNull Exception exception) {
- if (exception instanceof RuntimeException) {
- throw (RuntimeException) exception;
- } else {
- // listeners should not throw exceptions that their registrations cannot handle
- throw new UnsupportedOperationException(exception);
- }
+ protected void onListenerUnregister() {};
+
+ final void executeInternal(@NonNull ListenerOperation<TListener> operation) {
+ executeSafely(mExecutor, () -> mListener, operation);
}
@Override
diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
index 53391512f2b2..b5d2ef6a72ec 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -47,37 +47,28 @@ public abstract class PendingIntentListenerRegistration<TRequest, TListener> ext
super(tag, request, callerIdentity, listener);
}
- /**
- * May be overridden in place of {@link #onRegister(Object)}. Should return true if registration
- * is successful, and false otherwise.
- */
- protected boolean onPendingIntentRegister(Object key) {
- return true;
- }
-
- /**
- * May be overridden in place of {@link #onUnregister()}.
- */
- protected void onPendingIntentUnregister(Object key) {}
-
@Override
- protected final boolean onRemovableRegister(Object key) {
- PendingIntent pendingIntent = getPendingIntentFromKey(key);
- pendingIntent.registerCancelListener(this);
- if (!onPendingIntentRegister(key)) {
- pendingIntent.unregisterCancelListener(this);
- return false;
- }
- return true;
+ protected final void onRemovableListenerRegister() {
+ getPendingIntentFromKey(getKey()).registerCancelListener(this);
+ onPendingIntentListenerRegister();
}
@Override
- protected final void onRemovableUnregister(Object key) {
- PendingIntent pendingIntent = getPendingIntentFromKey(key);
- onPendingIntentUnregister(key);
- pendingIntent.unregisterCancelListener(this);
+ protected final void onRemovableListenerUnregister() {
+ onPendingIntentListenerUnregister();
+ getPendingIntentFromKey(getKey()).unregisterCancelListener(this);
}
+ /**
+ * May be overridden in place of {@link #onRemovableListenerRegister()}.
+ */
+ protected void onPendingIntentListenerRegister() {}
+
+ /**
+ * May be overridden in place of {@link #onRemovableListenerUnregister()}.
+ */
+ protected void onPendingIntentListenerUnregister() {}
+
@Override
public void onCancelled(PendingIntent intent) {
if (Log.isLoggable(mTag, Log.DEBUG)) {
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index f40a61709664..6a815ead9f9f 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -47,7 +47,7 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends
* with. Often this is easiest to accomplish by defining registration subclasses as non-static
* inner classes of the multiplexer they are to be used with.
*/
- protected abstract ListenerMultiplexer<?, TRequest, TListener, ?, ?> getOwner();
+ protected abstract ListenerMultiplexer<?, ? super TRequest, ? super TListener, ?, ?> getOwner();
/**
* Returns the key associated with this registration. May not be invoked before
@@ -58,50 +58,41 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends
}
/**
- * Removes this registration. May not be invoked before {@link #onRegister(Object)} or after
- * {@link #onUnregister()}.
+ * Removes this registration. Does nothing if invoked before {@link #onRegister(Object)} or
+ * after {@link #onUnregister()}. It is safe to invoke this from within either function.
*/
public final void remove() {
- getOwner().removeRegistration(Objects.requireNonNull(mKey), this);
- }
-
- @Override
- protected void onOperationFailure(ListenerOperation<TListener> operation, Exception e) {
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else {
- Log.w(mTag,
- "registration " + getIdentity() + " removed due to unexpected exception",
- e);
- remove();
+ Object key = mKey;
+ if (key != null) {
+ getOwner().removeRegistration(key, this);
}
}
- /**
- * May be overridden in place of {@link #onRegister(Object)}.
- */
- protected boolean onRemovableRegister(Object key) {
- return true;
+ @Override
+ public <Listener> void onOperationFailure(ListenerOperation<Listener> operation, Exception e) {
+ Log.w(mTag, "registration " + getIdentity() + " removed due to unexpected exception", e);
+ remove();
}
- /**
- * May be overridden in place of {@link #onUnregister()}.
- */
- protected void onRemovableUnregister(Object key) {}
-
@Override
- protected final boolean onRegister(Object key) {
+ protected final void onRegister(Object key) {
mKey = Objects.requireNonNull(key);
- if (!onRemovableRegister(key)) {
- mKey = null;
- return false;
- }
- return true;
+ onRemovableListenerRegister();
}
@Override
protected final void onUnregister() {
- onRemovableUnregister(mKey);
+ onRemovableListenerUnregister();
mKey = null;
}
+
+ /**
+ * May be overridden in place of {@link #onRegister(Object)}.
+ */
+ protected void onRemovableListenerRegister() {}
+
+ /**
+ * May be overridden in place of {@link #onUnregister()}.
+ */
+ protected void onRemovableListenerUnregister() {}
}
diff --git a/services/core/java/com/android/server/location/util/AppOpsHelper.java b/services/core/java/com/android/server/location/util/AppOpsHelper.java
index 3e42f27da78c..1578289d53b4 100644
--- a/services/core/java/com/android/server/location/util/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/AppOpsHelper.java
@@ -16,15 +16,8 @@
package com.android.server.location.util;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-
-import android.app.AppOpsManager;
import android.location.util.identity.CallerIdentity;
-import com.android.server.location.LocationPermissions;
-import com.android.server.location.LocationPermissions.PermissionLevel;
-
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -70,84 +63,27 @@ public abstract class AppOpsHelper {
}
/**
- * Checks if the given identity may have locations delivered without noting that a location is
- * being delivered. This is a looser guarantee than
- * {@link #noteLocationAccess(CallerIdentity, int)}, and this function does not validate package
- * arguments and so should not be used with unvalidated arguments or before actually delivering
- * locations.
- *
- * @see AppOpsManager#checkOpNoThrow(int, int, String)
- */
- public final boolean checkLocationAccess(CallerIdentity callerIdentity,
- @PermissionLevel int permissionLevel) {
- if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
- return false;
- }
-
- return checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), callerIdentity);
- }
-
- /**
- * Notes location access to the given identity, ie, location delivery. This method should be
- * called right before a location is delivered, and if it returns false, the location should not
- * be delivered.
- */
- public final boolean noteLocationAccess(CallerIdentity identity,
- @PermissionLevel int permissionLevel) {
- if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
- return false;
- }
-
- return noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
- }
-
- /**
- * Notifies app ops that the given identity is using location at normal/low power levels. If
- * this function returns false, do not later call
- * {@link #stopLocationMonitoring(CallerIdentity)}.
+ * Starts the given appop.
*/
- public final boolean startLocationMonitoring(CallerIdentity identity) {
- return startOpNoThrow(OP_MONITOR_LOCATION, identity);
- }
+ public abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is no longer using location at normal/low power
- * levels.
+ * Finishes the given appop.
*/
- public final void stopLocationMonitoring(CallerIdentity identity) {
- finishOp(OP_MONITOR_LOCATION, identity);
- }
+ public abstract void finishOp(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is using location at high levels. If this function
- * returns false, do not later call {@link #stopLocationMonitoring(CallerIdentity)}.
+ * Checks the given appop.
*/
- public final boolean startHighPowerLocationMonitoring(CallerIdentity identity) {
- return startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity);
- }
+ public abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is no longer using location at high power levels.
+ * Notes the given appop (and may throw a security exception).
*/
- public final void stopHighPowerLocationMonitoring(CallerIdentity identity) {
- finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
- }
+ public abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
/**
- * Notes access to any mock location APIs. If this call returns false, access to the APIs should
- * silently fail.
+ * Notes the given appop.
*/
- public final boolean noteMockLocationAccess(CallerIdentity callerIdentity) {
- return noteOp(AppOpsManager.OP_MOCK_LOCATION, callerIdentity);
- }
-
- protected abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
- protected abstract void finishOp(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
+ public abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
}
diff --git a/services/core/java/com/android/server/location/util/Injector.java b/services/core/java/com/android/server/location/util/Injector.java
index e16df5dc26cd..379b303bbfc3 100644
--- a/services/core/java/com/android/server/location/util/Injector.java
+++ b/services/core/java/com/android/server/location/util/Injector.java
@@ -17,6 +17,7 @@
package com.android.server.location.util;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.LocationRequestStatistics;
/**
* Injects various location dependencies so that they may be controlled by tests.
@@ -30,15 +31,27 @@ public interface Injector {
/** Returns an AppOpsHelper. */
AppOpsHelper getAppOpsHelper();
+ /** Returns a LocationPermissionsHelper. */
+ LocationPermissionsHelper getLocationPermissionsHelper();
+
/** Returns a SettingsHelper. */
SettingsHelper getSettingsHelper();
/** Returns an AppForegroundHelper. */
AppForegroundHelper getAppForegroundHelper();
- /** Returns a LocationUsageLogger. */
- LocationUsageLogger getLocationUsageLogger();
+ /** Returns a LocationPowerSaveModeHelper. */
+ LocationPowerSaveModeHelper getLocationPowerSaveModeHelper();
+
+ /** Returns a ScreenInteractiveHelper. */
+ ScreenInteractiveHelper getScreenInteractiveHelper();
/** Returns a LocationAttributionHelper. */
LocationAttributionHelper getLocationAttributionHelper();
+
+ /** Returns a LocationUsageLogger. */
+ LocationUsageLogger getLocationUsageLogger();
+
+ /** Returns a LocationRequestStatistics. */
+ LocationRequestStatistics getLocationRequestStatistics();
}
diff --git a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
index 8fe09412c166..bc3ac0ff2e48 100644
--- a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
@@ -16,9 +16,16 @@
package com.android.server.location.util;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
import android.location.util.identity.CallerIdentity;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -83,7 +90,7 @@ public class LocationAttributionHelper {
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
if (keySet.add(new ProviderListener(provider, key)) && empty) {
- if (!mAppOpsHelper.startLocationMonitoring(identity)) {
+ if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
mAttributions.remove(identity);
}
}
@@ -99,7 +106,7 @@ public class LocationAttributionHelper {
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
&& keySet.isEmpty()) {
mAttributions.remove(identity);
- mAppOpsHelper.stopLocationMonitoring(identity);
+ mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
}
}
@@ -113,14 +120,18 @@ public class LocationAttributionHelper {
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
if (keySet.add(new ProviderListener(provider, key)) && empty) {
- if (!mAppOpsHelper.startHighPowerLocationMonitoring(identity)) {
+ if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
+ if (D) {
+ Log.v(TAG, "starting high power location attribution for " + identity);
+ }
+ } else {
mHighPowerAttributions.remove(identity);
}
}
}
/**
- * Report high power location usage has stopped for the given caller on the given provider,
+ * Report high power location usage has stopped for the given caller on the given provider,
* with a unique key.
*/
public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider,
@@ -128,8 +139,11 @@ public class LocationAttributionHelper {
Set<ProviderListener> keySet = mHighPowerAttributions.get(identity);
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
&& keySet.isEmpty()) {
+ if (D) {
+ Log.v(TAG, "stopping high power location attribution for " + identity);
+ }
mHighPowerAttributions.remove(identity);
- mAppOpsHelper.stopHighPowerLocationMonitoring(identity);
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
}
}
}
diff --git a/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
new file mode 100644
index 000000000000..daf56797c0c9
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+
+import android.location.util.identity.CallerIdentity;
+
+import com.android.server.location.LocationPermissions;
+import com.android.server.location.LocationPermissions.PermissionLevel;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides helpers and listeners for appops.
+ */
+public abstract class LocationPermissionsHelper {
+
+ /**
+ * Listener for current user changes.
+ */
+ public interface LocationPermissionsListener {
+
+ /**
+ * Called when something has changed about location permissions for the given package.
+ */
+ void onLocationPermissionsChanged(String packageName);
+
+ /**
+ * Called when something has changed about location permissions for the given uid.
+ */
+ void onLocationPermissionsChanged(int uid);
+ }
+
+ private final CopyOnWriteArrayList<LocationPermissionsListener> mListeners;
+ private final AppOpsHelper mAppOps;
+
+ public LocationPermissionsHelper(AppOpsHelper appOps) {
+ mListeners = new CopyOnWriteArrayList<>();
+ mAppOps = appOps;
+
+ mAppOps.addListener(this::onAppOpsChanged);
+ }
+
+ protected final void notifyLocationPermissionsChanged(String packageName) {
+ for (LocationPermissionsListener listener : mListeners) {
+ listener.onLocationPermissionsChanged(packageName);
+ }
+ }
+
+ protected final void notifyLocationPermissionsChanged(int uid) {
+ for (LocationPermissionsListener listener : mListeners) {
+ listener.onLocationPermissionsChanged(uid);
+ }
+ }
+
+ private void onAppOpsChanged(String packageName) {
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ /**
+ * Adds a listener for location permissions events. Callbacks occur on an unspecified thread.
+ */
+ public final void addListener(LocationPermissionsListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for location permissions events.
+ */
+ public final void removeListener(LocationPermissionsListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Returns true if the given identity may access location at the given permissions level, taking
+ * into account both permissions and appops.
+ */
+ public final boolean hasLocationPermissions(@PermissionLevel int permissionLevel,
+ CallerIdentity identity) {
+ if (permissionLevel == PERMISSION_NONE) {
+ return false;
+ }
+
+ if (!hasPermission(LocationPermissions.asPermission(permissionLevel), identity)) {
+ return false;
+ }
+
+ return mAppOps.checkOpNoThrow(permissionLevel, identity);
+ }
+
+ protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
+}
diff --git a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
new file mode 100644
index 000000000000..a9a8c50f11dc
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.os.PowerManager.LocationPowerSaveMode;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public abstract class LocationPowerSaveModeHelper {
+
+ /**
+ * Listener for location power save mode changes.
+ */
+ public interface LocationPowerSaveModeChangedListener {
+ /**
+ * Called when the location power save mode changes.
+ */
+ void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode);
+ }
+
+ private final CopyOnWriteArrayList<LocationPowerSaveModeChangedListener> mListeners;
+
+ public LocationPowerSaveModeHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Add a listener for changes to location power save mode. Callbacks occur on an unspecified
+ * thread.
+ */
+ public final void addListener(LocationPowerSaveModeChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for changes to location power save mode.
+ */
+ public final void removeListener(LocationPowerSaveModeChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ protected final void notifyLocationPowerSaveModeChanged(
+ @LocationPowerSaveMode int locationPowerSaveMode) {
+ for (LocationPowerSaveModeChangedListener listener : mListeners) {
+ listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
+ }
+ }
+
+ /**
+ * Returns the current location power save mode.
+ */
+ @LocationPowerSaveMode
+ public abstract int getLocationPowerSaveMode();
+}
diff --git a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
new file mode 100644
index 000000000000..d47bce31ed23
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public abstract class ScreenInteractiveHelper {
+
+ /**
+ * Listener for screen interactive changes.
+ */
+ public interface ScreenInteractiveChangedListener {
+ /**
+ * Called when the screen interative state changes.
+ */
+ void onScreenInteractiveChanged(boolean isInteractive);
+ }
+
+ private final CopyOnWriteArrayList<ScreenInteractiveChangedListener> mListeners;
+
+ public ScreenInteractiveHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Add a listener for changes to screen interactive state. Callbacks occur on an unspecified
+ * thread.
+ */
+ public final void addListener(ScreenInteractiveChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for changes to screen interactive state.
+ */
+ public final void removeListener(ScreenInteractiveChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ protected final void notifyScreenInteractiveChanged(boolean interactive) {
+ for (ScreenInteractiveChangedListener listener : mListeners) {
+ listener.onScreenInteractiveChanged(interactive);
+ }
+ }
+
+ /**
+ * Returns true if the screen is currently interactive, and false otherwise.
+ */
+ public abstract boolean isInteractive();
+}
diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
index 243ad931e600..cfb7697a8dfc 100644
--- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
@@ -16,12 +16,8 @@
package com.android.server.location.util;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-
import android.app.AppOpsManager;
import android.content.Context;
-import android.content.Intent;
-import android.location.LocationManager;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
@@ -54,43 +50,32 @@ public class SystemAppOpsHelper extends AppOpsHelper {
AppOpsManager.OP_COARSE_LOCATION,
null,
AppOpsManager.WATCH_FOREGROUND_CHANGES,
- new AppOpsManager.OnOpChangedInternalListener() {
- public void onOpChanged(int op, String packageName) {
- // invoked on ui thread, move to fg thread so ui thread isn't blocked
- FgThread.getHandler().post(() -> notifyAppOpChanged(packageName));
- }
+ (op, packageName) -> {
+ // invoked on ui thread, move to fg thread so ui thread isn't blocked
+ FgThread.getHandler().post(() -> notifyAppOpChanged(packageName));
});
}
@Override
- protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
try {
- boolean allowed = mAppOps.startOpNoThrow(
+ return mAppOps.startOpNoThrow(
appOp,
callerIdentity.getUid(),
callerIdentity.getPackageName(),
false,
callerIdentity.getAttributionTag(),
callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED;
-
- if (allowed && appOp == OP_MONITOR_HIGH_POWER_LOCATION) {
- // notify of possible location icon change
- mContext.sendBroadcast(
- new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION).addFlags(
- Intent.FLAG_RECEIVER_FOREGROUND));
- }
-
- return allowed;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+ public void finishOp(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -100,20 +85,13 @@ public class SystemAppOpsHelper extends AppOpsHelper {
callerIdentity.getUid(),
callerIdentity.getPackageName(),
callerIdentity.getAttributionTag());
-
- if (appOp == OP_MONITOR_HIGH_POWER_LOCATION) {
- // notify of possible location icon change
- mContext.sendBroadcast(
- new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION).addFlags(
- Intent.FLAG_RECEIVER_FOREGROUND));
- }
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -128,7 +106,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -145,7 +123,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
new file mode 100644
index 000000000000..b9c0ddef04ab
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.Context;
+import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
+
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for location permissions, including appops.
+ */
+public class SystemLocationPermissionsHelper extends LocationPermissionsHelper {
+
+ private final Context mContext;
+
+ private boolean mInited;
+
+ public SystemLocationPermissionsHelper(Context context, AppOpsHelper appOps) {
+ super(appOps);
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mInited) {
+ return;
+ }
+
+ mContext.getPackageManager().addOnPermissionsChangeListener(
+ uid -> {
+ // invoked on ui thread, move to fg thread so ui thread isn't blocked
+ FgThread.getHandler().post(() -> notifyLocationPermissionsChanged(uid));
+ });
+ mInited = true;
+ }
+
+ @Override
+ protected boolean hasPermission(String permission, CallerIdentity callerIdentity) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mContext.checkPermission(permission, callerIdentity.getPid(),
+ callerIdentity.getUid()) == PERMISSION_GRANTED;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
new file mode 100644
index 000000000000..c8d8202157f0
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public class SystemLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper implements
+ Consumer<PowerSaveState> {
+
+ private final Context mContext;
+ private boolean mReady;
+
+ @LocationPowerSaveMode
+ private volatile int mLocationPowerSaveMode;
+
+ public SystemLocationPowerSaveModeHelper(Context context) {
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mReady) {
+ return;
+ }
+
+ LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver(
+ PowerManager.ServiceType.LOCATION, this);
+ mLocationPowerSaveMode = Objects.requireNonNull(
+ mContext.getSystemService(PowerManager.class)).getLocationPowerSaveMode();
+
+ mReady = true;
+ }
+
+ @Override
+ public void accept(PowerSaveState powerSaveState) {
+ int locationPowerSaveMode;
+ if (!powerSaveState.batterySaverEnabled) {
+ locationPowerSaveMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+ } else {
+ locationPowerSaveMode = powerSaveState.locationMode;
+ }
+
+ if (locationPowerSaveMode == mLocationPowerSaveMode) {
+ return;
+ }
+
+ mLocationPowerSaveMode = locationPowerSaveMode;
+
+ // invoked on ui thread, move to fg thread so we don't block the ui thread
+ FgThread.getHandler().post(() -> notifyLocationPowerSaveModeChanged(locationPowerSaveMode));
+ }
+
+ @LocationPowerSaveMode
+ @Override
+ public int getLocationPowerSaveMode() {
+ Preconditions.checkState(mReady);
+ return mLocationPowerSaveMode;
+ }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
new file mode 100644
index 000000000000..70cedac20868
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public class SystemScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+ private final Context mContext;
+
+ private boolean mReady;
+
+ private volatile boolean mIsInteractive;
+
+ public SystemScreenInteractiveHelper(Context context) {
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mReady) {
+ return;
+ }
+
+ IntentFilter screenIntentFilter = new IntentFilter();
+ screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean interactive;
+ if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+ interactive = true;
+ } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ interactive = false;
+ } else {
+ return;
+ }
+
+ onScreenInteractiveChanged(interactive);
+ }
+ }, UserHandle.ALL, screenIntentFilter, null, FgThread.getHandler());
+
+ mReady = true;
+ }
+
+ private void onScreenInteractiveChanged(boolean interactive) {
+ if (interactive == mIsInteractive) {
+ return;
+ }
+
+ mIsInteractive = interactive;
+ notifyScreenInteractiveChanged(interactive);
+ }
+
+ @Override
+ public boolean isInteractive() {
+ Preconditions.checkState(mReady);
+ return mIsInteractive;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index bacc43bfd502..e56884832f0f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1615,6 +1615,7 @@ public class LockSettingsService extends ILockSettings.Stub {
synchronized (mSeparateChallengeLock) {
if (!setLockCredentialInternal(credential, savedCredential,
userId, /* isLockTiedToParent= */ false)) {
+ scheduleGc();
return false;
}
setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
@@ -1625,6 +1626,7 @@ public class LockSettingsService extends ILockSettings.Stub {
setDeviceUnlockedForUser(userId);
}
notifySeparateProfileChallengeChanged(userId);
+ scheduleGc();
return true;
}
@@ -1964,7 +1966,11 @@ public class LockSettingsService extends ILockSettings.Stub {
public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId,
ICheckCredentialProgressCallback progressCallback) {
checkPasswordReadPermission(userId);
- return doVerifyCredential(credential, CHALLENGE_NONE, 0, userId, progressCallback);
+ try {
+ return doVerifyCredential(credential, CHALLENGE_NONE, 0, userId, progressCallback);
+ } finally {
+ scheduleGc();
+ }
}
@Override
@@ -1977,8 +1983,12 @@ public class LockSettingsService extends ILockSettings.Stub {
challengeType = CHALLENGE_NONE;
}
- return doVerifyCredential(credential, challengeType, challenge, userId,
- null /* progressCallback */);
+ try {
+ return doVerifyCredential(credential, challengeType, challenge, userId,
+ null /* progressCallback */);
+ } finally {
+ scheduleGc();
+ }
}
private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
@@ -2069,6 +2079,8 @@ public class LockSettingsService extends ILockSettings.Stub {
| BadPaddingException | CertificateException | IOException e) {
Slog.e(TAG, "Failed to decrypt child profile key", e);
throw new IllegalStateException("Unable to get tied profile token");
+ } finally {
+ scheduleGc();
}
}
@@ -2687,7 +2699,7 @@ public class LockSettingsService extends ILockSettings.Stub {
// If there are multiple profiles in the same account, ensure we only generate the
// challenge once.
challengeType = CHALLENGE_INTERNAL;
- challenge = mContext.getSystemService(FaceManager.class).generateChallenge();
+ challenge = mContext.getSystemService(FaceManager.class).generateChallengeBlocking();
}
final AuthenticationResult authResult;
@@ -2980,27 +2992,31 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
checkPasswordReadPermission(userId);
- if (isManagedProfileWithUnifiedLock(userId)) {
- try {
- currentCredential = getDecryptedPasswordForTiedProfile(userId);
- } catch (Exception e) {
- Slog.e(TAG, "Failed to get work profile credential", e);
- return null;
- }
- }
- synchronized (mSpManager) {
- if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
- Slog.w(TAG, "Synthetic password not enabled");
- return null;
+ try {
+ if (isManagedProfileWithUnifiedLock(userId)) {
+ try {
+ currentCredential = getDecryptedPasswordForTiedProfile(userId);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to get work profile credential", e);
+ return null;
+ }
}
- long handle = getSyntheticPasswordHandleLocked(userId);
- AuthenticationResult auth = mSpManager.unwrapPasswordBasedSyntheticPassword(
- getGateKeeperService(), handle, currentCredential, userId, null);
- if (auth.authToken == null) {
- Slog.w(TAG, "Current credential is incorrect");
- return null;
+ synchronized (mSpManager) {
+ if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
+ Slog.w(TAG, "Synthetic password not enabled");
+ return null;
+ }
+ long handle = getSyntheticPasswordHandleLocked(userId);
+ AuthenticationResult auth = mSpManager.unwrapPasswordBasedSyntheticPassword(
+ getGateKeeperService(), handle, currentCredential, userId, null);
+ if (auth.authToken == null) {
+ Slog.w(TAG, "Current credential is incorrect");
+ return null;
+ }
+ return auth.authToken.derivePasswordHashFactor();
}
- return auth.authToken.derivePasswordHashFactor();
+ } finally {
+ scheduleGc();
}
}
@@ -3284,6 +3300,22 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
+ /**
+ * Schedules garbage collection to sanitize lockscreen credential remnants in memory.
+ *
+ * One source of leftover lockscreen credentials is the unmarshalled binder method arguments.
+ * Since this method will be called within the binder implementation method, a small delay is
+ * added before the GC operation to allow the enclosing binder proxy code to complete and
+ * release references to the argument.
+ */
+ private void scheduleGc() {
+ mHandler.postDelayed(() -> {
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ }, 2000);
+ }
+
private class DeviceProvisionedObserver extends ContentObserver {
private final Uri mDeviceProvisionedUri = Settings.Global.getUriFor(
Settings.Global.DEVICE_PROVISIONED);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 7b767b86f0d4..c4581c88f49e 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -155,7 +155,7 @@ class LockSettingsShellCommand extends ShellCommand {
pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>");
pw.println(" Sets the lock screen as PIN, using the given PIN to unlock.");
pw.println("");
- pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
+ pw.println(" set-password [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
pw.println(" Sets the lock screen as password, using the given PASSOWRD to unlock.");
pw.println("");
pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID]");
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index 55e0795f8b50..b9822fcb096a 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -26,12 +26,12 @@ import android.os.Message;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.IntArray;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -104,7 +104,7 @@ class AudioPlayerStateMonitor {
// TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
@GuardedBy("mLock")
@SuppressWarnings("WeakerAccess") /* synthetic access */
- final IntArray mSortedAudioPlaybackClientUids = new IntArray();
+ final List<Integer> mSortedAudioPlaybackClientUids = new ArrayList<>();
static AudioPlayerStateMonitor getInstance(Context context) {
synchronized (AudioPlayerStateMonitor.class) {
@@ -145,8 +145,8 @@ class AudioPlayerStateMonitor {
* audio/video) The UID whose audio is currently playing comes first, then the UID whose audio
* playback becomes active at the last comes next.
*/
- public IntArray getSortedAudioPlaybackClientUids() {
- IntArray sortedAudioPlaybackClientUids = new IntArray();
+ public List<Integer> getSortedAudioPlaybackClientUids() {
+ List<Integer> sortedAudioPlaybackClientUids = new ArrayList();
synchronized (mLock) {
sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids);
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 25bbfa02fa05..3a4dfaf9bfcd 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -45,6 +45,7 @@ import com.android.internal.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -318,16 +319,6 @@ class BluetoothRouteProvider {
btRoute.route = builder.build();
}
- private void clearActiveRoutes() {
- if (DEBUG) {
- Log.d(TAG, "Clearing active routes");
- }
- for (BluetoothRouteInfo btRoute : mActiveRoutes) {
- setRouteConnectionState(btRoute, STATE_DISCONNECTED);
- }
- mActiveRoutes.clear();
- }
-
private void addActiveRoute(BluetoothRouteInfo btRoute) {
if (DEBUG) {
Log.d(TAG, "Adding active route: " + btRoute.route);
@@ -348,18 +339,34 @@ class BluetoothRouteProvider {
}
}
- private void findAndSetActiveHearingAidDevices() {
+ private void clearActiveRoutesWithType(int type) {
if (DEBUG) {
- Log.d(TAG, "Setting active hearing aid devices");
+ Log.d(TAG, "Clearing active routes with type. type=" + type);
+ }
+ Iterator<BluetoothRouteInfo> iter = mActiveRoutes.iterator();
+ while (iter.hasNext()) {
+ BluetoothRouteInfo btRoute = iter.next();
+ if (btRoute.route.getType() == type) {
+ iter.remove();
+ setRouteConnectionState(btRoute, STATE_DISCONNECTED);
+ }
}
+ }
- BluetoothHearingAid hearingAidProfile = mHearingAidProfile;
- if (hearingAidProfile == null) {
- return;
+ private void addActiveHearingAidDevices(BluetoothDevice device) {
+ if (DEBUG) {
+ Log.d(TAG, "Setting active hearing aid devices. device=" + device);
}
- List<BluetoothDevice> activeDevices = hearingAidProfile.getActiveDevices();
+
+ // Let the given device be the first active device
+ BluetoothRouteInfo activeBtRoute = mBluetoothRoutes.get(device.getAddress());
+ addActiveRoute(activeBtRoute);
+
+ // A bluetooth route with the same route ID should be added.
for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
- if (activeDevices.contains(btRoute.btDevice)) {
+ if (TextUtils.equals(btRoute.route.getId(), activeBtRoute.route.getId())
+ && !TextUtils.equals(btRoute.btDevice.getAddress(),
+ activeBtRoute.btDevice.getAddress())) {
addActiveRoute(btRoute);
}
}
@@ -465,16 +472,16 @@ class BluetoothRouteProvider {
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
switch (intent.getAction()) {
case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
- clearActiveRoutes();
+ clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
if (device != null) {
addActiveRoute(mBluetoothRoutes.get(device.getAddress()));
}
notifyBluetoothRoutesUpdated();
break;
case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
- clearActiveDevices();
+ clearActiveRoutesWithType(MediaRoute2Info.TYPE_HEARING_AID);
if (device != null) {
- findAndSetActiveHearingAidDevices();
+ addActiveHearingAidDevices(device);
}
notifyBluetoothRoutesUpdated();
break;
diff --git a/services/core/java/com/android/server/media/MediaResourceMonitorService.java b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
index 8ed32f09e23a..6669b8cec143 100644
--- a/services/core/java/com/android/server/media/MediaResourceMonitorService.java
+++ b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
@@ -26,9 +26,8 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.util.Slog;
-import com.android.server.SystemService;
-import java.util.List;
+import com.android.server.SystemService;
/** This class provides a system service that monitors media resource usage. */
public class MediaResourceMonitorService extends SystemService {
@@ -62,8 +61,7 @@ public class MediaResourceMonitorService extends SystemService {
if (pkgNames == null) {
return;
}
- UserManager manager = (UserManager) getContext().getSystemService(
- Context.USER_SERVICE);
+ UserManager manager = getContext().getSystemService(UserManager.class);
int[] userIds = manager.getEnabledProfileIds(ActivityManager.getCurrentUser());
if (userIds == null || userIds.length == 0) {
return;
@@ -81,15 +79,11 @@ public class MediaResourceMonitorService extends SystemService {
}
private String[] getPackageNamesFromPid(int pid) {
- try {
- for (ActivityManager.RunningAppProcessInfo proc :
- ActivityManager.getService().getRunningAppProcesses()) {
- if (proc.pid == pid) {
- return proc.pkgList;
- }
+ ActivityManager manager = getContext().getSystemService(ActivityManager.class);
+ for (ActivityManager.RunningAppProcessInfo proc : manager.getRunningAppProcesses()) {
+ if (proc.pid == pid) {
+ return proc.pkgList;
}
- } catch (RemoteException e) {
- Slog.w(TAG, "ActivityManager.getRunningAppProcesses() failed");
}
return null;
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index cc9503995ad9..875bfdffafcd 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1555,7 +1555,6 @@ class MediaRouter2ServiceImpl {
long managerRequestId = (matchingRequest == null)
? MediaRoute2ProviderService.REQUEST_ID_NONE
: matchingRequest.mManagerRequestId;
- // Managers should know created session even if it's not requested.
notifySessionCreatedToManagers(managerRequestId, sessionInfo);
if (matchingRequest == null) {
@@ -1575,18 +1574,6 @@ class MediaRouter2ServiceImpl {
+ "session=" + matchingRequest.mOldSession);
}
- String originalRouteId = matchingRequest.mRoute.getId();
- RouterRecord routerRecord = matchingRequest.mRouterRecord;
-
- if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)) {
- Slog.w(TAG, "Created session doesn't match the original request."
- + " originalRouteId=" + originalRouteId
- + ", uniqueRequestId=" + uniqueRequestId + ", sessionInfo=" + sessionInfo);
- notifySessionCreationFailedToRouter(matchingRequest.mRouterRecord,
- toOriginalRequestId(uniqueRequestId));
- return;
- }
-
// Succeeded
if (sessionInfo.isSystemSession()
&& !matchingRequest.mRouterRecord.mHasModifyAudioRoutingPermission) {
@@ -1597,7 +1584,7 @@ class MediaRouter2ServiceImpl {
notifySessionCreatedToRouter(matchingRequest.mRouterRecord,
toOriginalRequestId(uniqueRequestId), sessionInfo);
}
- mSessionToRouterMap.put(sessionInfo.getId(), routerRecord);
+ mSessionToRouterMap.put(sessionInfo.getId(), matchingRequest.mRouterRecord);
}
private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index dbf1abc0ca70..0f6748366e16 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -50,7 +50,6 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.util.Log;
-import android.util.Slog;
import android.view.KeyEvent;
import com.android.server.LocalServices;
@@ -996,7 +995,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
return true;
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
+ Log.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
@@ -1013,7 +1012,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
return true;
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
+ Log.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
@@ -1023,7 +1022,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onCommand(packageName, pid, uid, command, args, cb);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in sendCommand.", e);
+ Log.e(TAG, "Remote failure in sendCommand.", e);
}
}
@@ -1032,7 +1031,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onCustomAction(packageName, pid, uid, action, args);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in sendCustomAction.", e);
+ Log.e(TAG, "Remote failure in sendCustomAction.", e);
}
}
@@ -1040,7 +1039,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPrepare(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in prepare.", e);
+ Log.e(TAG, "Remote failure in prepare.", e);
}
}
@@ -1049,7 +1048,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in prepareFromMediaId.", e);
+ Log.e(TAG, "Remote failure in prepareFromMediaId.", e);
}
}
@@ -1058,7 +1057,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPrepareFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in prepareFromSearch.", e);
+ Log.e(TAG, "Remote failure in prepareFromSearch.", e);
}
}
@@ -1066,7 +1065,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPrepareFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in prepareFromUri.", e);
+ Log.e(TAG, "Remote failure in prepareFromUri.", e);
}
}
@@ -1074,7 +1073,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPlay(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in play.", e);
+ Log.e(TAG, "Remote failure in play.", e);
}
}
@@ -1083,7 +1082,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in playFromMediaId.", e);
+ Log.e(TAG, "Remote failure in playFromMediaId.", e);
}
}
@@ -1092,7 +1091,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPlayFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in playFromSearch.", e);
+ Log.e(TAG, "Remote failure in playFromSearch.", e);
}
}
@@ -1100,7 +1099,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPlayFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in playFromUri.", e);
+ Log.e(TAG, "Remote failure in playFromUri.", e);
}
}
@@ -1108,7 +1107,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onSkipToTrack(packageName, pid, uid, id);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in skipToTrack", e);
+ Log.e(TAG, "Remote failure in skipToTrack", e);
}
}
@@ -1116,7 +1115,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPause(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in pause.", e);
+ Log.e(TAG, "Remote failure in pause.", e);
}
}
@@ -1124,7 +1123,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onStop(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in stop.", e);
+ Log.e(TAG, "Remote failure in stop.", e);
}
}
@@ -1132,7 +1131,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onNext(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in next.", e);
+ Log.e(TAG, "Remote failure in next.", e);
}
}
@@ -1140,7 +1139,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onPrevious(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in previous.", e);
+ Log.e(TAG, "Remote failure in previous.", e);
}
}
@@ -1148,7 +1147,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onFastForward(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in fastForward.", e);
+ Log.e(TAG, "Remote failure in fastForward.", e);
}
}
@@ -1156,7 +1155,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onRewind(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in rewind.", e);
+ Log.e(TAG, "Remote failure in rewind.", e);
}
}
@@ -1164,7 +1163,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onSeekTo(packageName, pid, uid, pos);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in seekTo.", e);
+ Log.e(TAG, "Remote failure in seekTo.", e);
}
}
@@ -1172,7 +1171,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onRate(packageName, pid, uid, rating);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in rate.", e);
+ Log.e(TAG, "Remote failure in rate.", e);
}
}
@@ -1180,7 +1179,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onSetPlaybackSpeed(packageName, pid, uid, speed);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e);
+ Log.e(TAG, "Remote failure in setPlaybackSpeed.", e);
}
}
@@ -1194,7 +1193,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
mCb.onAdjustVolume(packageName, pid, uid, direction);
}
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in adjustVolume.", e);
+ Log.e(TAG, "Remote failure in adjustVolume.", e);
}
}
@@ -1202,7 +1201,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
try {
mCb.onSetVolumeTo(packageName, pid, uid, value);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in setVolumeTo.", e);
+ Log.e(TAG, "Remote failure in setVolumeTo.", e);
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a162eb87ca71..9f6c18d5ec72 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,7 +16,8 @@
package com.android.server.media;
-import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.ALL;
+import static android.os.UserHandle.CURRENT;
import static com.android.server.media.MediaKeyDispatcher.KEY_EVENT_LONG_PRESS;
import static com.android.server.media.MediaKeyDispatcher.isDoubleTapOverridden;
@@ -25,8 +26,8 @@ import static com.android.server.media.MediaKeyDispatcher.isSingleTapOverridden;
import static com.android.server.media.MediaKeyDispatcher.isTripleTapOverridden;
import android.app.ActivityManager;
-import android.app.INotificationManager;
import android.app.KeyguardManager;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -59,6 +60,7 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
@@ -66,7 +68,6 @@ import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManager;
@@ -74,7 +75,6 @@ import android.provider.Settings;
import android.speech.RecognizerIntent;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.KeyEvent;
@@ -117,8 +117,9 @@ public class MediaSessionService extends SystemService implements Monitor {
private final SessionManagerImpl mSessionManagerImpl;
private final MessageHandler mHandler = new MessageHandler();
private final PowerManager.WakeLock mMediaEventWakeLock;
- private final INotificationManager mNotificationManager;
+ private final NotificationManager mNotificationManager;
private final Object mLock = new Object();
+ private final HandlerThread mRecordThread = new HandlerThread("SessionRecordThread");
// Keeps the full user id for each user.
@GuardedBy("mLock")
private final SparseIntArray mFullUserIds = new SparseIntArray();
@@ -156,10 +157,9 @@ public class MediaSessionService extends SystemService implements Monitor {
super(context);
mContext = context;
mSessionManagerImpl = new SessionManagerImpl();
- PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ PowerManager pm = mContext.getSystemService(PowerManager.class);
mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
- mNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
}
@Override
@@ -198,6 +198,7 @@ public class MediaSessionService extends SystemService implements Monitor {
instantiateCustomProvider(null);
instantiateCustomDispatcher(null);
+ mRecordThread.start();
}
private boolean isGlobalPriorityActiveLocked() {
@@ -249,7 +250,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
List<MediaSessionRecord> records = new ArrayList<>();
- if (userId == USER_ALL) {
+ if (userId == ALL.getIdentifier()) {
int size = mUserRecords.size();
for (int i = 0; i < size; i++) {
records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
@@ -265,7 +266,8 @@ public class MediaSessionService extends SystemService implements Monitor {
// Return global priority session at the first whenever it's asked.
if (isGlobalPriorityActiveLocked()
- && (userId == USER_ALL || userId == mGlobalPrioritySession.getUserId())) {
+ && (userId == ALL.getIdentifier()
+ || userId == mGlobalPrioritySession.getUserId())) {
records.add(0, mGlobalPrioritySession);
}
return records;
@@ -273,7 +275,7 @@ public class MediaSessionService extends SystemService implements Monitor {
List<Session2Token> getSession2TokensLocked(int userId) {
List<Session2Token> list = new ArrayList<>();
- if (userId == USER_ALL) {
+ if (userId == ALL.getIdentifier()) {
int size = mUserRecords.size();
for (int i = 0; i < size; i++) {
list.addAll(mUserRecords.valueAt(i).mPriorityStack.getSession2Tokens(userId));
@@ -349,7 +351,7 @@ public class MediaSessionService extends SystemService implements Monitor {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user != null) {
if (user.mFullUserId == userId) {
- user.destroySessionsForUserLocked(USER_ALL);
+ user.destroySessionsForUserLocked(ALL.getIdentifier());
mUserRecords.remove(userId);
} else {
user.destroySessionsForUserLocked(userId);
@@ -503,11 +505,12 @@ public class MediaSessionService extends SystemService implements Monitor {
private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
int resolvedUserId) {
if (hasStatusBarServicePermission(pid, uid)) return;
+ // TODO: Refactor to use hasMediaControlPermission and hasEnabledNotificationListener
if (mContext
.checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
!= PackageManager.PERMISSION_GRANTED
&& !isEnabledNotificationListener(compName,
- UserHandle.getUserHandleForUid(uid).getIdentifier(), resolvedUserId)) {
+ UserHandle.getUserHandleForUid(uid), resolvedUserId)) {
throw new SecurityException("Missing permission to control media.");
}
}
@@ -543,13 +546,13 @@ public class MediaSessionService extends SystemService implements Monitor {
* they're running as.
*
* @param compName The component that is enabled.
- * @param userId The user id of the caller.
+ * @param userHandle The user handle of the caller.
* @param forUserId The user id they're making the request on behalf of.
* @return True if the component is enabled, false otherwise
*/
- private boolean isEnabledNotificationListener(ComponentName compName, int userId,
+ private boolean isEnabledNotificationListener(ComponentName compName, UserHandle userHandle,
int forUserId) {
- if (userId != forUserId) {
+ if (userHandle.getIdentifier() != forUserId) {
// You may not access another user's content as an enabled listener.
return false;
}
@@ -557,12 +560,8 @@ public class MediaSessionService extends SystemService implements Monitor {
Log.d(TAG, "Checking if enabled notification listener " + compName);
}
if (compName != null) {
- try {
- return mNotificationManager.isNotificationListenerAccessGrantedForUser(
- compName, userId);
- } catch (RemoteException e) {
- Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
- }
+ return mNotificationManager.hasEnabledNotificationListener(compName.getPackageName(),
+ userHandle);
}
return false;
}
@@ -599,8 +598,8 @@ public class MediaSessionService extends SystemService implements Monitor {
final MediaSessionRecord session;
try {
session = new MediaSessionRecord(callerPid, callerUid, userId,
- callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper(),
- policies);
+ callerPackageName, cb, tag, sessionInfo, this,
+ mRecordThread.getLooper(), policies);
} catch (RemoteException e) {
throw new RuntimeException("Media Session owner died prematurely.", e);
}
@@ -651,7 +650,7 @@ public class MediaSessionService extends SystemService implements Monitor {
pushRemoteVolumeUpdateLocked(userId);
for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
SessionsListenerRecord record = mSessionsListeners.get(i);
- if (record.userId == USER_ALL || record.userId == userId) {
+ if (record.userId == ALL.getIdentifier() || record.userId == userId) {
try {
record.listener.onActiveSessionsChanged(tokens);
} catch (RemoteException e) {
@@ -666,13 +665,13 @@ public class MediaSessionService extends SystemService implements Monitor {
void pushSession2Changed(int userId) {
synchronized (mLock) {
- List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL);
+ List<Session2Token> allSession2Tokens = getSession2TokensLocked(ALL.getIdentifier());
List<Session2Token> session2Tokens = getSession2TokensLocked(userId);
for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i);
try {
- if (listenerRecord.userId == USER_ALL) {
+ if (listenerRecord.userId == ALL.getIdentifier()) {
listenerRecord.listener.onSession2TokensChanged(allSession2Tokens);
} else if (listenerRecord.userId == userId) {
listenerRecord.listener.onSession2TokensChanged(session2Tokens);
@@ -758,7 +757,8 @@ public class MediaSessionService extends SystemService implements Monitor {
}
private MediaSessionRecord getMediaSessionRecordLocked(MediaSession.Token sessionToken) {
- FullUserRecord user = getFullUserRecordLocked(UserHandle.getUserId(sessionToken.getUid()));
+ FullUserRecord user = getFullUserRecordLocked(
+ UserHandle.getUserHandleForUid(sessionToken.getUid()).getIdentifier());
if (user != null) {
return user.mPriorityStack.getMediaSessionRecord(sessionToken);
}
@@ -1087,7 +1087,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private void observe() {
mContentResolver.registerContentObserver(mSecureSettingsUri,
- false, this, USER_ALL);
+ false, this, ALL.getIdentifier());
}
@Override
@@ -1137,7 +1137,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
return sessionBinder;
} catch (Exception e) {
- Slog.w(TAG, "Exception in creating a new session", e);
+ Log.w(TAG, "Exception in creating a new session", e);
throw e;
} finally {
Binder.restoreCallingIdentity(token);
@@ -1157,8 +1157,8 @@ public class MediaSessionService extends SystemService implements Monitor {
throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
+ " but actually=" + sessionToken.getUid());
}
- MediaSession2Record record = new MediaSession2Record(
- sessionToken, MediaSessionService.this, mHandler.getLooper(), 0);
+ MediaSession2Record record = new MediaSession2Record(sessionToken,
+ MediaSessionService.this, mRecordThread.getLooper(), 0);
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
user.mPriorityStack.addSession(record);
@@ -1348,7 +1348,7 @@ public class MediaSessionService extends SystemService implements Monitor {
if (!isUserSetupComplete()) {
// Global media key handling can have the side-effect of starting new
// activities which is undesirable while setup is in progress.
- Slog.i(TAG, "Not dispatching media key event because user "
+ Log.i(TAG, "Not dispatching media key event because user "
+ "setup is in progress.");
return;
}
@@ -1358,7 +1358,7 @@ public class MediaSessionService extends SystemService implements Monitor {
if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
// Prevent dispatching key event through reflection while the global
// priority session is active.
- Slog.i(TAG, "Only the system can dispatch media key event "
+ Log.i(TAG, "Only the system can dispatch media key event "
+ "to the global priority session.");
return;
}
@@ -1433,7 +1433,7 @@ public class MediaSessionService extends SystemService implements Monitor {
final IOnMediaKeyEventDispatchedListener listener) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- final int userId = UserHandle.getUserId(uid);
+ final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
if (!hasMediaControlPermission(pid, uid)) {
@@ -1461,7 +1461,7 @@ public class MediaSessionService extends SystemService implements Monitor {
final IOnMediaKeyEventDispatchedListener listener) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- final int userId = UserHandle.getUserId(uid);
+ final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
if (!hasMediaControlPermission(pid, uid)) {
@@ -1489,7 +1489,7 @@ public class MediaSessionService extends SystemService implements Monitor {
final IOnMediaKeyEventSessionChangedListener listener) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- final int userId = UserHandle.getUserId(uid);
+ final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
if (!hasMediaControlPermission(pid, uid)) {
@@ -1517,7 +1517,7 @@ public class MediaSessionService extends SystemService implements Monitor {
final IOnMediaKeyEventSessionChangedListener listener) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- final int userId = UserHandle.getUserId(uid);
+ final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
if (!hasMediaControlPermission(pid, uid)) {
@@ -1555,7 +1555,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
synchronized (mLock) {
- int userId = UserHandle.getUserId(uid);
+ int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null || user.mFullUserId != userId) {
Log.w(TAG, "Only the full user can set the volume key long-press listener"
@@ -1614,7 +1614,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
synchronized (mLock) {
- int userId = UserHandle.getUserId(uid);
+ int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null || user.mFullUserId != userId) {
Log.w(TAG, "Only the full user can set the media key listener"
@@ -1740,10 +1740,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
if (down || up) {
int flags = AudioManager.FLAG_FROM_KEY;
- if (musicOnly) {
- // This flag is used when the screen is off to only affect active media.
- flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
- } else {
+ if (!musicOnly) {
// These flags are consistent with the home screen
if (up) {
flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
@@ -1757,11 +1754,12 @@ public class MediaSessionService extends SystemService implements Monitor {
direction = 0;
}
dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
- asSystemService, stream, direction, flags);
+ asSystemService, stream, direction, flags, musicOnly);
} else if (isMute) {
if (down && keyEvent.getRepeatCount() == 0) {
dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
- asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
+ asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags,
+ musicOnly);
}
}
}
@@ -1844,7 +1842,7 @@ public class MediaSessionService extends SystemService implements Monitor {
try {
synchronized (mLock) {
dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false,
- suggestedStream, delta, flags);
+ suggestedStream, delta, flags, false);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1919,10 +1917,10 @@ public class MediaSessionService extends SystemService implements Monitor {
* @param controllerUid uid of the controller app
*/
@Override
- public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
- throws RemoteException {
+ public boolean isTrusted(String controllerPackageName, int controllerPid,
+ int controllerUid) {
final int uid = Binder.getCallingUid();
- final int userId = UserHandle.getUserId(uid);
+ final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
// Don't perform sanity check between controllerPackageName and controllerUid.
@@ -1934,7 +1932,7 @@ public class MediaSessionService extends SystemService implements Monitor {
// Context#getPackageName() for getting package name that matches with the PID/UID,
// but it doesn't tell which package has created the MediaController, so useless.
return hasMediaControlPermission(controllerPid, controllerUid)
- || hasEnabledNotificationListener(userId, controllerPackageName);
+ || hasEnabledNotificationListener(userId, controllerPackageName, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1998,35 +1996,28 @@ public class MediaSessionService extends SystemService implements Monitor {
return resolvedUserId;
}
- private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName)
- throws RemoteException {
+ private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName,
+ int uid) {
+ // TODO: revisit this checking code
// You may not access another user's content as an enabled listener.
- final int userId = UserHandle.getUserId(resolvedUserId);
+ final int userId = UserHandle.getUserHandleForUid(resolvedUserId).getIdentifier();
if (resolvedUserId != userId) {
return false;
}
-
- // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
- // String pkgName) to notification team for optimization
- final List<ComponentName> enabledNotificationListeners =
- mNotificationManager.getEnabledNotificationListeners(userId);
- if (enabledNotificationListeners != null) {
- for (int i = 0; i < enabledNotificationListeners.size(); i++) {
- if (TextUtils.equals(packageName,
- enabledNotificationListeners.get(i).getPackageName())) {
- return true;
- }
- }
+ if (mNotificationManager.hasEnabledNotificationListener(packageName,
+ UserHandle.getUserHandleForUid(uid))) {
+ return true;
}
if (DEBUG) {
- Log.d(TAG, packageName + " (uid=" + resolvedUserId + ") doesn't have an enabled "
+ Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
+ "notification listener");
}
return false;
}
private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
- int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
+ int uid, boolean asSystemService, int suggestedStream, int direction, int flags,
+ boolean musicOnly) {
MediaSessionRecordImpl session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
: mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
@@ -2041,8 +2032,7 @@ public class MediaSessionService extends SystemService implements Monitor {
+ ". flags=" + flags + ", preferSuggestedStream="
+ preferSuggestedStream + ", session=" + session);
}
- if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
- && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
+ if (musicOnly && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Nothing is playing on the music stream. Skipping volume event,"
+ " flags=" + flags);
@@ -2096,7 +2086,7 @@ public class MediaSessionService extends SystemService implements Monitor {
boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
if (mCurrentFullUserRecord.getMediaButtonSessionLocked()
instanceof MediaSession2Record) {
- // TODO(jaewan): Implement
+ // TODO(jaewan): Make MediaSession2 to receive media key event
return;
}
MediaSessionRecord session = null;
@@ -2218,7 +2208,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private boolean isUserSetupComplete() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ Settings.Secure.USER_SETUP_COMPLETE, 0, CURRENT.getIdentifier()) != 0;
}
// we only handle public stream types, which are 0-5
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 402355a0161a..953aae44d6a7 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -22,7 +22,6 @@ import android.media.Session2Token;
import android.media.session.MediaSession;
import android.os.Debug;
import android.os.UserHandle;
-import android.util.IntArray;
import android.util.Log;
import android.util.SparseArray;
@@ -190,7 +189,8 @@ class MediaSessionStack {
if (DEBUG) {
Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2));
}
- IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
+ List<Integer> audioPlaybackUids =
+ mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
for (int i = 0; i < audioPlaybackUids.size(); i++) {
int audioPlaybackUid = audioPlaybackUids.get(i);
MediaSessionRecordImpl mediaButtonSession = findMediaButtonSession(audioPlaybackUid);
@@ -244,6 +244,10 @@ class MediaSessionStack {
private MediaSessionRecordImpl findMediaButtonSession(int uid) {
MediaSessionRecordImpl mediaButtonSession = null;
for (MediaSessionRecordImpl session : mSessions) {
+ if (session instanceof MediaSession2Record) {
+ // TODO(jaewan): Make MediaSession2 to receive media key event
+ continue;
+ }
if (uid == session.getUid()) {
if (session.checkPlaybackActiveState(
mAudioPlayerStateMonitor.isPlaybackActive(session.getUid()))) {
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index b460cb5b23ea..2e2d812c058e 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -2,6 +2,7 @@ elaurent@google.com
hdmoon@google.com
insun@google.com
jaewan@google.com
+jinpark@google.com
klhyun@google.com
lajos@google.com
sungsoo@google.com
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index e3e02e32ad50..f0bf5c0975f2 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -24,6 +24,7 @@ import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
+import android.net.Uri;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
@@ -372,7 +373,7 @@ public class IpConfigStore {
config.httpProxy = proxyInfo;
break;
case PAC:
- ProxyInfo proxyPacProperties = new ProxyInfo(pacFileUrl);
+ ProxyInfo proxyPacProperties = new ProxyInfo(Uri.parse(pacFileUrl));
config.proxySettings = proxySettings;
config.httpProxy = proxyPacProperties;
break;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 87f0fb14ee33..d6557f6410ec 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3910,11 +3910,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private void updateRulesForAppIdleParoleUL() {
final boolean paroled = mAppStandby.isInParole();
final boolean enableChain = !paroled;
- enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain);
int ruleCount = mUidFirewallStandbyRules.size();
+ final SparseIntArray blockedUids = new SparseIntArray();
for (int i = 0; i < ruleCount; i++) {
final int uid = mUidFirewallStandbyRules.keyAt(i);
+ if (!isUidValidForBlacklistRulesUL(uid)) {
+ continue;
+ }
int oldRules = mUidRules.get(uid);
if (enableChain) {
// Chain wasn't enabled before and the other power-related
@@ -3926,13 +3929,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// Skip if it had no restrictions to begin with
if ((oldRules & MASK_ALL_NETWORKS) == 0) continue;
}
- final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules, paroled);
+ final boolean isUidIdle = !paroled && isUidIdle(uid);
+ if (isUidIdle && !mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid))
+ && !isUidForegroundOnRestrictPowerUL(uid)) {
+ mUidFirewallStandbyRules.put(uid, FIREWALL_RULE_DENY);
+ blockedUids.put(uid, FIREWALL_RULE_DENY);
+ } else {
+ mUidFirewallStandbyRules.put(uid, FIREWALL_RULE_DEFAULT);
+ }
+ final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules,
+ isUidIdle);
if (newUidRules == RULE_NONE) {
mUidRules.delete(uid);
} else {
mUidRules.put(uid, newUidRules);
}
}
+ setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, blockedUids,
+ enableChain ? CHAIN_TOGGLE_ENABLE : CHAIN_TOGGLE_DISABLE);
}
/**
@@ -4400,7 +4414,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private void updateRulesForPowerRestrictionsUL(int uid) {
final int oldUidRules = mUidRules.get(uid, RULE_NONE);
- final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false);
+ final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules,
+ isUidIdle(uid));
if (newUidRules == RULE_NONE) {
mUidRules.delete(uid);
@@ -4414,33 +4429,33 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
*
* @param uid the uid of the app to update rules for
* @param oldUidRules the current rules for the uid, in order to determine if there's a change
- * @param paroled whether to ignore idle state of apps and only look at other restrictions
+ * @param isUidIdle whether uid is idle or not
*
* @return the new computed rules for the uid
*/
@GuardedBy("mUidRulesFirstLock")
- private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) {
+ private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean isUidIdle) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
"updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/"
- + (paroled ? "P" : "-"));
+ + (isUidIdle ? "I" : "-"));
}
try {
- return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, paroled);
+ return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, isUidIdle);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
@GuardedBy("mUidRulesFirstLock")
- private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) {
+ private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules,
+ boolean isUidIdle) {
if (!isUidValidForBlacklistRulesUL(uid)) {
if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);
return RULE_NONE;
}
- final boolean isIdle = !paroled && isUidIdle(uid);
- final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
+ final boolean restrictMode = isUidIdle || mRestrictPower || mDeviceIdleMode;
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
@@ -4463,7 +4478,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (LOGV) {
Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
- + ", isIdle: " + isIdle
+ + ", isIdle: " + isUidIdle
+ ", mRestrictPower: " + mRestrictPower
+ ", mDeviceIdleMode: " + mDeviceIdleMode
+ ", isForeground=" + isForeground
@@ -5273,6 +5288,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@Override
public void onTempPowerSaveWhitelistChange(int appId, boolean added) {
synchronized (mUidRulesFirstLock) {
+ if (!mSystemReady) {
+ return;
+ }
mLogger.tempPowerSaveWlChanged(appId, added);
if (added) {
mPowerSaveTempWhitelistAppIds.put(appId, true);
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index db553ee79555..0575ac6315a1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -16,12 +16,14 @@
package com.android.server.net;
+import static android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA;
import static android.net.NetworkTemplate.getCollapsedRatType;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Looper;
import android.telephony.Annotation;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
@@ -196,7 +198,18 @@ public class NetworkStatsSubscriptionsMonitor extends
@Override
public void onServiceStateChanged(@NonNull ServiceState ss) {
- final int networkType = ss.getDataNetworkType();
+ // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony
+ // would report RAT = 5G_NR.
+ // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and
+ // network allocates a secondary 5G cell so telephony reports RAT = LTE along with
+ // NR state as connected. In such case, attributes the data usage to NR.
+ // See b/160727498.
+ final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE
+ || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA)
+ && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+
+ final int networkType =
+ (is5GNsa ? NETWORK_TYPE_5G_NSA : ss.getDataNetworkType());
final int collapsedRatType = getCollapsedRatType(networkType);
if (collapsedRatType == mLastCollapsedRatType) return;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e2be1932a08d..37cc6b2453fe 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -515,7 +515,7 @@ public class NotificationManagerService extends SystemService {
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
- private static final IBinder WHITELIST_TOKEN = new Binder();
+ private static final IBinder ALLOWLIST_TOKEN = new Binder();
protected RankingHandler mRankingHandler;
private long mLastOverRateLogTime;
private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
@@ -1722,7 +1722,7 @@ public class NotificationManagerService extends SystemService {
super(context);
mNotificationRecordLogger = notificationRecordLogger;
mNotificationInstanceIdSequence = notificationInstanceIdSequence;
- Notification.processWhitelistToken = WHITELIST_TOKEN;
+ Notification.processAllowlistToken = ALLOWLIST_TOKEN;
}
// TODO - replace these methods with new fields in the VisibleForTesting constructor
@@ -4674,10 +4674,11 @@ public class NotificationManagerService extends SystemService {
@Override
public void setNotificationPolicy(String pkg, Policy policy) {
enforcePolicyAccess(pkg, "setNotificationPolicy");
+ int callingUid = Binder.getCallingUid();
final long identity = Binder.clearCallingIdentity();
try {
final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkg,
- 0, UserHandle.getUserId(MY_UID));
+ 0, UserHandle.getUserId(callingUid));
Policy currPolicy = mZenModeHelper.getNotificationPolicy();
if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.P) {
@@ -4751,6 +4752,12 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public boolean hasEnabledNotificationListener(String packageName, int userId) {
+ checkCallerIsSystem();
+ return mListeners.isPackageAllowed(packageName, userId);
+ }
+
+ @Override
public boolean isNotificationListenerAccessGranted(ComponentName listener) {
Objects.requireNonNull(listener);
checkCallerIsSystemOrSameApp(listener.getPackageName());
@@ -5774,21 +5781,21 @@ public class NotificationManagerService extends SystemService {
mShortcutHelper.cacheShortcut(info, user);
}
- // Whitelist pending intents.
+ // temporarily allow apps to perform extra work when their pending intents are launched
if (notification.allPendingIntents != null) {
final int intentCount = notification.allPendingIntents.size();
if (intentCount > 0) {
final ActivityManagerInternal am = LocalServices
.getService(ActivityManagerInternal.class);
final long duration = LocalServices.getService(
- DeviceIdleInternal.class).getNotificationWhitelistDuration();
+ DeviceIdleInternal.class).getNotificationAllowlistDuration();
for (int i = 0; i < intentCount; i++) {
PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
if (pendingIntent != null) {
am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
- WHITELIST_TOKEN, duration);
+ ALLOWLIST_TOKEN, duration);
am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
- WHITELIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
+ ALLOWLIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
| FLAG_SERVICE_SENDER));
}
}
@@ -7641,7 +7648,7 @@ public class NotificationManagerService extends SystemService {
// make sure deleteIntent cannot be used to start activities from background
LocalServices.getService(ActivityManagerInternal.class)
.clearPendingIntentAllowBgActivityStarts(deleteIntent.getTarget(),
- WHITELIST_TOKEN);
+ ALLOWLIST_TOKEN);
deleteIntent.send();
} catch (PendingIntent.CanceledException ex) {
// do nothing - there's no relevant way to recover, and
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index dd6a83bc3f60..9cf9545d889a 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -75,6 +75,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -172,6 +173,8 @@ public class PreferencesHelper implements RankingConfig {
private boolean mAllowInvalidShortcuts = false;
+ private Map<String, List<String>> mOemLockedApps = new HashMap();
+
public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger,
AppOpsManager appOpsManager,
@@ -314,6 +317,12 @@ public class PreferencesHelper implements RankingConfig {
}
channel.setImportanceLockedByCriticalDeviceFunction(
r.defaultAppLockedImportance);
+ channel.setImportanceLockedByOEM(r.oemLockedImportance);
+ if (!channel.isImportanceLockedByOEM()) {
+ if (r.oemLockedChannels.contains(channel.getId())) {
+ channel.setImportanceLockedByOEM(true);
+ }
+ }
boolean isInvalidShortcutChannel =
channel.getConversationId() != null &&
channel.getConversationId().contains(
@@ -396,6 +405,14 @@ public class PreferencesHelper implements RankingConfig {
r.visibility = visibility;
r.showBadge = showBadge;
r.bubblePreference = bubblePreference;
+ if (mOemLockedApps.containsKey(r.pkg)) {
+ List<String> channels = mOemLockedApps.get(r.pkg);
+ if (channels == null || channels.isEmpty()) {
+ r.oemLockedImportance = true;
+ } else {
+ r.oemLockedChannels = channels;
+ }
+ }
try {
createDefaultChannelIfNeededLocked(r);
@@ -1149,8 +1166,10 @@ public class PreferencesHelper implements RankingConfig {
String channelId = appSplit.length == 2 ? appSplit[1] : null;
synchronized (mPackagePreferences) {
+ boolean foundApp = false;
for (PackagePreferences r : mPackagePreferences.values()) {
if (r.pkg.equals(appName)) {
+ foundApp = true;
if (channelId == null) {
// lock all channels for the app
r.oemLockedImportance = true;
@@ -1168,6 +1187,14 @@ public class PreferencesHelper implements RankingConfig {
}
}
}
+ if (!foundApp) {
+ List<String> channels =
+ mOemLockedApps.getOrDefault(appName, new ArrayList<>());
+ if (channelId != null) {
+ channels.add(channelId);
+ }
+ mOemLockedApps.put(appName, channels);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index ee02e3fa8140..9c3d6d352c89 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -37,7 +37,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Helper for querying shortcuts.
@@ -100,9 +102,13 @@ public class ShortcutHelper {
HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName);
ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
if (shortcutBubbles != null) {
+ // Copy to avoid a concurrent modification exception when we remove bubbles from
+ // shortcutBubbles.
+ final Set<String> shortcutIds = new HashSet<>(shortcutBubbles.keySet());
+
// If we can't find one of our bubbles in the shortcut list, that bubble needs
// to be removed.
- for (String shortcutId : shortcutBubbles.keySet()) {
+ for (String shortcutId : shortcutIds) {
boolean foundShortcut = false;
for (int i = 0; i < shortcuts.size(); i++) {
if (shortcuts.get(i).getId().equals(shortcutId)) {
@@ -249,8 +255,11 @@ public class ShortcutHelper {
if (!TextUtils.isEmpty(shortcutId)) {
packageBubbles.remove(shortcutId);
} else {
+ // Copy the shortcut IDs to avoid a concurrent modification exception.
+ final Set<String> shortcutIds = new HashSet<>(packageBubbles.keySet());
+
// Check if there was a matching entry
- for (String pkgShortcutId : packageBubbles.keySet()) {
+ for (String pkgShortcutId : shortcutIds) {
String entryKey = packageBubbles.get(pkgShortcutId);
if (r.getKey().equals(entryKey)) {
// No longer has shortcut id so remove it
diff --git a/services/core/java/com/android/server/os/TEST_MAPPING b/services/core/java/com/android/server/os/TEST_MAPPING
index 502f1e852e08..a837fb4184ba 100644
--- a/services/core/java/com/android/server/os/TEST_MAPPING
+++ b/services/core/java/com/android/server/os/TEST_MAPPING
@@ -1,7 +1,43 @@
{
"presubmit": [
+ // TODO(159590499) add BugreportManagerTestCases
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "CtsBugreportTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ },
{
"name": "CtsUsbTests"
+ },
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "ShellTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "BugreportManagerTestCases"
+ },
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "CtsBugreportTestCases"
+ },
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "ShellTests"
}
]
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 3810e14ff04d..249b6801758b 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -77,6 +77,8 @@ public abstract class ApexManager {
public static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
static final int MATCH_FACTORY_PACKAGE = 1 << 1;
+ private static final String VNDK_APEX_MODULE_NAME_PREFIX = "com.android.vndk.";
+
private static final Singleton<ApexManager> sApexManagerSingleton =
new Singleton<ApexManager>() {
@Override
@@ -342,14 +344,28 @@ public abstract class ApexManager {
public abstract boolean destroyDeSnapshots(int rollbackId);
/**
+ * Deletes snapshots of the credential encrypted apex data directories for the specified user,
+ * for the given rollback id as long as the user is credential unlocked.
+ *
+ * @return boolean true if the delete was successful
+ */
+ public abstract boolean destroyCeSnapshots(int userId, int rollbackId);
+
+ /**
* Deletes snapshots of the credential encrypted apex data directories for the specified user,
- * where the rollback id is not included in {@code retainRollbackIds}.
+ * where the rollback id is not included in {@code retainRollbackIds} as long as the user is
+ * credential unlocked.
*
* @return boolean true if the delete was successful
*/
public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds);
/**
+ * Inform apexd that the boot has completed.
+ */
+ public abstract void markBootCompleted();
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -521,7 +537,9 @@ public abstract class ApexManager {
activePackagesSet.add(packageInfo.packageName);
}
if (ai.isFactory) {
- if (factoryPackagesSet.contains(packageInfo.packageName)) {
+ // Don't throw when the duplicating APEX is VNDK APEX
+ if (factoryPackagesSet.contains(packageInfo.packageName)
+ && !ai.moduleName.startsWith(VNDK_APEX_MODULE_NAME_PREFIX)) {
throw new IllegalStateException(
"Two factory packages have the same name: "
+ packageInfo.packageName);
@@ -545,76 +563,85 @@ public abstract class ApexManager {
@Override
@Nullable
- public PackageInfo getPackageInfo(String packageName,
- @PackageInfoFlags int flags) {
- Preconditions.checkState(mAllPackagesCache != null,
- "APEX packages have not been scanned");
- boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
- boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
- for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
- final PackageInfo packageInfo = mAllPackagesCache.get(i);
- if (!packageInfo.packageName.equals(packageName)) {
- continue;
- }
- if ((matchActive && isActive(packageInfo))
- || (matchFactory && isFactory(packageInfo))) {
- return packageInfo;
+ public PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
+ synchronized (mLock) {
+ Preconditions.checkState(mAllPackagesCache != null,
+ "APEX packages have not been scanned");
+ boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
+ boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
+ for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
+ final PackageInfo packageInfo = mAllPackagesCache.get(i);
+ if (!packageInfo.packageName.equals(packageName)) {
+ continue;
+ }
+ if ((matchActive && isActive(packageInfo))
+ || (matchFactory && isFactory(packageInfo))) {
+ return packageInfo;
+ }
}
+ return null;
}
- return null;
}
@Override
List<PackageInfo> getActivePackages() {
- Preconditions.checkState(mAllPackagesCache != null,
- "APEX packages have not been scanned");
- final List<PackageInfo> activePackages = new ArrayList<>();
- for (int i = 0; i < mAllPackagesCache.size(); i++) {
- final PackageInfo packageInfo = mAllPackagesCache.get(i);
- if (isActive(packageInfo)) {
- activePackages.add(packageInfo);
+ synchronized (mLock) {
+ Preconditions.checkState(mAllPackagesCache != null,
+ "APEX packages have not been scanned");
+ final List<PackageInfo> activePackages = new ArrayList<>();
+ for (int i = 0; i < mAllPackagesCache.size(); i++) {
+ final PackageInfo packageInfo = mAllPackagesCache.get(i);
+ if (isActive(packageInfo)) {
+ activePackages.add(packageInfo);
+ }
}
+ return activePackages;
}
- return activePackages;
}
@Override
List<PackageInfo> getFactoryPackages() {
- Preconditions.checkState(mAllPackagesCache != null,
- "APEX packages have not been scanned");
- final List<PackageInfo> factoryPackages = new ArrayList<>();
- for (int i = 0; i < mAllPackagesCache.size(); i++) {
- final PackageInfo packageInfo = mAllPackagesCache.get(i);
- if (isFactory(packageInfo)) {
- factoryPackages.add(packageInfo);
+ synchronized (mLock) {
+ Preconditions.checkState(mAllPackagesCache != null,
+ "APEX packages have not been scanned");
+ final List<PackageInfo> factoryPackages = new ArrayList<>();
+ for (int i = 0; i < mAllPackagesCache.size(); i++) {
+ final PackageInfo packageInfo = mAllPackagesCache.get(i);
+ if (isFactory(packageInfo)) {
+ factoryPackages.add(packageInfo);
+ }
}
+ return factoryPackages;
}
- return factoryPackages;
}
@Override
List<PackageInfo> getInactivePackages() {
- Preconditions.checkState(mAllPackagesCache != null,
- "APEX packages have not been scanned");
- final List<PackageInfo> inactivePackages = new ArrayList<>();
- for (int i = 0; i < mAllPackagesCache.size(); i++) {
- final PackageInfo packageInfo = mAllPackagesCache.get(i);
- if (!isActive(packageInfo)) {
- inactivePackages.add(packageInfo);
+ synchronized (mLock) {
+ Preconditions.checkState(mAllPackagesCache != null,
+ "APEX packages have not been scanned");
+ final List<PackageInfo> inactivePackages = new ArrayList<>();
+ for (int i = 0; i < mAllPackagesCache.size(); i++) {
+ final PackageInfo packageInfo = mAllPackagesCache.get(i);
+ if (!isActive(packageInfo)) {
+ inactivePackages.add(packageInfo);
+ }
}
+ return inactivePackages;
}
- return inactivePackages;
}
@Override
boolean isApexPackage(String packageName) {
if (!isApexSupported()) return false;
- Preconditions.checkState(mAllPackagesCache != null,
- "APEX packages have not been scanned");
- for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
- final PackageInfo packageInfo = mAllPackagesCache.get(i);
- if (packageInfo.packageName.equals(packageName)) {
- return true;
+ synchronized (mLock) {
+ Preconditions.checkState(mAllPackagesCache != null,
+ "APEX packages have not been scanned");
+ for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
+ final PackageInfo packageInfo = mAllPackagesCache.get(i);
+ if (packageInfo.packageName.equals(packageName)) {
+ return true;
+ }
}
}
return false;
@@ -622,14 +649,11 @@ public abstract class ApexManager {
@Override
@Nullable
- public String getActiveApexPackageNameContainingPackage(
- @NonNull AndroidPackage containedPackage) {
- Preconditions.checkState(mPackageNameToApexModuleName != null,
- "APEX packages have not been scanned");
-
+ public String getActiveApexPackageNameContainingPackage(AndroidPackage containedPackage) {
Objects.requireNonNull(containedPackage);
-
synchronized (mLock) {
+ Preconditions.checkState(mPackageNameToApexModuleName != null,
+ "APEX packages have not been scanned");
int numApksInApex = mApksInApex.size();
for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) {
if (mApksInApex.valueAt(apkInApexNum).contains(
@@ -870,6 +894,17 @@ public abstract class ApexManager {
}
@Override
+ public boolean destroyCeSnapshots(int userId, int rollbackId) {
+ try {
+ waitForApexService().destroyCeSnapshots(userId, rollbackId);
+ return true;
+ } catch (Exception e) {
+ Slog.e(TAG, e.getMessage(), e);
+ return false;
+ }
+ }
+
+ @Override
public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
try {
waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
@@ -880,6 +915,15 @@ public abstract class ApexManager {
}
}
+ @Override
+ public void markBootCompleted() {
+ try {
+ waitForApexService().markBootCompleted();
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to contact apexservice", re);
+ }
+ }
+
/**
* Dump information about the packages contained in a particular cache
* @param packagesCache the cache to print information about.
@@ -944,9 +988,11 @@ public abstract class ApexManager {
}
ipw.decreaseIndent();
ipw.println();
- if (mAllPackagesCache == null) {
- ipw.println("APEX packages have not been scanned");
- return;
+ synchronized (mLock) {
+ if (mAllPackagesCache == null) {
+ ipw.println("APEX packages have not been scanned");
+ return;
+ }
}
ipw.println("Active APEX packages:");
dumpFromPackagesCache(getActivePackages(), packageName, ipw);
@@ -985,12 +1031,17 @@ public abstract class ApexManager {
if (files != null) {
for (File file : files) {
if (file.isDirectory() && !file.getName().contains("@")) {
+ boolean skip = false;
for (String skipDir : skipDirs) {
if (file.getName().equals(skipDir)) {
- continue;
+ skip = true;
+ break;
}
}
- result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
+ if (!skip) {
+ result.add(
+ new ActiveApexInfo(file, Environment.getRootDirectory()));
+ }
}
}
}
@@ -1122,11 +1173,21 @@ public abstract class ApexManager {
}
@Override
+ public boolean destroyCeSnapshots(int userId, int rollbackId) {
+ return true;
+ }
+
+ @Override
public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
return true;
}
@Override
+ public void markBootCompleted() {
+ // No-op
+ }
+
+ @Override
void dump(PrintWriter pw, String packageName) {
// No-op
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index b219e265072d..c3c2e5e65103 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -280,11 +280,15 @@ public class AppsFilter {
@Override
public void onCompatChange(String packageName) {
- updateEnabledState(mPmInternal.getPackage(packageName));
+ AndroidPackage pkg = mPmInternal.getPackage(packageName);
+ if (pkg == null) {
+ return;
+ }
+ updateEnabledState(pkg);
mAppsFilter.updateShouldFilterCacheForPackage(packageName);
}
- private void updateEnabledState(AndroidPackage pkg) {
+ private void updateEnabledState(@NonNull AndroidPackage pkg) {
// TODO(b/135203078): Do not use toAppInfo
final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternal(
PackageManager.FILTER_APPLICATION_QUERY, pkg.toAppInfoWithoutState());
@@ -297,12 +301,12 @@ public class AppsFilter {
@Override
public void updatePackageState(PackageSetting setting, boolean removed) {
- final boolean enableLogging =
+ final boolean enableLogging = setting.pkg != null &&
!removed && (setting.pkg.isTestOnly() || setting.pkg.isDebuggable());
enableLogging(setting.appId, enableLogging);
if (removed) {
- mDisabledPackages.remove(setting.pkg.getPackageName());
- } else {
+ mDisabledPackages.remove(setting.name);
+ } else if (setting.pkg != null) {
updateEnabledState(setting.pkg);
}
}
@@ -583,8 +587,9 @@ public class AppsFilter {
}
}
// if either package instruments the other, mark both as visible to one another
- if (pkgInstruments(newPkgSetting, existingSetting)
- || pkgInstruments(existingSetting, newPkgSetting)) {
+ if (newPkgSetting.pkg != null && existingSetting.pkg != null
+ && (pkgInstruments(newPkgSetting.pkg, existingSetting.pkg)
+ || pkgInstruments(existingSetting.pkg, newPkgSetting.pkg))) {
mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
}
@@ -703,12 +708,15 @@ public class AppsFilter {
return ret;
}
+ /**
+ * This method recomputes all component / intent-based visibility and is intended to match the
+ * relevant logic of {@link #addPackageInternal(PackageSetting, ArrayMap)}
+ */
private void recomputeComponentVisibility(ArrayMap<String, PackageSetting> existingSettings) {
mQueriesViaComponent.clear();
for (int i = existingSettings.size() - 1; i >= 0; i--) {
PackageSetting setting = existingSettings.valueAt(i);
- if (setting.pkg == null
- || mForceQueryable.contains(setting.appId)) {
+ if (setting.pkg == null || requestsQueryAllPackages(setting.pkg)) {
continue;
}
for (int j = existingSettings.size() - 1; j >= 0; j--) {
@@ -716,7 +724,7 @@ public class AppsFilter {
continue;
}
final PackageSetting otherSetting = existingSettings.valueAt(j);
- if (otherSetting.pkg == null) {
+ if (otherSetting.pkg == null || mForceQueryable.contains(otherSetting.appId)) {
continue;
}
if (canQueryViaComponents(setting.pkg, otherSetting.pkg, mProtectedBroadcasts)) {
@@ -1106,16 +1114,14 @@ public class AppsFilter {
}
/** Returns {@code true} if the source package instruments the target package. */
- private static boolean pkgInstruments(PackageSetting source, PackageSetting target) {
+ private static boolean pkgInstruments(
+ @NonNull AndroidPackage source, @NonNull AndroidPackage target) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "pkgInstruments");
- final String packageName = target.pkg.getPackageName();
- final List<ParsedInstrumentation> inst = source.pkg.getInstrumentations();
+ final String packageName = target.getPackageName();
+ final List<ParsedInstrumentation> inst = source.getInstrumentations();
for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
if (Objects.equals(inst.get(i).getTargetPackage(), packageName)) {
- if (DEBUG_LOGGING) {
- log(source, target, "instrumentation");
- }
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 6fdde7a196cc..fe6aad70c31e 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -10,8 +10,8 @@ toddke@android.com
toddke@google.com
# apex support
-per-file ApexManager.java = dariofreni@google.com
-per-file StagingManager.java = dariofreni@google.com
+per-file ApexManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
+per-file StagingManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
# dex
per-file AbstractStatsBase.java = agampe@google.com, calin@google.com, ngeoffray@google.com
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 2df4a920d5c2..eddab76de5ee 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
import android.content.Context;
@@ -42,10 +43,9 @@ import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
/**
* A service for A/B OTA dexopting.
@@ -123,15 +123,20 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
}
final List<PackageSetting> important;
final List<PackageSetting> others;
+ Predicate<PackageSetting> isPlatformPackage = pkgSetting ->
+ PLATFORM_PACKAGE_NAME.equals(pkgSetting.pkg.getPackageName());
synchronized (mPackageManagerService.mLock) {
// Important: the packages we need to run with ab-ota compiler-reason.
important = PackageManagerServiceUtils.getPackagesForDexopt(
mPackageManagerService.mSettings.mPackages.values(), mPackageManagerService,
DEBUG_DEXOPT);
+ // Remove Platform Package from A/B OTA b/160735835.
+ important.removeIf(isPlatformPackage);
// Others: we should optimize this with the (first-)boot compiler-reason.
others = new ArrayList<>(mPackageManagerService.mSettings.mPackages.values());
others.removeAll(important);
others.removeIf(PackageManagerServiceUtils.REMOVE_IF_NULL_PKG);
+ others.removeIf(isPlatformPackage);
// Pre-size the array list by over-allocating by a factor of 1.5.
mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
@@ -147,7 +152,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
throw new IllegalStateException("Found a core app that's not important");
}
mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.pkg, pkgSetting,
- PackageManagerService.REASON_FIRST_BOOT));
+ PackageManagerService.REASON_FIRST_BOOT));
}
completeSize = mDexoptCommands.size();
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index e355bb9f6e60..30b1c2c93a45 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Pair;
@@ -27,6 +28,8 @@ import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.File;
import java.util.Set;
+
+
// TODO: Move to .parsing sub-package
@VisibleForTesting
public interface PackageAbiHelper {
@@ -34,7 +37,8 @@ public interface PackageAbiHelper {
* Derive and get the location of native libraries for the given package,
* which varies depending on where and how the package was installed.
*/
- NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting,
+ @NonNull
+ NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg, boolean isUpdatedSystemApp,
File appLib32InstallDir);
/**
@@ -51,7 +55,7 @@ public interface PackageAbiHelper {
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isUpdatedSystemApp,
- String cpuAbiOverride, boolean extractLibs) throws PackageManagerException;
+ String cpuAbiOverride) throws PackageManagerException;
/**
* Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index fc58968a7325..8af7e1f4f6d1 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -131,16 +131,16 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
}
@Override
- public NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting,
- File appLib32InstallDir) {
+ public NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg,
+ boolean isUpdatedSystemApp, File appLib32InstallDir) {
// Trying to derive the paths, thus need the raw ABI info from the parsed package, and the
// current state in PackageSetting is irrelevant.
- return getNativeLibraryPaths(new Abis(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi()),
+ return deriveNativeLibraryPaths(new Abis(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi()),
appLib32InstallDir, pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
- pkgSetting.getPkgState().isUpdatedSystemApp());
+ isUpdatedSystemApp);
}
- private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis,
+ private static NativeLibraryPaths deriveNativeLibraryPaths(final Abis abis,
final File appLib32InstallDir, final String codePath, final String sourceDir,
final boolean isSystemApp, final boolean isUpdatedSystemApp) {
final File codeFile = new File(codePath);
@@ -296,22 +296,19 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
@Override
public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg,
- boolean isUpdatedSystemApp, String cpuAbiOverride, boolean extractLibs)
+ boolean isUpdatedSystemApp, String cpuAbiOverride)
throws PackageManagerException {
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(pkg);
String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg);
- final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(
+ final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths(
new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi),
PackageManagerService.sAppLib32InstallDir, pkg.getCodePath(),
pkg.getBaseCodePath(), pkg.isSystem(),
isUpdatedSystemApp);
- // We shouldn't attempt to extract libs from system app when it was not updated.
- if (pkg.isSystem() && !isUpdatedSystemApp) {
- extractLibs = false;
- }
+ final boolean extractLibs = shouldExtractLibs(pkg, isUpdatedSystemApp);
final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir;
final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa;
@@ -455,11 +452,21 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
return new Pair<>(abis,
- getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir,
+ deriveNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir,
pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
isUpdatedSystemApp));
}
+ private boolean shouldExtractLibs(AndroidPackage pkg, boolean isUpdatedSystemApp) {
+ // We shouldn't extract libs if the package is a library or if extractNativeLibs=false
+ boolean extractLibs = !AndroidPackageUtils.isLibrary(pkg) && pkg.isExtractNativeLibs();
+ // We shouldn't attempt to extract libs from system app when it was not updated.
+ if (pkg.isSystem() && !isUpdatedSystemApp) {
+ extractLibs = false;
+ }
+ return extractLibs;
+ }
+
/**
* Adjusts ABIs for a set of packages belonging to a shared user so that they all match.
* i.e, so that all packages can be run inside a single process if required.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 843f0ae75a1a..312dcddd577d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -84,6 +84,8 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -201,6 +203,27 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
};
+ private static final class Lifecycle extends SystemService {
+ private final PackageInstallerService mPackageInstallerService;
+
+ Lifecycle(Context context, PackageInstallerService service) {
+ super(context);
+ mPackageInstallerService = service;
+ }
+
+ @Override
+ public void onStart() {
+ // no-op
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mPackageInstallerService.onBroadcastReady();
+ }
+ }
+ }
+
public PackageInstallerService(Context context, PackageManagerService pm,
Supplier<PackageParser2> apexParserSupplier) {
mContext = context;
@@ -222,6 +245,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
mApexManager = ApexManager.getInstance();
mStagingManager = new StagingManager(this, context, apexParserSupplier);
+
+ LocalServices.getService(SystemServiceManager.class).startService(
+ new Lifecycle(context, this));
}
boolean okToSendBroadcasts() {
@@ -259,6 +285,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
+ private void onBroadcastReady() {
+ // Broadcasts are not sent while we restore sessions on boot, since no processes would be
+ // ready to listen to them. From now on, it is safe to send broadcasts which otherwise will
+ // be rejected by ActivityManagerService if its systemReady() is not completed.
+ mOkToSendBroadcasts = true;
+ }
+
void restoreAndApplyStagedSessionIfNeeded() {
List<PackageInstallerSession> stagedSessionsToRestore = new ArrayList<>();
synchronized (mSessions) {
@@ -281,16 +314,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
mStagingManager.restoreSession(session, isDeviceUpgrading);
}
- // Broadcasts are not sent while we restore sessions on boot, since no processes would be
- // ready to listen to them. From now on, we greedily assume that broadcasts requests are
- // safe to send out. The worst that can happen is that a broadcast is attempted before
- // ActivityManagerService completes its own systemReady(), in which case it will be rejected
- // with an otherwise harmless exception.
- // A more appropriate way to do this would be to wait until the correct boot phase is
- // reached, but since we are not a SystemService we can't override onBootPhase.
- // Waiting on the BOOT_COMPLETED broadcast can take several minutes, so that's not a viable
- // way either.
- mOkToSendBroadcasts = true;
}
@GuardedBy("mSessions")
@@ -525,7 +548,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;
-
+ // adb installs can override the installingPackageName, but not the
+ // initiatingPackageName
+ installerPackageName = null;
} else {
if (callingUid != Process.SYSTEM_UID) {
// The supplied installerPackageName must always belong to the calling app.
@@ -590,12 +615,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
- if (mBypassNextStagedInstallerCheck) {
- mBypassNextStagedInstallerCheck = false;
- } else if (params.isStaged
- && !isCalledBySystemOrShell(callingUid)
- && !isWhitelistedStagedInstaller(requestedInstallerPackageName)) {
- throw new SecurityException("Installer not allowed to commit staged install");
+ if (params.isStaged && !isCalledBySystemOrShell(callingUid)) {
+ if (mBypassNextStagedInstallerCheck) {
+ mBypassNextStagedInstallerCheck = false;
+ } else if (!isStagedInstallerAllowed(requestedInstallerPackageName)) {
+ throw new SecurityException("Installer not allowed to commit staged install");
+ }
}
if (!params.isMultiPackage) {
@@ -727,7 +752,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|| callingUid == Process.SHELL_UID;
}
- private boolean isWhitelistedStagedInstaller(String installerName) {
+ private boolean isStagedInstallerAllowed(String installerName) {
return SystemConfig.getInstance().getWhitelistedStagedInstallers().contains(installerName);
}
@@ -867,7 +892,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
@Override
public ParceledListSlice<SessionInfo> getStagedSessions() {
- return mStagingManager.getSessions(Binder.getCallingUid());
+ final List<SessionInfo> result = new ArrayList<>();
+ synchronized (mSessions) {
+ for (int i = 0; i < mSessions.size(); i++) {
+ final PackageInstallerSession session = mSessions.valueAt(i);
+ if (session.isStaged() && !session.isDestroyed()) {
+ result.add(session.generateInfoForCaller(false, Binder.getCallingUid()));
+ }
+ }
+ }
+ return new ParceledListSlice<>(result);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a2ada46f2f97..391a08db6716 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -268,6 +268,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** Uid of the creator of this session. */
private final int mOriginalInstallerUid;
+ /** Package name of the app that created the installation session. */
+ private final String mOriginalInstallerPackageName;
+
/** Uid of the owner of the installer session */
@GuardedBy("mLock")
private int mInstallerUid;
@@ -400,9 +403,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private boolean mVerityFound;
+ @GuardedBy("mLock")
private boolean mDataLoaderFinished = false;
- // TODO(b/159663586): should be protected by mLock
+ @GuardedBy("mLock")
private IncrementalFileStorages mIncrementalFileStorages;
private static final FileFilter sAddedApkFilter = new FileFilter() {
@@ -456,7 +460,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
args.recycle();
sendOnPackageInstalled(mContext, statusReceiver, sessionId,
- isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
packageName, returnCode, message, extras);
break;
@@ -486,8 +490,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
- @GuardedBy("mLock")
- private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
+ private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwner() {
+ assertNotLocked("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
+ // It is safe to access mInstallerUid and mInstallSource without lock
+ // because they are immutable after sealing.
+ assertSealed("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
if (userId != UserHandle.getUserId(mInstallerUid)) {
return false;
}
@@ -505,12 +512,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
*
* @return {@code true} iff we need to ask to confirm the permissions?
*/
- @GuardedBy("mLock")
- private boolean needToAskForPermissionsLocked() {
- if (mPermissionsManuallyAccepted) {
- return false;
+ private boolean needToAskForPermissions() {
+ final String packageName;
+ synchronized (mLock) {
+ if (mPermissionsManuallyAccepted) {
+ return false;
+ }
+ packageName = mPackageName;
}
+ // It is safe to access mInstallerUid and mInstallSource without lock
+ // because they are immutable after sealing.
final boolean isInstallPermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
@@ -520,7 +532,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isUpdatePermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
- final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
+ final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
final boolean isPermissionGranted = isInstallPermissionGranted
|| (isUpdatePermissionGranted && targetPackageUid != -1)
|| (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
@@ -532,7 +544,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
- || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
+ || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner());
}
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
@@ -557,6 +569,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mOriginalInstallerUid = installerUid;
mInstallerUid = installerUid;
mInstallSource = Objects.requireNonNull(installSource);
+ mOriginalInstallerPackageName = mInstallSource.installerPackageName;
this.params = params;
this.createdMillis = createdMillis;
this.updatedMillis = createdMillis;
@@ -606,14 +619,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- if (isIncrementalInstallation()) {
- if (!IncrementalManager.isAllowed()) {
- throw new IllegalArgumentException("Incremental installation not allowed.");
- }
- if (!isIncrementalInstallationAllowed(mPackageName)) {
- throw new IllegalArgumentException(
- "Incremental installation of this package is not allowed.");
- }
+ if (isIncrementalInstallation() && !IncrementalManager.isAllowed()) {
+ throw new IllegalArgumentException("Incremental installation not allowed.");
}
}
@@ -737,6 +744,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private void assertNotLocked(String cookie) {
+ if (Thread.holdsLock(mLock)) {
+ throw new IllegalStateException(cookie + " is holding mLock");
+ }
+ }
+
+ private void assertSealed(String cookie) {
+ if (!isSealed()) {
+ throw new IllegalStateException(cookie + " before sealing");
+ }
+ }
+
@GuardedBy("mLock")
private void assertPreparedAndNotSealedLocked(String cookie) {
assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
@@ -889,6 +908,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return markerName;
}
+ @GuardedBy("mLock")
private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
try {
final File target = new File(stageDir, getRemoveMarkerName(splitName));
@@ -1059,6 +1079,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ @GuardedBy("mLock")
private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
try {
if (!FileUtils.isValidExtFilename(name)) {
@@ -1120,20 +1141,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (hasParentSessionId()) {
throw new IllegalStateException(
"Session " + sessionId + " is a child of multi-package session "
- + mParentSessionId + " and may not be committed directly.");
+ + getParentSessionId() + " and may not be committed directly.");
}
if (!markAsSealed(statusReceiver, forTransfer)) {
return;
}
if (isMultiPackage()) {
- final SparseIntArray remainingSessions = mChildSessionIds.clone();
+ final SparseIntArray remainingSessions;
+ final int[] childSessionIds;
+ synchronized (mLock) {
+ remainingSessions = mChildSessionIds.clone();
+ childSessionIds = mChildSessionIds.copyKeys();
+ }
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
.getIntentSender();
boolean sealFailed = false;
- for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
- final int childSessionId = mChildSessionIds.keyAt(i);
+ for (int i = childSessionIds.length - 1; i >= 0; --i) {
+ final int childSessionId = childSessionIds[i];
// seal all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mSessionProvider.getSession(childSessionId)
@@ -1165,13 +1191,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
if (isMultiPackage()) {
- int childCount = mChildSessionIds.size();
+ final int[] childSessionIds;
+ synchronized (mLock) {
+ childSessionIds = mChildSessionIds.copyKeys();
+ }
+ int childCount = childSessionIds.length;
// This will contain all child sessions that do not encounter an unrecoverable failure
ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount);
for (int i = childCount - 1; i >= 0; --i) {
- final int childSessionId = mChildSessionIds.keyAt(i);
+ final int childSessionId = childSessionIds[i];
// commit all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
try {
@@ -1582,12 +1612,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void onStorageUnhealthy() {
- if (TextUtils.isEmpty(mPackageName)) {
+ final String packageName = getPackageName();
+ if (TextUtils.isEmpty(packageName)) {
// The package has not been installed.
return;
}
final PackageManagerService packageManagerService = mPm;
- final String packageName = mPackageName;
mHandler.post(() -> {
if (packageManagerService.deletePackageX(packageName,
PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
@@ -1668,11 +1698,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new IllegalArgumentException("Package is not valid", e);
}
- if (!mPackageName.equals(mInstallSource.installerPackageName)) {
- throw new SecurityException("Can only transfer sessions that update the original "
- + "installer");
- }
-
mInstallerUid = newOwnerAppInfo.uid;
mInstallSource = InstallSource.create(packageName, null, packageName, null);
}
@@ -1684,7 +1709,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void handleInstall() {
- if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) {
+ if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
.setAdmin(mInstallSource.installerPackageName)
@@ -1711,9 +1736,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
try {
- synchronized (mLock) {
- installNonStagedLocked(childSessions);
- }
+ installNonStaged(childSessions);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
@@ -1722,11 +1745,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- @GuardedBy("mLock")
- private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
+ private void installNonStaged(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
final PackageManagerService.ActiveInstallSession installingSession =
- makeSessionActiveLocked();
+ makeSessionActive();
if (installingSession == null) {
return;
}
@@ -1739,7 +1761,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.ActiveInstallSession installingChildSession =
- session.makeSessionActiveLocked();
+ session.makeSessionActive();
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
}
@@ -1749,12 +1771,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
if (!success) {
- sendOnPackageInstalled(mContext, mRemoteStatusReceiver, sessionId,
- isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
}
- mPm.installStage(installingChildSessions);
+ mPm.installStage(installingSession, installingChildSessions);
} else {
mPm.installStage(installingSession);
}
@@ -1765,41 +1791,58 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null
* in case permissions need to be requested before install can proceed.
*/
- @GuardedBy("mLock")
- private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
+ private PackageManagerService.ActiveInstallSession makeSessionActive()
throws PackageManagerException {
- if (mRelinquished) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session relinquished");
+ assertNotLocked("makeSessionActive");
+
+ synchronized (mLock) {
+ if (mRelinquished) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session relinquished");
+ }
+ if (mDestroyed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session destroyed");
+ }
+ if (!mSealed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session not sealed");
+ }
}
- if (mDestroyed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
+
+ if (!params.isMultiPackage && needToAskForPermissions()) {
+ // User needs to confirm installation;
+ // give installer an intent they can use to involve
+ // user.
+ final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+ intent.setPackage(mPm.getPackageInstallerPackageName());
+ intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+
+ // Commit was keeping session marked as active until now; release
+ // that extra refcount so session appears idle.
+ closeInternal(false);
+ return null;
}
- if (!mSealed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
+
+ synchronized (mLock) {
+ return makeSessionActiveLocked();
}
+ }
+ @GuardedBy("mLock")
+ private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
+ throws PackageManagerException {
if (!params.isMultiPackage) {
Objects.requireNonNull(mPackageName);
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
- if (needToAskForPermissionsLocked()) {
- // User needs to confirm installation;
- // give installer an intent they can use to involve
- // user.
- final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
- intent.setPackage(mPm.getPackageInstallerPackageName());
- intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-
- sendOnUserActionRequired(mContext, mRemoteStatusReceiver, sessionId, intent);
-
- // Commit was keeping session marked as active until now; release
- // that extra refcount so session appears idle.
- closeInternal(false);
- return null;
- }
-
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
@@ -1873,6 +1916,34 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
};
+ // An observer through which PMS returns the result of verification
+ // TODO(samiul): We are temporarily assigning two observer to ActiveInstallSession. One for
+ // installation and one for verification. This will be fixed within next few CLs.
+ final IPackageInstallObserver2 sessionVerificationObserver;
+ if (!hasParentSessionId()) {
+ // Avoid attaching this observer to child session since they won't use it.
+ sessionVerificationObserver = new IPackageInstallObserver2.Stub() {
+ @Override
+ public void onUserActionRequired(Intent intent) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+ Bundle extras) {
+ if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ // TODO(samiul): In future, packages will not be installed immediately after
+ // verification. Package verification will return control back to here,
+ // and we will have call into PMS again to install package.
+ //
+ // For now, this is a no op.
+ }
+ }
+ };
+ } else {
+ sessionVerificationObserver = null;
+ }
+
final UserHandle user;
if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
@@ -1882,7 +1953,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mRelinquished = true;
return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir, localObserver,
- sessionId, params, mInstallerUid, mInstallSource, user, mSigningDetails);
+ sessionVerificationObserver, sessionId, params, mInstallerUid, mInstallSource, user,
+ mSigningDetails);
}
private static void maybeRenameFile(File from, File to) throws PackageManagerException {
@@ -1898,19 +1970,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Skip logging the side-loaded app installations, as those are private and aren't reported
// anywhere; app stores already have a record of the installation and that's why reporting
// it here is fine
+ final String packageName = getPackageName();
final String packageNameToLog =
- (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? mPackageName : "";
+ (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? packageName : "";
final long currentTimestamp = System.currentTimeMillis();
FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED,
isIncrementalInstallation(),
packageNameToLog,
currentTimestamp - createdMillis,
returnCode,
- getApksSize());
+ getApksSize(packageName));
}
- private long getApksSize() {
- final PackageSetting ps = mPm.getPackageSetting(mPackageName);
+ private long getApksSize(String packageName) {
+ final PackageSetting ps = mPm.getPackageSetting(packageName);
if (ps == null) {
return 0;
}
@@ -1986,7 +2059,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
final File targetFile = new File(stageDir, targetName);
- resolveAndStageFile(addedFile, targetFile);
+ resolveAndStageFileLocked(addedFile, targetFile);
mResolvedBaseFile = targetFile;
// Populate package name of the apex session
@@ -2102,7 +2175,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
final File targetFile = new File(stageDir, targetName);
- resolveAndStageFile(addedFile, targetFile);
+ resolveAndStageFileLocked(addedFile, targetFile);
// Base is coming from session
if (apk.splitName == null) {
@@ -2119,7 +2192,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
final File targetDexMetadataFile = new File(stageDir,
DexMetadataHelper.buildDexMetadataPathForApk(targetName));
- resolveAndStageFile(dexMetadataFile, targetDexMetadataFile);
+ resolveAndStageFileLocked(dexMetadataFile, targetDexMetadataFile);
}
}
@@ -2154,6 +2227,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ if (isIncrementalInstallation() && !isIncrementalInstallationAllowed(mPackageName)) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_SESSION_INVALID,
+ "Incremental installation of this package is not allowed.");
+ }
+
+ if (mInstallerUid != mOriginalInstallerUid) {
+ // Session has been transferred, check package name.
+ if (TextUtils.isEmpty(mPackageName) || !mPackageName.equals(
+ mOriginalInstallerPackageName)) {
+ throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED,
+ "Can only transfer sessions that update the original installer");
+ }
+ }
+
if (params.mode == SessionParams.MODE_FULL_INSTALL) {
// Full installs must include a base package
if (!stagedSplits.contains(null)) {
@@ -2184,12 +2272,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Inherit base if not overridden
if (mResolvedBaseFile == null) {
mResolvedBaseFile = new File(appInfo.getBaseCodePath());
- resolveInheritedFile(mResolvedBaseFile);
+ resolveInheritedFileLocked(mResolvedBaseFile);
// Inherit the dex metadata if present.
final File baseDexMetadataFile =
DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
if (baseDexMetadataFile != null) {
- resolveInheritedFile(baseDexMetadataFile);
+ resolveInheritedFileLocked(baseDexMetadataFile);
}
baseApk = existingBase;
}
@@ -2201,12 +2289,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final File splitFile = new File(existing.splitCodePaths[i]);
final boolean splitRemoved = removeSplitList.contains(splitName);
if (!stagedSplits.contains(splitName) && !splitRemoved) {
- resolveInheritedFile(splitFile);
+ resolveInheritedFileLocked(splitFile);
// Inherit the dex metadata if present.
final File splitDexMetadataFile =
DexMetadataHelper.findDexMetadataForFile(splitFile);
if (splitDexMetadataFile != null) {
- resolveInheritedFile(splitDexMetadataFile);
+ resolveInheritedFileLocked(splitDexMetadataFile);
}
}
}
@@ -2307,7 +2395,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- private void resolveAndStageFile(File origFile, File targetFile)
+ @GuardedBy("mLock")
+ private void resolveAndStageFileLocked(File origFile, File targetFile)
throws PackageManagerException {
mResolvedStagedFiles.add(targetFile);
maybeRenameFile(origFile, targetFile);
@@ -2340,7 +2429,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mResolvedStagedFiles.add(stagedSignature);
}
- private void resolveInheritedFile(File origFile) {
+ @GuardedBy("mLock")
+ private void resolveInheritedFileLocked(File origFile) {
mResolvedInheritedFiles.add(origFile);
// Inherit the fsverity signature file if present.
@@ -2378,7 +2468,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* Determine if creating hard links between source and destination is
* possible. That is, do they all live on the same underlying device.
*/
- private boolean isLinkPossible(List<File> fromFiles, File toDir) {
+ private static boolean isLinkPossible(List<File> fromFiles, File toDir) {
try {
final StructStat toStat = Os.stat(toDir.getAbsolutePath());
for (File fromFile : fromFiles) {
@@ -2531,7 +2621,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
void setPermissionsResult(boolean accepted) {
- if (!mSealed) {
+ if (!isSealed()) {
throw new SecurityException("Must be sealed to accept permissions");
}
@@ -2551,7 +2641,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* Adds a child session ID without any safety / sanity checks. This should only be used to
* build a session from XML or similar.
*/
- void addChildSessionIdInternal(int sessionId) {
+ @GuardedBy("mLock")
+ void addChildSessionIdLocked(int sessionId) {
mChildSessionIds.put(sessionId, 0);
}
@@ -2606,7 +2697,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (hasParentSessionId()) {
throw new IllegalStateException(
"Session " + sessionId + " is a child of multi-package session "
- + mParentSessionId + " and may not be abandoned directly.");
+ + getParentSessionId() + " and may not be abandoned directly.");
}
List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
@@ -2776,7 +2867,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
- if (mDestroyed || mDataLoaderFinished) {
+ final boolean isDestroyedOrDataLoaderFinished;
+ synchronized (mLock) {
+ isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished;
+ }
+ if (isDestroyedOrDataLoaderFinished) {
switch (status) {
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
onStorageUnhealthy();
@@ -2788,7 +2883,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId);
if (dataLoader == null) {
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failure to obtain data loader");
return;
@@ -2821,10 +2918,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
break;
}
case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: {
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
if (hasParentSessionId()) {
mSessionProvider.getSession(
- mParentSessionId).dispatchStreamValidateAndCommit();
+ getParentSessionId()).dispatchStreamValidateAndCommit();
} else {
dispatchStreamValidateAndCommit();
}
@@ -2834,7 +2933,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
break;
}
case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to prepare image.");
if (manualStartAndDestroy) {
@@ -2844,11 +2945,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
// Don't fail or commit the session. Allow caller to commit again.
- sendPendingStreaming("DataLoader unavailable");
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendPendingStreaming(mContext, statusReceiver, sessionId,
+ "DataLoader unavailable");
break;
}
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"DataLoader reported unrecoverable failure.");
break;
@@ -2856,7 +2964,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
// Just return from this method and allow caller to commit again.
- sendPendingStreaming(e.getMessage());
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
}
}
};
@@ -2872,7 +2984,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
@Override
public void onHealthStatus(int storageId, int status) {
- if (mDestroyed || mDataLoaderFinished) {
+ final boolean isDestroyedOrDataLoaderFinished;
+ synchronized (mLock) {
+ isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished;
+ }
+ if (isDestroyedOrDataLoaderFinished) {
// App's installed.
switch (status) {
case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY:
@@ -2894,7 +3010,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// fallthrough
case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY:
// Even ADB installation can't wait for missing pages for too long.
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Image is missing pages required for installation.");
break;
@@ -2925,22 +3043,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
detailMessage).sendToTarget();
}
+ @GuardedBy("mLock")
+ private int[] getChildSessionIdsLocked() {
+ final int[] childSessionIds = mChildSessionIds.copyKeys();
+ return childSessionIds != null ? childSessionIds : EMPTY_CHILD_SESSION_ARRAY;
+ }
+
@Override
public int[] getChildSessionIds() {
- final int[] childSessionIds = mChildSessionIds.copyKeys();
- if (childSessionIds != null) {
- return childSessionIds;
+ synchronized (mLock) {
+ return getChildSessionIdsLocked();
+ }
+ }
+
+ private boolean canBeAddedAsChild(int parentCandidate) {
+ synchronized (mLock) {
+ return (!hasParentSessionId() || mParentSessionId == parentCandidate)
+ && !mCommitted && !mDestroyed;
}
- return EMPTY_CHILD_SESSION_ARRAY;
}
@Override
public void addChildSessionId(int childSessionId) {
final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId);
- if (childSession == null
- || (childSession.hasParentSessionId() && childSession.mParentSessionId != sessionId)
- || childSession.mCommitted
- || childSession.mDestroyed) {
+ if (childSession == null || !childSession.canBeAddedAsChild(sessionId)) {
throw new IllegalStateException("Unable to add child session " + childSessionId
+ " as it does not exist or is in an invalid state.");
}
@@ -2953,7 +3079,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
childSession.setParentSessionId(this.sessionId);
- addChildSessionIdInternal(childSessionId);
+ addChildSessionIdLocked(childSessionId);
}
}
@@ -2989,12 +3115,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
boolean hasParentSessionId() {
- return mParentSessionId != SessionInfo.INVALID_ID;
+ synchronized (mLock) {
+ return mParentSessionId != SessionInfo.INVALID_ID;
+ }
}
@Override
public int getParentSessionId() {
- return mParentSessionId;
+ synchronized (mLock) {
+ return mParentSessionId;
+ }
}
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
@@ -3084,27 +3214,37 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** {@hide} */
boolean isStagedSessionReady() {
- return mStagedSessionReady;
+ synchronized (mLock) {
+ return mStagedSessionReady;
+ }
}
/** {@hide} */
boolean isStagedSessionApplied() {
- return mStagedSessionApplied;
+ synchronized (mLock) {
+ return mStagedSessionApplied;
+ }
}
/** {@hide} */
boolean isStagedSessionFailed() {
- return mStagedSessionFailed;
+ synchronized (mLock) {
+ return mStagedSessionFailed;
+ }
}
/** {@hide} */
@StagedSessionErrorCode int getStagedSessionErrorCode() {
- return mStagedSessionErrorCode;
+ synchronized (mLock) {
+ return mStagedSessionErrorCode;
+ }
}
/** {@hide} */
String getStagedSessionErrorMessage() {
- return mStagedSessionErrorMessage;
+ synchronized (mLock) {
+ return mStagedSessionErrorMessage;
+ }
}
private void destroyInternal() {
@@ -3120,10 +3260,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
for (FileBridge bridge : mBridges) {
bridge.forceClose();
}
- }
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.cleanUp();
- mIncrementalFileStorages = null;
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ mIncrementalFileStorages = null;
+ }
}
// For staged sessions, we don't delete the directory where the packages have been copied,
// since these packages are supposed to be read on reboot.
@@ -3160,9 +3300,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void cleanStageDir() {
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.cleanUp();
- mIncrementalFileStorages = null;
+ synchronized (mLock) {
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ mIncrementalFileStorages = null;
+ }
}
try {
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
@@ -3183,6 +3325,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.printPair("userId", userId);
pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
+ pw.printPair("mOriginalInstallerPackageName", mOriginalInstallerPackageName);
pw.printPair("installerPackageName", mInstallSource.installerPackageName);
pw.printPair("installInitiatingPackageName", mInstallSource.initiatingPackageName);
pw.printPair("installOriginatingPackageName", mInstallSource.originatingPackageName);
@@ -3220,6 +3363,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.decreaseIndent();
}
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
private static void sendOnUserActionRequired(Context context, IntentSender target,
int sessionId, Intent intent) {
final Intent fillIn = new Intent();
@@ -3232,6 +3378,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
boolean showNotification, int userId, String basePackageName, int returnCode,
String msg, Bundle extras) {
@@ -3272,13 +3421,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- private void sendPendingStreaming(@Nullable String cause) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
-
- if (statusReceiver == null) {
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
+ private static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
+ @Nullable String cause) {
+ if (target == null) {
Slog.e(TAG, "Missing receiver for pending streaming status.");
return;
}
@@ -3293,7 +3441,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
}
try {
- statusReceiver.sendIntent(mContext, 0, intent, null, null);
+ target.sendIntent(context, 0, intent, null, null);
} catch (IntentSender.SendIntentException ignored) {
}
}
@@ -3367,10 +3515,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (stageCid != null) {
writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
}
- writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
- writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
- writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed());
- writeBooleanAttribute(out, ATTR_SEALED, isSealed());
+ writeBooleanAttribute(out, ATTR_PREPARED, mPrepared);
+ writeBooleanAttribute(out, ATTR_COMMITTED, mCommitted);
+ writeBooleanAttribute(out, ATTR_DESTROYED, mDestroyed);
+ writeBooleanAttribute(out, ATTR_SEALED, mSealed);
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
@@ -3432,7 +3580,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
params.appIconLastModified = appIconFile.lastModified();
}
- final int[] childSessionIds = getChildSessionIds();
+ final int[] childSessionIds = getChildSessionIdsLocked();
for (int childSessionId : childSessionIds) {
out.startTag(null, TAG_CHILD_SESSION);
writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
@@ -3440,7 +3588,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
final InstallationFile[] files = getInstallationFilesLocked();
- for (InstallationFile file : getInstallationFilesLocked()) {
+ for (InstallationFile file : files) {
out.startTag(null, TAG_SESSION_FILE);
writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
writeStringAttribute(out, ATTR_NAME, file.getName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7ac60c4c8064..2a59893aa9f5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -51,7 +51,6 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTAL
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
@@ -59,6 +58,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_L
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
@@ -651,6 +651,23 @@ public class PackageManagerService extends IPackageManager.Stub
private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE =
150857253;
+ /**
+ * Apps targeting Android S and above need to declare dependencies to the public native
+ * shared libraries that are defined by the device maker using {@code uses-native-library} tag
+ * in its {@code AndroidManifest.xml}.
+ *
+ * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
+ * the package manager rejects to install the app. The dependency can be specified as optional
+ * using {@code android:required} attribute in the tag, in which case failing to satisfy the
+ * dependency doesn't stop the installation.
+ * <p>Once installed, an app is provided with only the native shared libraries that are
+ * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
+ * in the app manifest will fail even if it actually exists on the device.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
+
public static final String PLATFORM_PACKAGE_NAME = "android";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
@@ -2952,9 +2969,8 @@ public class PackageManagerService extends IPackageManager.Stub
= systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
- String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
- addBuiltInSharedLibraryLocked(entry.filename, name);
+ addBuiltInSharedLibraryLocked(libConfig.valueAt(i));
}
// Now that we have added all the libraries, iterate again to add dependency
@@ -6429,9 +6445,14 @@ public class PackageManagerService extends IPackageManager.Stub
true /*allowDynamicSplits*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ final boolean queryMayBeFiltered =
+ UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
+ && !resolveForStart;
+
final ResolveInfo bestChoice =
chooseBestActivity(
- intent, resolvedType, flags, privateResolveFlags, query, userId);
+ intent, resolvedType, flags, privateResolveFlags, query, userId,
+ queryMayBeFiltered);
final boolean nonBrowserOnly =
(privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
@@ -6490,7 +6511,7 @@ public class PackageManagerService extends IPackageManager.Stub
intent, resolvedType, flags, query, 0, false, true, false, userId);
// Add the new activity as the last chosen for this filter
addPreferredActivityInternal(filter, match, null, activity, false, userId,
- "Setting last chosen");
+ "Setting last chosen", false);
}
@Override
@@ -6595,7 +6616,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
- int flags, int privateResolveFlags, List<ResolveInfo> query, int userId) {
+ int flags, int privateResolveFlags, List<ResolveInfo> query, int userId,
+ boolean queryMayBeFiltered) {
if (query != null) {
final int N = query.size();
if (N == 1) {
@@ -6620,7 +6642,7 @@ public class PackageManagerService extends IPackageManager.Stub
// If we have saved a preference for a preferred activity for
// this Intent, use that.
ResolveInfo ri = findPreferredActivityNotLocked(intent, resolvedType,
- flags, query, r0.priority, true, false, debug, userId);
+ flags, query, r0.priority, true, false, debug, userId, queryMayBeFiltered);
if (ri != null) {
return ri;
}
@@ -6802,11 +6824,19 @@ public class PackageManagerService extends IPackageManager.Stub
&& intent.hasCategory(CATEGORY_DEFAULT);
}
+ ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> query, int priority, boolean always,
+ boolean removeMatches, boolean debug, int userId) {
+ return findPreferredActivityNotLocked(
+ intent, resolvedType, flags, query, priority, always, removeMatches, debug, userId,
+ UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
+ }
+
// TODO: handle preferred activities missing while user has amnesia
/** <b>must not hold {@link #mLock}</b> */
ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, int priority, boolean always,
- boolean removeMatches, boolean debug, int userId) {
+ boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
if (Thread.holdsLock(mLock)) {
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
@@ -6900,10 +6930,12 @@ public class PackageManagerService extends IPackageManager.Stub
}
final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
&& !isDeviceProvisioned;
+ final boolean allowSetMutation = !excludeSetupWizardHomeActivity
+ && !queryMayBeFiltered;
if (ai == null) {
// Do not remove launcher's preferred activity during SetupWizard
// due to it may not install yet
- if (excludeSetupWizardHomeActivity) {
+ if (!allowSetMutation) {
continue;
}
@@ -6928,7 +6960,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
- if (removeMatches) {
+ if (removeMatches && allowSetMutation) {
pir.removeFilter(pa);
changed = true;
if (DEBUG_PREFERRED) {
@@ -6945,7 +6977,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
- if (!excludeSetupWizardHomeActivity) {
+ if (allowSetMutation) {
// some components of the set are no longer present in
// the query, but the preferred activity can still be reused
if (DEBUG_PREFERRED) {
@@ -6966,24 +6998,28 @@ public class PackageManagerService extends IPackageManager.Stub
changed = true;
} else {
if (DEBUG_PREFERRED) {
- Slog.i(TAG, "Do not remove preferred activity for launcher"
- + " during SetupWizard");
+ Slog.i(TAG, "Do not remove preferred activity");
}
}
} else {
- Slog.i(TAG,
- "Result set changed, dropping preferred activity for "
- + intent + " type " + resolvedType);
- if (DEBUG_PREFERRED) {
- Slog.v(TAG, "Removing preferred activity since set changed "
- + pa.mPref.mComponent);
+ if (allowSetMutation) {
+ Slog.i(TAG,
+ "Result set changed, dropping preferred activity "
+ + "for " + intent + " type "
+ + resolvedType);
+ if (DEBUG_PREFERRED) {
+ Slog.v(TAG,
+ "Removing preferred activity since set changed "
+ + pa.mPref.mComponent);
+ }
+ pir.removeFilter(pa);
+ // Re-add the filter as a "last chosen" entry (!always)
+ PreferredActivity lastChosen = new PreferredActivity(
+ pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
+ false);
+ pir.addFilter(lastChosen);
+ changed = true;
}
- pir.removeFilter(pa);
- // Re-add the filter as a "last chosen" entry (!always)
- PreferredActivity lastChosen = new PreferredActivity(
- pa, pa.mPref.mMatch, null, pa.mPref.mComponent, false);
- pir.addFilter(lastChosen);
- changed = true;
return null;
}
}
@@ -7158,8 +7194,9 @@ public class PackageManagerService extends IPackageManager.Stub
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
- final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
- && shouldFilterApplicationLocked(
+ final boolean blockNormalResolution =
+ !resolveForStart && !isTargetInstantApp && !isCallerInstantApp
+ && shouldFilterApplicationLocked(
getPackageSettingInternal(ai.applicationInfo.packageName,
Process.SYSTEM_UID), filterCallingUid, userId);
if (!blockInstantResolution && !blockNormalResolution) {
@@ -7252,8 +7289,8 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageSetting setting =
getPackageSettingInternal(pkgName, Process.SYSTEM_UID);
result = null;
- if (setting != null && setting.pkg != null
- && !shouldFilterApplicationLocked(setting, filterCallingUid, userId)) {
+ if (setting != null && setting.pkg != null && (resolveForStart
+ || !shouldFilterApplicationLocked(setting, filterCallingUid, userId))) {
result = filterIfNotSystemUser(mComponentResolver.queryActivities(
intent, resolvedType, flags, setting.pkg.getActivities(), userId),
userId);
@@ -10485,6 +10522,19 @@ public class PackageManagerService extends IPackageManager.Stub
null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
+ // TODO(b/160928779) gate this behavior using ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES
+ if (pkg.getTargetSdkVersion() > 30) {
+ if (!pkg.getUsesNativeLibraries().isEmpty() && pkg.getTargetSdkVersion() > 30) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
+ null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ }
+ if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
+ null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ }
+ }
return usesLibraryInfos;
}
@@ -11080,6 +11130,21 @@ public class PackageManagerService extends IPackageManager.Stub
pkgSetting.forceQueryableOverride = true;
}
+ // If this is part of a standard install, set the initiating package name, else rely on
+ // previous device state.
+ if (reconciledPkg.installArgs != null) {
+ InstallSource installSource = reconciledPkg.installArgs.installSource;
+ if (installSource.initiatingPackageName != null) {
+ final PackageSetting ips = mSettings.mPackages.get(
+ installSource.initiatingPackageName);
+ if (ips != null) {
+ installSource = installSource.setInitiatingPackageSignatures(
+ ips.signatures);
+ }
+ }
+ pkgSetting.setInstallSource(installSource);
+ }
+
// TODO(toddke): Consider a method specifically for modifying the Package object
// post scan; or, moving this stuff out of the Package object since it has nothing
// to do with the package on disk.
@@ -11477,15 +11542,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting);
+ final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
if (needToDeriveAbi) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage);
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
- packageAbiHelper.derivePackageAbi(parsedPackage,
- pkgSetting.getPkgState().isUpdatedSystemApp(), cpuAbiOverride,
- extractNativeLibs);
+ packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
+ cpuAbiOverride);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -11495,15 +11559,15 @@ public class PackageManagerService extends IPackageManager.Stub
// structure. Try to detect abi based on directory structure.
String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- if (parsedPackage.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp() &&
- pkgRawPrimaryCpuAbi == null) {
+ if (parsedPackage.isSystem() && !isUpdatedSystemApp
+ && pkgRawPrimaryCpuAbi == null) {
final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
parsedPackage);
abis.applyTo(parsedPackage);
abis.applyTo(pkgSetting);
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting,
- sAppLib32InstallDir);
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
} else {
@@ -11514,8 +11578,8 @@ public class PackageManagerService extends IPackageManager.Stub
.setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.getNativeLibraryPaths(parsedPackage,
- pkgSetting, sAppLib32InstallDir);
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
if (DEBUG_ABI_SELECTION) {
@@ -11540,7 +11604,7 @@ public class PackageManagerService extends IPackageManager.Stub
// ABIs we determined during compilation, but the path will depend on the final
// package path (after the rename away from the stage path).
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting,
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
@@ -11730,6 +11794,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
} else {
parsedPackage
+ // Non system apps cannot mark any broadcast as protected
+ .clearProtectedBroadcasts()
// non system apps can't be flagged as core
.setCoreApp(false)
// clear flags not applicable to regular apps
@@ -11741,7 +11807,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
parsedPackage
- .clearProtectedBroadcasts()
.markNotActivitiesAsNotExportedIfSingleUser();
}
@@ -12161,15 +12226,16 @@ public class PackageManagerService extends IPackageManager.Stub
}
@GuardedBy("mLock")
- private boolean addBuiltInSharedLibraryLocked(String path, String name) {
- if (nonStaticSharedLibExistsLocked(name)) {
+ private boolean addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
+ if (nonStaticSharedLibExistsLocked(entry.name)) {
return false;
}
- SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, null, null, name,
- (long) SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
- new VersionedPackage(PLATFORM_PACKAGE_NAME, (long) 0),
- null, null);
+ SharedLibraryInfo libraryInfo = new SharedLibraryInfo(entry.filename, null, null,
+ entry.name, (long) SharedLibraryInfo.VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_BUILTIN,
+ new VersionedPackage(PLATFORM_PACKAGE_NAME, (long)0), null, null,
+ entry.isNative);
commitSharedLibraryInfoLocked(libraryInfo);
return true;
@@ -12805,11 +12871,11 @@ public class PackageManagerService extends IPackageManager.Stub
mHandler.sendMessage(msg);
}
- void installStage(List<ActiveInstallSession> children)
+ void installStage(ActiveInstallSession parent, List<ActiveInstallSession> children)
throws PackageManagerException {
final Message msg = mHandler.obtainMessage(INIT_COPY);
final MultiPackageInstallParams params =
- new MultiPackageInstallParams(UserHandle.ALL, children);
+ new MultiPackageInstallParams(UserHandle.ALL, parent, children);
params.setTraceMethod("installStageMultiPackage")
.setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -14301,17 +14367,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void processPendingInstall(final InstallArgs args, final int currentStatus) {
- if (args.mMultiPackageInstallParams != null) {
- args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
- } else {
- PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
- processInstallRequestsAsync(
- res.returnCode == PackageManager.INSTALL_SUCCEEDED,
- Collections.singletonList(new InstallRequest(args, res)));
- }
- }
-
// Queue up an async operation since the package installation may take a little while.
private void processInstallRequestsAsync(boolean success,
List<InstallRequest> installRequests) {
@@ -14478,13 +14533,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (ps != null && doSnapshotOrRestore) {
final String seInfo = AndroidPackageUtils.getSeInfo(res.pkg, ps);
- try {
- rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
- appId, ceDataInode, seInfo, token);
- } catch (RuntimeException re) {
- Log.e(TAG, "Error snapshotting/restoring user data: " + re);
- return false;
- }
+ rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
+ appId, ceDataInode, seInfo, token);
return true;
}
return false;
@@ -14697,13 +14747,17 @@ public class PackageManagerService extends IPackageManager.Stub
* committed together.
*/
class MultiPackageInstallParams extends HandlerParams {
+ private final IPackageInstallObserver2 mVerificationObserver;
@NonNull
private final ArrayList<InstallParams> mChildParams;
+ // TODO(samiul): mCurrentState will relocated to a install-specific class in future
@NonNull
private final Map<InstallArgs, Integer> mCurrentState;
+ private final Map<InstallParams, Integer> mVerificationState;
MultiPackageInstallParams(
@NonNull UserHandle user,
+ @NonNull ActiveInstallSession parent,
@NonNull List<ActiveInstallSession> activeInstallSessions)
throws PackageManagerException {
super(user);
@@ -14717,6 +14771,8 @@ public class PackageManagerService extends IPackageManager.Stub
this.mChildParams.add(childParams);
}
this.mCurrentState = new ArrayMap<>(mChildParams.size());
+ this.mVerificationState = new ArrayMap<>(mChildParams.size());
+ mVerificationObserver = parent.getVerificationObserver();
}
@Override
@@ -14733,6 +14789,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ // TODO(samiul): this method will relocated to a install-specific class in future
void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
mCurrentState.put(args, currentStatus);
if (mCurrentState.size() != mChildParams.size()) {
@@ -14756,6 +14813,28 @@ public class PackageManagerService extends IPackageManager.Stub
completeStatus == PackageManager.INSTALL_SUCCEEDED,
installRequests);
}
+
+ void trySendVerificationCompleteNotification(InstallParams child, int currentStatus) {
+ mVerificationState.put(child, currentStatus);
+ if (mVerificationState.size() != mChildParams.size()) {
+ return;
+ }
+ int completeStatus = PackageManager.INSTALL_SUCCEEDED;
+ for (Integer status : mVerificationState.values()) {
+ if (status == PackageManager.INSTALL_UNKNOWN) {
+ return;
+ } else if (status != PackageManager.INSTALL_SUCCEEDED) {
+ completeStatus = status;
+ break;
+ }
+ }
+ try {
+ mVerificationObserver.onPackageInstalled(null, completeStatus,
+ "Package Verification Result", new Bundle());
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
+ }
+ }
}
class InstallParams extends HandlerParams {
@@ -14763,7 +14842,8 @@ public class PackageManagerService extends IPackageManager.Stub
final OriginInfo origin;
final MoveInfo move;
- final IPackageInstallObserver2 observer;
+ final IPackageInstallObserver2 mInstallObserver;
+ private final IPackageInstallObserver2 mVerificationObserver;
int installFlags;
@NonNull final InstallSource installSource;
final String volumeUuid;
@@ -14785,7 +14865,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int mDataLoaderType;
final int mSessionId;
- InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
+ InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 installObserver,
int installFlags, InstallSource installSource, String volumeUuid,
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
@@ -14795,7 +14875,8 @@ public class PackageManagerService extends IPackageManager.Stub
super(user);
this.origin = origin;
this.move = move;
- this.observer = observer;
+ this.mInstallObserver = installObserver;
+ this.mVerificationObserver = null;
this.installFlags = installFlags;
this.installSource = Preconditions.checkNotNull(installSource);
this.volumeUuid = volumeUuid;
@@ -14833,7 +14914,8 @@ public class PackageManagerService extends IPackageManager.Stub
activeInstallSession.getInstallSource().installerPackageName,
activeInstallSession.getInstallerUid(),
sessionParams.installReason);
- observer = activeInstallSession.getObserver();
+ mInstallObserver = activeInstallSession.getInstallObserver();
+ mVerificationObserver = activeInstallSession.getVerificationObserver();
installFlags = sessionParams.installFlags;
installSource = activeInstallSession.getInstallSource();
volumeUuid = sessionParams.volumeUuid;
@@ -15163,8 +15245,13 @@ public class PackageManagerService extends IPackageManager.Stub
idleController.addPowerSaveTempWhitelistAppDirect(Process.myUid(),
idleDuration,
false, "integrity component");
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppWhitelistDuration(idleDuration);
+
mContext.sendOrderedBroadcastAsUser(integrityVerification, UserHandle.SYSTEM,
/* receiverPermission= */ null,
+ /* appOp= */ AppOpsManager.OP_NONE,
+ /* options= */ options.toBundle(),
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -15264,6 +15351,8 @@ public class PackageManagerService extends IPackageManager.Stub
DeviceIdleInternal idleController =
mInjector.getLocalDeviceIdleController();
final long idleDuration = getVerificationTimeout();
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppWhitelistDuration(idleDuration);
/*
* If any sufficient verifiers were listed in the package
@@ -15283,7 +15372,9 @@ public class PackageManagerService extends IPackageManager.Stub
final Intent sufficientIntent = new Intent(verification);
sufficientIntent.setComponent(verifierComponent);
- mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
+ mContext.sendBroadcastAsUser(sufficientIntent, verifierUser,
+ /* receiverPermission= */ null,
+ options.toBundle());
}
}
}
@@ -15302,6 +15393,8 @@ public class PackageManagerService extends IPackageManager.Stub
verifierUser.getIdentifier(), false, "package verifier");
mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ /* appOp= */ AppOpsManager.OP_NONE,
+ /* options= */ options.toBundle(),
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -15387,17 +15480,45 @@ public class PackageManagerService extends IPackageManager.Stub
if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
try {
- observer.onPackageInstalled(null, mRet, "Dry run", new Bundle());
+ mInstallObserver.onPackageInstalled(null, mRet, "Dry run", new Bundle());
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
return;
}
+ sendVerificationCompleteNotification();
+
+ // TODO(samiul): In future return once verification is complete
+ processPendingInstall();
+ }
+
+ private void processPendingInstall() {
InstallArgs args = createInstallArgs(this);
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
mRet = args.copyApk();
}
- processPendingInstall(args, mRet);
+ if (mParentInstallParams != null) {
+ mParentInstallParams.tryProcessInstallRequest(args, mRet);
+ } else {
+ PackageInstalledInfo res = createPackageInstalledInfo(mRet);
+ processInstallRequestsAsync(
+ res.returnCode == PackageManager.INSTALL_SUCCEEDED,
+ Collections.singletonList(new InstallRequest(args, res)));
+
+ }
+ }
+
+ private void sendVerificationCompleteNotification() {
+ if (mParentInstallParams != null) {
+ mParentInstallParams.trySendVerificationCompleteNotification(this, mRet);
+ } else {
+ try {
+ mVerificationObserver.onPackageInstalled(null, mRet,
+ "Package Verification Result", new Bundle());
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
+ }
+ }
}
}
@@ -15440,7 +15561,6 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageParser.SigningDetails signingDetails;
final int installReason;
final boolean forceQueryableOverride;
- @Nullable final MultiPackageInstallParams mMultiPackageInstallParams;
final int mDataLoaderType;
// The list of instruction sets supported by this app. This is currently
@@ -15455,8 +15575,7 @@ public class PackageManagerService extends IPackageManager.Stub
List<String> whitelistedRestrictedPermissions,
int autoRevokePermissionsMode,
String traceMethod, int traceCookie, SigningDetails signingDetails,
- int installReason, boolean forceQueryableOverride,
- MultiPackageInstallParams multiPackageInstallParams, int dataLoaderType) {
+ int installReason, boolean forceQueryableOverride, int dataLoaderType) {
this.origin = origin;
this.move = move;
this.installFlags = installFlags;
@@ -15474,20 +15593,18 @@ public class PackageManagerService extends IPackageManager.Stub
this.signingDetails = signingDetails;
this.installReason = installReason;
this.forceQueryableOverride = forceQueryableOverride;
- this.mMultiPackageInstallParams = multiPackageInstallParams;
this.mDataLoaderType = dataLoaderType;
}
/** New install */
InstallArgs(InstallParams params) {
- this(params.origin, params.move, params.observer, params.installFlags,
+ this(params.origin, params.move, params.mInstallObserver, params.installFlags,
params.installSource, params.volumeUuid,
params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
params.autoRevokePermissionsMode,
params.traceMethod, params.traceCookie, params.signingDetails,
- params.installReason, params.forceQueryableOverride,
- params.mParentInstallParams, params.mDataLoaderType);
+ params.installReason, params.forceQueryableOverride, params.mDataLoaderType);
}
abstract int copyApk();
@@ -15578,7 +15695,7 @@ public class PackageManagerService extends IPackageManager.Stub
super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
PackageParser.SigningDetails.UNKNOWN,
- PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */,
+ PackageManager.INSTALL_REASON_UNKNOWN, false,
DataLoaderType.NONE);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
@@ -16044,16 +16161,7 @@ public class PackageManagerService extends IPackageManager.Stub
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
- if (installSource.initiatingPackageName != null) {
- final PackageSetting ips = mSettings.mPackages.get(
- installSource.initiatingPackageName);
- if (ips != null) {
- installSource = installSource.setInitiatingPackageSignatures(
- ips.signatures);
- }
- }
- ps.setInstallSource(installSource);
- mSettings.addInstallerPackageNames(installSource);
+ mSettings.addInstallerPackageNames(ps.installSource);
// When replacing an existing package, preserve the original install reason for all
// users that had the package installed before. Similarly for uninstall reasons.
@@ -17122,7 +17230,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Sanity check
if (instantApp && onExternal) {
Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID);
}
// Retrieve PackageSettings and parse package
@@ -17147,13 +17255,13 @@ public class PackageManagerService extends IPackageManager.Stub
if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) {
Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ " does not target at least O");
- throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
"Instant app package must target at least O");
}
if (parsedPackage.getSharedUserId() != null) {
Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ " may not declare sharedUserId.");
- throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
"Instant app package may not declare a sharedUserId");
}
}
@@ -17193,7 +17301,7 @@ public class PackageManagerService extends IPackageManager.Stub
< SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ " is not signed with at least APK Signature Scheme v2");
- throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
"Instant app package must be signed with APK Signature Scheme v2 or greater");
}
@@ -17399,7 +17507,7 @@ public class PackageManagerService extends IPackageManager.Stub
"Cannot install updates to system apps on sdcard");
} else if (instantApp) {
// Abort update; system app can't be replaced with an instant app
- throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
"Cannot update a system app with an instant app");
}
}
@@ -17427,7 +17535,6 @@ public class PackageManagerService extends IPackageManager.Stub
scanFlags |= SCAN_NO_DEX;
try {
- final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage);
PackageSetting pkgSetting;
synchronized (mLock) {
pkgSetting = mSettings.getPackageLPr(pkgName);
@@ -17442,7 +17549,7 @@ public class PackageManagerService extends IPackageManager.Stub
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- abiOverride, extractNativeLibs);
+ abiOverride);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
@@ -17591,7 +17698,7 @@ public class PackageManagerService extends IPackageManager.Stub
"Can't replace full app with instant app: " + pkgName11
+ " for user: " + currentUser);
throw new PrepareFailure(
- PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+ PackageManager.INSTALL_FAILED_SESSION_INVALID);
}
}
} else if (!ps.getInstantApp(args.user.getIdentifier())) {
@@ -17599,7 +17706,7 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
+ " for user: " + args.user.getIdentifier());
throw new PrepareFailure(
- PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+ PackageManager.INSTALL_FAILED_SESSION_INVALID);
}
}
}
@@ -19116,9 +19223,7 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean systemApp = isSystemApp(ps);
final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
- if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, userId)) {
- unsuspendForSuspendingPackage(packageName, userId);
- }
+
if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)
&& userId != UserHandle.USER_ALL) {
// The caller is asking that the package only be deleted for a single
@@ -19176,6 +19281,20 @@ public class PackageManagerService extends IPackageManager.Stub
outInfo, writeSettings);
}
+ // If the package removed had SUSPEND_APPS, unset any restrictions that might have been in
+ // place for all affected users.
+ int[] affectedUserIds = (outInfo != null) ? outInfo.removedUsers : null;
+ if (affectedUserIds == null) {
+ affectedUserIds = resolveUserIds(userId);
+ }
+ for (final int affectedUserId : affectedUserIds) {
+ if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS,
+ affectedUserId)) {
+ unsuspendForSuspendingPackage(packageName, affectedUserId);
+ removeAllDistractingPackageRestrictions(affectedUserId);
+ }
+ }
+
// Take a note whether we deleted the package for all users
if (outInfo != null) {
outInfo.removedForAllUsers = mPackages.get(ps.name) == null;
@@ -19541,14 +19660,14 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void addPreferredActivity(IntentFilter filter, int match,
- ComponentName[] set, ComponentName activity, int userId) {
+ ComponentName[] set, ComponentName activity, int userId, boolean removeExisting) {
addPreferredActivityInternal(filter, match, set, activity, true, userId,
- "Adding preferred");
+ "Adding preferred", removeExisting);
}
private void addPreferredActivityInternal(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, boolean always, int userId,
- String opname) {
+ String opname, boolean removeExisting) {
// writer
int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
@@ -19576,6 +19695,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
synchronized (mLock) {
final PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId);
+ final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
+ if (removeExisting && existing != null) {
+ removeFiltersLocked(pir, filter, existing);
+ }
pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
scheduleWritePackageRestrictionsLocked(userId);
}
@@ -19675,24 +19798,28 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
if (existing != null) {
- if (DEBUG_PREFERRED) {
- Slog.i(TAG, existing.size() + " existing preferred matches for:");
- filter.dump(new LogPrinter(Log.INFO, TAG), " ");
- }
- for (int i = existing.size() - 1; i >= 0; --i) {
- final PreferredActivity pa = existing.get(i);
- if (DEBUG_PREFERRED) {
- Slog.i(TAG, "Removing existing preferred activity "
- + pa.mPref.mComponent + ":");
- pa.dump(new LogPrinter(Log.INFO, TAG), " ");
- }
- pir.removeFilter(pa);
- }
+ removeFiltersLocked(pir, filter, existing);
}
}
}
addPreferredActivityInternal(filter, match, set, activity, true, userId,
- "Replacing preferred");
+ "Replacing preferred", false);
+ }
+
+ private void removeFiltersLocked(@NonNull PreferredIntentResolver pir,
+ @NonNull IntentFilter filter, @NonNull List<PreferredActivity> existing) {
+ if (DEBUG_PREFERRED) {
+ Slog.i(TAG, existing.size() + " preferred matches for:");
+ filter.dump(new LogPrinter(Log.INFO, TAG), " ");
+ }
+ for (int i = existing.size() - 1; i >= 0; --i) {
+ final PreferredActivity pa = existing.get(i);
+ if (DEBUG_PREFERRED) {
+ Slog.i(TAG, "Removing preferred activity " + pa.mPref.mComponent + ":");
+ pa.dump(new LogPrinter(Log.INFO, TAG), " ");
+ }
+ pir.removeFilter(pa);
+ }
}
@Override
@@ -21823,7 +21950,11 @@ public class PackageManagerService extends IPackageManager.Stub
pw.print(" -> ");
}
if (libraryInfo.getPath() != null) {
- pw.print(" (jar) ");
+ if (libraryInfo.isNative()) {
+ pw.print(" (so) ");
+ } else {
+ pw.print(" (jar) ");
+ }
pw.print(libraryInfo.getPath());
} else {
pw.print(" (apk) ");
@@ -25477,7 +25608,8 @@ public class PackageManagerService extends IPackageManager.Stub
private void applyMimeGroupChanges(String packageName, String mimeGroup) {
if (mComponentResolver.updateMimeGroup(packageName, mimeGroup)) {
- clearPackagePreferredActivities(packageName, UserHandle.USER_ALL);
+ Binder.withCleanCallingIdentity(() ->
+ clearPackagePreferredActivities(packageName, UserHandle.USER_ALL));
}
mPmInternal.writeSettings(false);
@@ -25501,7 +25633,10 @@ public class PackageManagerService extends IPackageManager.Stub
static class ActiveInstallSession {
private final String mPackageName;
private final File mStagedDir;
- private final IPackageInstallObserver2 mObserver;
+ private final IPackageInstallObserver2 mInstallObserver;
+ // TODO(samiul): We are temporarily assigning two observer to ActiveInstallSession. One for
+ // installation and one for verification. This will be fixed within next few CLs.
+ private final IPackageInstallObserver2 mVerificationObserver;
private final int mSessionId;
private final PackageInstaller.SessionParams mSessionParams;
private final int mInstallerUid;
@@ -25509,12 +25644,15 @@ public class PackageManagerService extends IPackageManager.Stub
private final UserHandle mUser;
private final SigningDetails mSigningDetails;
- ActiveInstallSession(String packageName, File stagedDir, IPackageInstallObserver2 observer,
+ ActiveInstallSession(String packageName, File stagedDir,
+ IPackageInstallObserver2 installObserver,
+ IPackageInstallObserver2 verificationObserver,
int sessionId, PackageInstaller.SessionParams sessionParams, int installerUid,
InstallSource installSource, UserHandle user, SigningDetails signingDetails) {
mPackageName = packageName;
mStagedDir = stagedDir;
- mObserver = observer;
+ mInstallObserver = installObserver;
+ mVerificationObserver = verificationObserver;
mSessionId = sessionId;
mSessionParams = sessionParams;
mInstallerUid = installerUid;
@@ -25531,8 +25669,12 @@ public class PackageManagerService extends IPackageManager.Stub
return mStagedDir;
}
- public IPackageInstallObserver2 getObserver() {
- return mObserver;
+ public IPackageInstallObserver2 getInstallObserver() {
+ return mInstallObserver;
+ }
+
+ public IPackageInstallObserver2 getVerificationObserver() {
+ return mVerificationObserver;
}
public int getSessionId() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d06a231d1da6..668f375e2e9b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -124,7 +124,6 @@ import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -297,7 +296,8 @@ class PackageManagerShellCommand extends ShellCommand {
case "get-stagedsessions":
return runListStagedSessions();
case "uninstall-system-updates":
- return uninstallSystemUpdates();
+ String packageName = getNextArg();
+ return uninstallSystemUpdates(packageName);
case "rollback-app":
return runRollbackApp();
case "get-moduleinfo":
@@ -415,15 +415,22 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
- private int uninstallSystemUpdates() {
+ private int uninstallSystemUpdates(String packageName) {
final PrintWriter pw = getOutPrintWriter();
- List<String> failedUninstalls = new LinkedList<>();
+ boolean failedUninstalls = false;
try {
- final ParceledListSlice<ApplicationInfo> packages =
- mInterface.getInstalledApplications(
- PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
final IPackageInstaller installer = mInterface.getPackageInstaller();
- List<ApplicationInfo> list = packages.getList();
+ final List<ApplicationInfo> list;
+ if (packageName == null) {
+ final ParceledListSlice<ApplicationInfo> packages =
+ mInterface.getInstalledApplications(
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ list = packages.getList();
+ } else {
+ list = new ArrayList<>(1);
+ list.add(mInterface.getApplicationInfo(packageName,
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM));
+ }
for (ApplicationInfo info : list) {
if (info.isUpdatedSystemApp()) {
pw.println("Uninstalling updates to " + info.packageName + "...");
@@ -436,7 +443,8 @@ class PackageManagerShellCommand extends ShellCommand {
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status != PackageInstaller.STATUS_SUCCESS) {
- failedUninstalls.add(info.packageName);
+ failedUninstalls = true;
+ pw.println("Couldn't uninstall package: " + info.packageName);
}
}
}
@@ -446,10 +454,7 @@ class PackageManagerShellCommand extends ShellCommand {
+ e.getMessage() + "]");
return 0;
}
- if (!failedUninstalls.isEmpty()) {
- pw.println("Failure [Couldn't uninstall packages: "
- + TextUtils.join(", ", failedUninstalls)
- + "]");
+ if (failedUninstalls) {
return 0;
}
pw.println("Success");
@@ -1371,7 +1376,7 @@ class PackageManagerShellCommand extends ShellCommand {
long timeoutMs = -1;
while ((opt = getNextOption()) != null) {
switch (opt) {
- case "--wait":
+ case "--wait-for-staged-ready":
waitForStagedSessionReady = true;
// If there is only one remaining argument, then it represents the sessionId, we
// shouldn't try to parse it as timeoutMs.
@@ -2280,7 +2285,7 @@ class PackageManagerShellCommand extends ShellCommand {
if (grant) {
mPermissionManager.grantRuntimePermission(pkg, perm, translatedUserId);
} else {
- mPermissionManager.revokeRuntimePermission(pkg, perm, translatedUserId);
+ mPermissionManager.revokeRuntimePermission(pkg, perm, translatedUserId, null);
}
return 0;
}
@@ -2859,7 +2864,7 @@ class PackageManagerShellCommand extends ShellCommand {
}
sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
break;
- case "--wait":
+ case "--wait-for-staged-ready":
params.mWaitForStagedSessionReady = true;
try {
params.timeoutMs = Long.parseLong(peekNextArg());
@@ -3592,7 +3597,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
pw.println(" [--enable-rollback]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
- pw.println(" [--apex] [--wait TIMEOUT]");
+ pw.println(" [--apex] [--wait-for-staged-ready TIMEOUT]");
pw.println(" [PATH [SPLIT...]|-]");
pw.println(" Install an application. Must provide the apk data to install, either as");
pw.println(" file path(s) or '-' to read from stdin. Options are:");
@@ -3620,8 +3625,8 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" 3=device setup, 4=user request");
pw.println(" --force-uuid: force install on to disk volume with given UUID");
pw.println(" --apex: install an .apex file, not an .apk");
- pw.println(" --wait: when performing staged install, wait TIMEOUT milliseconds");
- pw.println(" for pre-reboot verification to complete. If TIMEOUT is not");
+ pw.println(" --wait-for-staged-ready: when performing staged install, wait TIMEOUT");
+ pw.println(" ms for pre-reboot verification to complete. If TIMEOUT is not");
pw.println(" specified it will wait for " + DEFAULT_WAIT_MS + " milliseconds.");
pw.println("");
pw.println(" install-existing [--user USER_ID|all|current]");
@@ -3824,9 +3829,10 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" get-harmful-app-warning [--user <USER_ID>] <PACKAGE>");
pw.println(" Return the harmful app warning message for the given app, if present");
pw.println();
- pw.println(" uninstall-system-updates");
- pw.println(" Remove updates to all system applications and fall back to their /system " +
- "version.");
+ pw.println(" uninstall-system-updates [<PACKAGE>]");
+ pw.println(" Removes updates to the given system application and falls back to its");
+ pw.println(" /system version. Does nothing if the given package is not a system app.");
+ pw.println(" If no package is specified, removes updates to all system applications.");
pw.println("");
pw.println(" get-moduleinfo [--all | --installed] [module-name]");
pw.println(" Displays module info. If module-name is specified only that info is shown");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ad61d242ed34..7106499f9b56 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1407,7 +1407,7 @@ public final class Settings {
if (pa.mPref.getParseError() == null) {
final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId);
ArrayList<PreferredActivity> pal = resolver.findFilters(pa);
- if (pal == null || pal.size() == 0) {
+ if (pal == null || pal.size() == 0 || pa.mPref.mAlways) {
resolver.addFilter(pa);
}
} else {
@@ -4784,6 +4784,23 @@ public final class Settings {
}
}
+ List<String> usesNativeLibraries = pkg.getUsesNativeLibraries();
+ if (usesNativeLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesNativeLibraries:");
+ for (int i=0; i< usesNativeLibraries.size(); i++) {
+ pw.print(prefix); pw.print(" "); pw.println(usesNativeLibraries.get(i));
+ }
+ }
+
+ List<String> usesOptionalNativeLibraries = pkg.getUsesOptionalNativeLibraries();
+ if (usesOptionalNativeLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesOptionalNativeLibraries:");
+ for (int i=0; i< usesOptionalNativeLibraries.size(); i++) {
+ pw.print(prefix); pw.print(" ");
+ pw.println(usesOptionalNativeLibraries.get(i));
+ }
+ }
+
List<String> usesLibraryFiles = ps.getPkgState().getUsesLibraryFiles();
if (usesLibraryFiles.size() > 0) {
pw.print(prefix); pw.println(" usesLibraryFiles:");
@@ -4935,9 +4952,9 @@ public final class Settings {
String[] overlayPaths = ps.getOverlayPaths(user.id);
if (overlayPaths != null && overlayPaths.length > 0) {
- pw.print(prefix); pw.println(" overlay paths:");
+ pw.print(prefix); pw.println(" overlay paths:");
for (String path : overlayPaths) {
- pw.print(prefix); pw.print(" "); pw.println(path);
+ pw.print(prefix); pw.print(" "); pw.println(path);
}
}
@@ -4949,10 +4966,10 @@ public final class Settings {
if (libOverlayPaths.getValue() == null) {
continue;
}
- pw.print(prefix); pw.print(" ");
+ pw.print(prefix); pw.print(" ");
pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:");
for (String path : libOverlayPaths.getValue()) {
- pw.print(prefix); pw.print(" "); pw.println(path);
+ pw.print(prefix); pw.print(" "); pw.println(path);
}
}
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index c99a56fc6f8f..616e5d13f990 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -38,7 +38,6 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
-import android.content.pm.ParceledListSlice;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -164,6 +163,7 @@ public class StagingManager {
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_BOOT_COMPLETED && sStagingManager != null) {
sStagingManager.markStagedSessionsAsSuccessful();
+ sStagingManager.markBootCompleted();
}
}
}
@@ -179,18 +179,8 @@ public class StagingManager {
}
}
- ParceledListSlice<PackageInstaller.SessionInfo> getSessions(int callingUid) {
- final List<PackageInstaller.SessionInfo> result = new ArrayList<>();
- synchronized (mStagedSessions) {
- for (int i = 0; i < mStagedSessions.size(); i++) {
- final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
- if (stagedSession.isDestroyed()) {
- continue;
- }
- result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid));
- }
- }
- return new ParceledListSlice<>(result);
+ private void markBootCompleted() {
+ mApexManager.markBootCompleted();
}
/**
@@ -522,13 +512,9 @@ public class StagingManager {
private void snapshotAndRestoreApexUserData(
String packageName, int[] allUsers, RollbackManagerInternal rm) {
- try {
- // appId, ceDataInode, and seInfo are not needed for APEXes
- rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(allUsers), 0, 0,
- null, 0 /*token*/);
- } catch (RuntimeException re) {
- Slog.e(TAG, "Error snapshotting/restoring user data: " + re);
- }
+ // appId, ceDataInode, and seInfo are not needed for APEXes
+ rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(allUsers), 0, 0,
+ null, 0 /*token*/);
}
private void snapshotAndRestoreApkInApexUserData(
@@ -552,12 +538,8 @@ public class StagingManager {
final int[] installedUsers = ps.queryInstalledUsers(allUsers, true);
final String seInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
- try {
- rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
- appId, ceDataInode, seInfo, 0 /*token*/);
- } catch (RuntimeException re) {
- Slog.e(TAG, "Error snapshotting/restoring user data: " + re);
- }
+ rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
+ appId, ceDataInode, seInfo, 0 /*token*/);
}
}
@@ -756,7 +738,6 @@ public class StagingManager {
PackageInstaller.SessionParams params = originalSession.params.copy();
params.isStaged = false;
params.installFlags |= PackageManager.INSTALL_STAGED;
- // TODO(b/129744602): use the userid from the original session.
if (preReboot) {
params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
params.installFlags |= PackageManager.INSTALL_DRY_RUN;
@@ -766,7 +747,7 @@ public class StagingManager {
try {
int apkSessionId = mPi.createSession(
params, originalSession.getInstallerPackageName(),
- originalSession.getInstallerAttributionTag(), 0 /* UserHandle.SYSTEM */);
+ originalSession.getInstallerAttributionTag(), originalSession.userId);
PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
apkSession.open();
for (int i = 0, size = apkFilePaths.size(); i < size; i++) {
@@ -824,10 +805,9 @@ public class StagingManager {
if (preReboot) {
params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
}
- // TODO(b/129744602): use the userid from the original session.
final int apkParentSessionId = mPi.createSession(
params, session.getInstallerPackageName(), session.getInstallerAttributionTag(),
- 0 /* UserHandle.SYSTEM */);
+ session.userId);
final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
try {
apkParentSession.open();
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 154028251b18..dbe96e63d978 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -71,7 +71,12 @@
]
},
{
- "name": "PackageManagerServiceHostTests"
+ "name": "PackageManagerServiceHostTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ }
+ ]
}
],
"postsubmit": [
@@ -88,6 +93,9 @@
// "name": "CtsAppSecurityHostTestCases"
// },
{
+ "name": "PackageManagerServiceHostTests"
+ },
+ {
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c37f09a96f95..a44d1797b82b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3306,27 +3306,6 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- private long logUserCreateJourneyBegin(@UserIdInt int userId, String userType,
- @UserInfoFlag int flags) {
- final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
- // log the journey atom with the user metadata
- FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
- FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE,
- /* origin_user= */ -1, userId, UserManager.getUserTypeForStatsd(userType), flags);
- // log the event atom to indicate the event start
- FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
- FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
- FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN);
- return sessionId;
- }
-
- private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) {
- FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
- FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
- finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH
- : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
- }
-
private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
@NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
boolean preCreate, @Nullable String[] disallowedPackages,
@@ -3433,6 +3412,7 @@ public class UserManagerService extends IUserManager.Stub {
}
userId = getNextAvailableId();
+ Slog.i(LOG_TAG, "Creating user " + userId + " of type " + userType);
Environment.getUserSystemDirectory(userId).mkdirs();
synchronized (mUsersLock) {
@@ -3684,6 +3664,27 @@ public class UserManagerService extends IUserManager.Stub {
&& !userTypeDetails.getName().equals(UserManager.USER_TYPE_FULL_RESTRICTED);
}
+ private long logUserCreateJourneyBegin(@UserIdInt int userId, String userType,
+ @UserInfoFlag int flags) {
+ final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
+ // log the journey atom with the user metadata
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE,
+ /* origin_user= */ -1, userId, UserManager.getUserTypeForStatsd(userType), flags);
+ // log the event atom to indicate the event start
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN);
+ return sessionId;
+ }
+
+ private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) {
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
+ finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH
+ : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
+ }
+
@VisibleForTesting
UserData putUserInfo(UserInfo userInfo) {
final UserData userData = new UserData();
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index c9e0bb467ce4..39784cf32cea 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -252,6 +252,19 @@ public interface AndroidPackage extends PkgAppInfo, PkgPackageInfo, ParsingPacka
@NonNull
List<String> getUsesOptionalLibraries();
+ /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+ @NonNull
+ List<String> getUsesNativeLibraries();
+
+ /**
+ * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+ * expected to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesNativeLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalNativeLibraries();
+
/**
* TODO(b/135203078): Move static library stuff to an inner data class
* @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 1b11e2d0860d..bc7554c54eb0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -29,6 +29,9 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAU
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
@@ -327,13 +330,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mPackageManagerInt.writeSettings(true);
}
@Override
- public void onPermissionRevoked(int uid, int userId) {
+ public void onPermissionRevoked(int uid, int userId, String reason) {
mOnPermissionChangeListeners.onPermissionsChanged(uid);
// Critical; after this call the application should never have the permission
mPackageManagerInt.writeSettings(false);
final int appId = UserHandle.getAppId(uid);
- mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
+ if (reason == null) {
+ mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
+ } else {
+ mHandler.post(() -> killUid(appId, userId, reason));
+ }
}
@Override
public void onInstallPermissionRevoked() {
@@ -470,7 +477,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
IActivityManager am = ActivityManager.getService();
if (am != null) {
try {
- am.killUid(appId, userId, reason);
+ am.killUidForPermissionChange(appId, userId, reason);
} catch (RemoteException e) {
/* ignore - same process */
}
@@ -754,9 +761,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
- flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
- flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
- flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ flagValues &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ flagValues &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
flagValues &= ~PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
}
@@ -782,6 +789,31 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final PermissionsState permissionsState = ps.getPermissionsState();
final boolean hadState =
permissionsState.getRuntimePermissionState(permName, userId) != null;
+ if (!hadState) {
+ boolean isRequested = false;
+ // Fast path, the current package has requested the permission.
+ if (pkg.getRequestedPermissions().contains(permName)) {
+ isRequested = true;
+ }
+ if (!isRequested) {
+ // Slow path, go through all shared user packages.
+ String[] sharedUserPackageNames =
+ mPackageManagerInt.getSharedUserPackagesForPackage(packageName, userId);
+ for (String sharedUserPackageName : sharedUserPackageNames) {
+ AndroidPackage sharedUserPkg = mPackageManagerInt.getPackage(
+ sharedUserPackageName);
+ if (sharedUserPkg != null
+ && sharedUserPkg.getRequestedPermissions().contains(permName)) {
+ isRequested = true;
+ break;
+ }
+ }
+ }
+ if (!isRequested) {
+ Log.e(TAG, "Permission " + permName + " isn't requested by package " + packageName);
+ return;
+ }
+ }
final boolean permissionUpdated =
permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues);
if (permissionUpdated && bp.isRuntime()) {
@@ -1112,13 +1144,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
int queryFlags = 0;
if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) {
- queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
}
if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
- queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
}
if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
- queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
}
ArrayList<String> whitelistedPermissions = null;
@@ -1280,8 +1312,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final long identity = Binder.clearCallingIdentity();
try {
- setWhitelistedRestrictedPermissionsForUser(
- pkg, userId, permissions, Process.myUid(), flags, mDefaultPermissionCallback);
+ setWhitelistedRestrictedPermissionsForUsers(pkg, new int[]{ userId }, permissions,
+ Process.myUid(), flags, mDefaultPermissionCallback);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1526,19 +1558,21 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId) {
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
checkUidPermission(ADJUST_RUNTIME_PERMISSIONS_POLICY, callingUid)
== PackageManager.PERMISSION_GRANTED;
revokeRuntimePermissionInternal(permName, packageName, overridePolicy, callingUid, userId,
- mDefaultPermissionCallback);
+ reason, mDefaultPermissionCallback);
}
// TODO swap permission name and package name
private void revokeRuntimePermissionInternal(String permName, String packageName,
- boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) {
+ boolean overridePolicy, int callingUid, final int userId, String reason,
+ PermissionCallback callback) {
if (ApplicationPackageManager.DEBUG_TRACE_PERMISSION_UPDATES
&& ApplicationPackageManager.shouldTraceGrant(packageName, permName, userId)) {
Log.i(TAG, "System is revoking " + packageName + " "
@@ -1629,7 +1663,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (callback != null) {
callback.onPermissionRevoked(UserHandle.getUid(userId,
- UserHandle.getAppId(pkg.getUid())), userId);
+ UserHandle.getAppId(pkg.getUid())), userId, reason);
}
if (bp.isRuntime()) {
@@ -1703,7 +1737,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mDefaultPermissionCallback.onInstallPermissionGranted();
}
- public void onPermissionRevoked(int uid, int userId) {
+ public void onPermissionRevoked(int uid, int userId, String reason) {
revokedPermissions.add(IntPair.of(uid, userId));
syncUpdatedUsers.add(userId);
@@ -1816,7 +1850,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
} else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
// Otherwise, reset the permission.
revokeRuntimePermissionInternal(permName, packageName, false, Process.SYSTEM_UID,
- userId, delayingPermCallback);
+ userId, null, delayingPermCallback);
}
}
@@ -2297,7 +2331,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
try {
revokeRuntimePermissionInternal(permissionName, packageName,
- false, callingUid, userId, permissionCallback);
+ false, callingUid, userId, null, permissionCallback);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Could not revoke " + permissionName + " from "
+ packageName, e);
@@ -2517,8 +2551,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (permission.isHardOrSoftRestricted()
|| permission.isImmutablyRestricted()) {
permissionsState.updatePermissionFlags(permission, userId,
- PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
- PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
+ FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
+ FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
}
if (targetSdkVersion < Build.VERSION_CODES.M) {
permissionsState.updatePermissionFlags(permission, userId,
@@ -3756,8 +3790,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- private void setWhitelistedRestrictedPermissionsForUser(@NonNull AndroidPackage pkg,
- @UserIdInt int userId, @Nullable List<String> permissions, int callingUid,
+ private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg,
+ @UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid,
@PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) {
final PermissionsState permissionsState =
PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg);
@@ -3765,95 +3799,102 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return;
}
- ArraySet<String> oldGrantedRestrictedPermissions = null;
+ SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>();
boolean updatePermissions = false;
-
final int permissionCount = pkg.getRequestedPermissions().size();
- for (int i = 0; i < permissionCount; i++) {
- final String permissionName = pkg.getRequestedPermissions().get(i);
- final BasePermission bp = mSettings.getPermissionLocked(permissionName);
+ for (int i = 0; i < userIds.length; i++) {
+ int userId = userIds[i];
+ for (int j = 0; j < permissionCount; j++) {
+ final String permissionName = pkg.getRequestedPermissions().get(j);
- if (bp == null || !bp.isHardOrSoftRestricted()) {
- continue;
- }
+ final BasePermission bp = mSettings.getPermissionLocked(permissionName);
- if (permissionsState.hasPermission(permissionName, userId)) {
- if (oldGrantedRestrictedPermissions == null) {
- oldGrantedRestrictedPermissions = new ArraySet<>();
+ if (bp == null || !bp.isHardOrSoftRestricted()) {
+ continue;
}
- oldGrantedRestrictedPermissions.add(permissionName);
- }
-
- final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId);
-
- int newFlags = oldFlags;
- int mask = 0;
- int whitelistFlagsCopy = whitelistFlags;
- while (whitelistFlagsCopy != 0) {
- final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy);
- whitelistFlagsCopy &= ~flag;
- switch (flag) {
- case FLAG_PERMISSION_WHITELIST_SYSTEM: {
- mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
- if (permissions != null && permissions.contains(permissionName)) {
- newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
- } else {
- newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+
+ if (permissionsState.hasPermission(permissionName, userId)) {
+ if (oldGrantedRestrictedPermissions.get(userId) == null) {
+ oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
+ }
+ oldGrantedRestrictedPermissions.get(userId).add(permissionName);
+ }
+
+ final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId);
+
+ int newFlags = oldFlags;
+ int mask = 0;
+ int whitelistFlagsCopy = whitelistFlags;
+ while (whitelistFlagsCopy != 0) {
+ final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy);
+ whitelistFlagsCopy &= ~flag;
+ switch (flag) {
+ case FLAG_PERMISSION_WHITELIST_SYSTEM: {
+ mask |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ if (permissions != null && permissions.contains(permissionName)) {
+ newFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ } else {
+ newFlags &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ }
}
- } break;
- case FLAG_PERMISSION_WHITELIST_UPGRADE: {
- mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
- if (permissions != null && permissions.contains(permissionName)) {
- newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
- } else {
- newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ break;
+ case FLAG_PERMISSION_WHITELIST_UPGRADE: {
+ mask |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ if (permissions != null && permissions.contains(permissionName)) {
+ newFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ } else {
+ newFlags &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ }
}
- } break;
- case FLAG_PERMISSION_WHITELIST_INSTALLER: {
- mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
- if (permissions != null && permissions.contains(permissionName)) {
- newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
- } else {
- newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ break;
+ case FLAG_PERMISSION_WHITELIST_INSTALLER: {
+ mask |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ if (permissions != null && permissions.contains(permissionName)) {
+ newFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ } else {
+ newFlags &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ }
}
- } break;
+ break;
+ }
}
- }
-
- if (oldFlags == newFlags) {
- continue;
- }
- updatePermissions = true;
+ if (oldFlags == newFlags) {
+ continue;
+ }
- final boolean wasWhitelisted = (oldFlags
- & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
- final boolean isWhitelisted = (newFlags
- & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
+ updatePermissions = true;
+
+ final boolean wasWhitelisted = (oldFlags
+ & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
+ final boolean isWhitelisted = (newFlags
+ & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
+
+ // If the permission is policy fixed as granted but it is no longer
+ // on any of the whitelists we need to clear the policy fixed flag
+ // as whitelisting trumps policy i.e. policy cannot grant a non
+ // grantable permission.
+ if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ final boolean isGranted = permissionsState.hasPermission(permissionName,
+ userId);
+ if (!isWhitelisted && isGranted) {
+ mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ }
+ }
- // If the permission is policy fixed as granted but it is no longer
- // on any of the whitelists we need to clear the policy fixed flag
- // as whitelisting trumps policy i.e. policy cannot grant a non
- // grantable permission.
- if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- final boolean isGranted = permissionsState.hasPermission(permissionName, userId);
- if (!isWhitelisted && isGranted) {
- mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
- newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ // If we are whitelisting an app that does not support runtime permissions
+ // we need to make sure it goes through the permission review UI at launch.
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
+ && !wasWhitelisted && isWhitelisted) {
+ mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
}
- }
- // If we are whitelisting an app that does not support runtime permissions
- // we need to make sure it goes through the permission review UI at launch.
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
- && !wasWhitelisted && isWhitelisted) {
- mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
- newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags,
+ callingUid, userId, false, null /*callback*/);
}
-
- updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags,
- callingUid, userId, false, null /*callback*/);
}
if (updatePermissions) {
@@ -3861,15 +3902,22 @@ public class PermissionManagerService extends IPermissionManager.Stub {
restorePermissionState(pkg, false, pkg.getPackageName(), callback);
// If this resulted in losing a permission we need to kill the app.
- if (oldGrantedRestrictedPermissions != null) {
- final int oldGrantedCount = oldGrantedRestrictedPermissions.size();
- for (int i = 0; i < oldGrantedCount; i++) {
- final String permission = oldGrantedRestrictedPermissions.valueAt(i);
+ for (int i = 0; i < userIds.length; i++) {
+ int userId = userIds[i];
+ ArraySet<String> oldPermsForUser = oldGrantedRestrictedPermissions.get(userId);
+ if (oldPermsForUser == null) {
+ continue;
+ }
+
+ final int oldGrantedCount = oldPermsForUser.size();
+ for (int j = 0; j < oldGrantedCount; j++) {
+ final String permission = oldPermsForUser.valueAt(j);
// Sometimes we create a new permission state instance during update.
final PermissionsState newPermissionsState =
- PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg);
+ PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt,
+ pkg);
if (!newPermissionsState.hasPermission(permission, userId)) {
- callback.onPermissionRevoked(pkg.getUid(), userId);
+ callback.onPermissionRevoked(pkg.getUid(), userId, null);
break;
}
}
@@ -4176,6 +4224,20 @@ public class PermissionManagerService extends IPermissionManager.Stub {
revokePermissionFromPackageForUser(p.getPackageName(),
bp.getName(), true, userId, callback));
}
+ } else {
+ mPackageManagerInt.forEachPackage(p -> {
+ PackageSetting ps = mPackageManagerInt.getPackageSetting(
+ p.getPackageName());
+ if (ps == null) {
+ return;
+ }
+ PermissionsState permissionsState = ps.getPermissionsState();
+ if (permissionsState.getInstallPermissionState(bp.getName()) != null) {
+ permissionsState.revokeInstallPermission(bp);
+ permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+ MASK_PERMISSION_FLAGS_ALL, 0);
+ }
+ });
}
it.remove();
}
@@ -4228,7 +4290,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
overridePolicy,
Process.SYSTEM_UID,
userId,
- callback);
+ null, callback);
} catch (IllegalArgumentException e) {
Slog.e(TAG,
"Failed to revoke "
@@ -4624,10 +4686,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
public void setWhitelistedRestrictedPermissions(@NonNull AndroidPackage pkg,
@NonNull int[] userIds, @Nullable List<String> permissions, int callingUid,
@PackageManager.PermissionWhitelistFlags int flags) {
- for (int userId : userIds) {
- setWhitelistedRestrictedPermissionsForUser(pkg, userId, permissions,
- callingUid, flags, mDefaultPermissionCallback);
- }
+ setWhitelistedRestrictedPermissionsForUsers(pkg, userIds, permissions,
+ callingUid, flags, mDefaultPermissionCallback);
}
@Override
public void setWhitelistedRestrictedPermissions(String packageName,
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 4412162a5cc8..2e83b23f57d8 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -156,7 +156,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
}
public void onInstallPermissionGranted() {
}
- public void onPermissionRevoked(int uid, @UserIdInt int userId) {
+ public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {
}
public void onInstallPermissionRevoked() {
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e75dab73478e..03868e922bdd 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -209,7 +209,6 @@ import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.AppTransition;
import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
@@ -491,7 +490,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private boolean mPendingKeyguardOccluded;
private boolean mKeyguardOccludedChanged;
- SleepToken mScreenOffSleepToken;
+ private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer;
volatile boolean mKeyguardOccluded;
Intent mHomeIntent;
Intent mCarDockIntent;
@@ -1741,6 +1740,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
mLogger = new MetricsLogger();
+ mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal
+ .createSleepTokenAcquirer("ScreenOff");
+
Resources res = mContext.getResources();
mWakeOnDpadKeyPress =
res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
@@ -4984,15 +4986,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
private void updateScreenOffSleepToken(boolean acquire) {
if (acquire) {
- if (mScreenOffSleepToken == null) {
- mScreenOffSleepToken = mActivityTaskManagerInternal.acquireSleepToken(
- "ScreenOff", DEFAULT_DISPLAY);
- }
+ mScreenOffSleepTokenAcquirer.acquire(DEFAULT_DISPLAY);
} else {
- if (mScreenOffSleepToken != null) {
- mScreenOffSleepToken.release();
- mScreenOffSleepToken = null;
- }
+ mScreenOffSleepTokenAcquirer.release(DEFAULT_DISPLAY);
}
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 018a1dacee88..651eafd77fe7 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -140,6 +140,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
@IntDef({NAV_BAR_LEFT, NAV_BAR_RIGHT, NAV_BAR_BOTTOM})
@interface NavigationBarPosition {}
+ @Retention(SOURCE)
+ @IntDef({ALT_BAR_UNKNOWN, ALT_BAR_LEFT, ALT_BAR_RIGHT, ALT_BAR_BOTTOM, ALT_BAR_TOP})
+ @interface AltBarPosition {}
+
/**
* Pass this event to the user / app. To be returned from
* {@link #interceptKeyBeforeQueueing}.
diff --git a/services/core/java/com/android/server/power/ShutdownCheckPoints.java b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
new file mode 100644
index 000000000000..e6d0dddf710c
--- /dev/null
+++ b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * The shutdown check points are a recording of more detailed information of the origin of calls to
+ * system shutdown and reboot framework methods.
+ *
+ * @hide
+ */
+public final class ShutdownCheckPoints {
+
+ private static final String TAG = "ShutdownCheckPoints";
+
+ private static final ShutdownCheckPoints INSTANCE = new ShutdownCheckPoints();
+
+ private static final int MAX_CHECK_POINTS = 100;
+ private static final int MAX_DUMP_FILES = 20;
+ private static final SimpleDateFormat DATE_FORMAT =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
+
+ private final LinkedList<CheckPoint> mCheckPoints;
+ private final Injector mInjector;
+
+ private ShutdownCheckPoints() {
+ this(new Injector() {
+ @Override
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ @Override
+ public int maxCheckPoints() {
+ return MAX_CHECK_POINTS;
+ }
+
+ @Override
+ public int maxDumpFiles() {
+ return MAX_DUMP_FILES;
+ }
+
+ @Override
+ public IActivityManager activityManager() {
+ return ActivityManager.getService();
+ }
+ });
+ }
+
+ @VisibleForTesting
+ ShutdownCheckPoints(Injector injector) {
+ mCheckPoints = new LinkedList<>();
+ mInjector = injector;
+ }
+
+ /** Records the stack trace of this {@link Thread} as a shutdown check point. */
+ public static void recordCheckPoint() {
+ INSTANCE.recordCheckPointInternal();
+ }
+
+ /** Records the pid of the caller process as a shutdown check point. */
+ public static void recordCheckPoint(int callerProcessId) {
+ INSTANCE.recordCheckPointInternal(callerProcessId);
+ }
+
+ /** Records the {@link android.content.Intent} name and package as a shutdown check point. */
+ public static void recordCheckPoint(String intentName, String packageName) {
+ INSTANCE.recordCheckPointInternal(intentName, packageName);
+ }
+
+ /** Serializes the recorded check points and writes them to given {@code printWriter}. */
+ public static void dump(PrintWriter printWriter) {
+ INSTANCE.dumpInternal(printWriter);
+ }
+
+ /**
+ * Creates a {@link Thread} that calls {@link #dump(PrintWriter)} on a rotating file created
+ * from given {@code baseFile} and a timestamp suffix. Older dump files are also deleted by this
+ * thread.
+ */
+ public static Thread newDumpThread(File baseFile) {
+ return INSTANCE.newDumpThreadInternal(baseFile);
+ }
+
+ @VisibleForTesting
+ void recordCheckPointInternal() {
+ recordCheckPointInternal(new SystemServerCheckPoint(mInjector));
+ Slog.v(TAG, "System server shutdown checkpoint recorded");
+ }
+
+ @VisibleForTesting
+ void recordCheckPointInternal(int callerProcessId) {
+ recordCheckPointInternal(callerProcessId == Process.myPid()
+ ? new SystemServerCheckPoint(mInjector)
+ : new BinderCheckPoint(mInjector, callerProcessId));
+ Slog.v(TAG, "Binder shutdown checkpoint recorded with pid=" + callerProcessId);
+ }
+
+ @VisibleForTesting
+ void recordCheckPointInternal(String intentName, String packageName) {
+ recordCheckPointInternal("android".equals(packageName)
+ ? new SystemServerCheckPoint(mInjector)
+ : new IntentCheckPoint(mInjector, intentName, packageName));
+ Slog.v(TAG, String.format("Shutdown intent checkpoint recorded intent=%s from package=%s",
+ intentName, packageName));
+ }
+
+ private void recordCheckPointInternal(CheckPoint checkPoint) {
+ synchronized (mCheckPoints) {
+ mCheckPoints.addLast(checkPoint);
+ if (mCheckPoints.size() > mInjector.maxCheckPoints()) mCheckPoints.removeFirst();
+ }
+ }
+
+ @VisibleForTesting
+ void dumpInternal(PrintWriter printWriter) {
+ final List<CheckPoint> records;
+ synchronized (mCheckPoints) {
+ records = new ArrayList<>(mCheckPoints);
+ }
+ for (CheckPoint record : records) {
+ record.dump(printWriter);
+ printWriter.println();
+ }
+ }
+
+ @VisibleForTesting
+ Thread newDumpThreadInternal(File baseFile) {
+ return new FileDumperThread(this, baseFile, mInjector.maxDumpFiles());
+ }
+
+ /** Injector used by {@link ShutdownCheckPoints} for testing purposes. */
+ @VisibleForTesting
+ interface Injector {
+
+ long currentTimeMillis();
+
+ int maxCheckPoints();
+
+ int maxDumpFiles();
+
+ IActivityManager activityManager();
+ }
+
+ /** Representation of a generic shutdown call, which can be serialized. */
+ private abstract static class CheckPoint {
+
+ private final long mTimestamp;
+
+ CheckPoint(Injector injector) {
+ mTimestamp = injector.currentTimeMillis();
+ }
+
+ final void dump(PrintWriter printWriter) {
+ printWriter.print("Shutdown request from ");
+ printWriter.print(getOrigin());
+ printWriter.print(" at ");
+ printWriter.print(DATE_FORMAT.format(new Date(mTimestamp)));
+ printWriter.println(" (epoch=" + mTimestamp + ")");
+ dumpDetails(printWriter);
+ }
+
+ abstract String getOrigin();
+
+ abstract void dumpDetails(PrintWriter printWriter);
+ }
+
+ /** Representation of a shutdown call from the system server, with stack trace. */
+ private static class SystemServerCheckPoint extends CheckPoint {
+
+ private final StackTraceElement[] mStackTraceElements;
+
+ SystemServerCheckPoint(Injector injector) {
+ super(injector);
+ mStackTraceElements = Thread.currentThread().getStackTrace();
+ }
+
+ @Override
+ String getOrigin() {
+ return "SYSTEM";
+ }
+
+ @Override
+ void dumpDetails(PrintWriter printWriter) {
+ String methodName = getMethodName();
+ printWriter.println(methodName == null ? "Failed to get method name" : methodName);
+ printStackTrace(printWriter);
+ }
+
+ @Nullable
+ String getMethodName() {
+ int idx = findCallSiteIndex();
+ if (idx < mStackTraceElements.length) {
+ StackTraceElement element = mStackTraceElements[idx];
+ return String.format("%s.%s", element.getClassName(), element.getMethodName());
+ }
+ return null;
+ }
+
+ void printStackTrace(PrintWriter printWriter) {
+ // Skip the call site line, as it's already considered with getMethodName.
+ for (int i = findCallSiteIndex() + 1; i < mStackTraceElements.length; i++) {
+ printWriter.print(" at ");
+ printWriter.println(mStackTraceElements[i]);
+ }
+ }
+
+ private int findCallSiteIndex() {
+ String className = ShutdownCheckPoints.class.getCanonicalName();
+ int idx = 0;
+ // Skip system trace lines until finding ShutdownCheckPoints call site.
+ while (idx < mStackTraceElements.length
+ && !mStackTraceElements[idx].getClassName().equals(className)) {
+ ++idx;
+ }
+ // Skip trace lines from ShutdownCheckPoints class.
+ while (idx < mStackTraceElements.length
+ && mStackTraceElements[idx].getClassName().equals(className)) {
+ ++idx;
+ }
+ return idx;
+ }
+ }
+
+ /** Representation of a shutdown call to {@link android.os.Binder}, with caller process id. */
+ private static class BinderCheckPoint extends SystemServerCheckPoint {
+ private final int mCallerProcessId;
+ private final IActivityManager mActivityManager;
+
+ BinderCheckPoint(Injector injector, int callerProcessId) {
+ super(injector);
+ mCallerProcessId = callerProcessId;
+ mActivityManager = injector.activityManager();
+ }
+
+ @Override
+ String getOrigin() {
+ return "BINDER";
+ }
+
+ @Override
+ void dumpDetails(PrintWriter printWriter) {
+ String methodName = getMethodName();
+ printWriter.println(methodName == null ? "Failed to get method name" : methodName);
+
+ String processName = getProcessName();
+ printWriter.print("From process ");
+ printWriter.print(processName == null ? "?" : processName);
+ printWriter.println(" (pid=" + mCallerProcessId + ")");
+ }
+
+ @Nullable
+ String getProcessName() {
+ try {
+ List<ActivityManager.RunningAppProcessInfo> runningProcesses =
+ mActivityManager.getRunningAppProcesses();
+ for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
+ if (processInfo.pid == mCallerProcessId) {
+ return processInfo.processName;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get running app processes from ActivityManager", e);
+ }
+ return null;
+ }
+ }
+
+ /** Representation of a shutdown call with {@link android.content.Intent}. */
+ private static class IntentCheckPoint extends CheckPoint {
+ private final String mIntentName;
+ private final String mPackageName;
+
+ IntentCheckPoint(Injector injector, String intentName, String packageName) {
+ super(injector);
+ mIntentName = intentName;
+ mPackageName = packageName;
+ }
+
+ @Override
+ String getOrigin() {
+ return "INTENT";
+ }
+
+ @Override
+ void dumpDetails(PrintWriter printWriter) {
+ printWriter.print("Intent: ");
+ printWriter.println(mIntentName);
+ printWriter.print("Package: ");
+ printWriter.println(mPackageName);
+ }
+ }
+
+ /**
+ * Thread that writes {@link ShutdownCheckPoints#dumpInternal(PrintWriter)} to a new file and
+ * deletes old ones to keep the total number of files down to a given limit.
+ */
+ private static final class FileDumperThread extends Thread {
+
+ private final ShutdownCheckPoints mInstance;
+ private final File mBaseFile;
+ private final int mFileCountLimit;
+
+ FileDumperThread(ShutdownCheckPoints instance, File baseFile, int fileCountLimit) {
+ mInstance = instance;
+ mBaseFile = baseFile;
+ mFileCountLimit = fileCountLimit;
+ }
+
+ @Override
+ public void run() {
+ mBaseFile.getParentFile().mkdirs();
+ File[] checkPointFiles = listCheckPointsFiles();
+
+ int filesToDelete = checkPointFiles.length - mFileCountLimit + 1;
+ for (int i = 0; i < filesToDelete; i++) {
+ checkPointFiles[i].delete();
+ }
+
+ File nextCheckPointsFile = new File(String.format("%s-%d",
+ mBaseFile.getAbsolutePath(), System.currentTimeMillis()));
+ writeCheckpoints(nextCheckPointsFile);
+ }
+
+ private File[] listCheckPointsFiles() {
+ String filePrefix = mBaseFile.getName() + "-";
+ File[] files = mBaseFile.getParentFile().listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ if (!name.startsWith(filePrefix)) {
+ return false;
+ }
+ try {
+ Long.valueOf(name.substring(filePrefix.length()));
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ return true;
+ }
+ });
+ Arrays.sort(files);
+ return files;
+ }
+
+ private void writeCheckpoints(File file) {
+ AtomicFile tmpFile = new AtomicFile(mBaseFile);
+ FileOutputStream fos = null;
+ try {
+ fos = tmpFile.startWrite();
+ PrintWriter pw = new PrintWriter(fos);
+ mInstance.dumpInternal(pw);
+ pw.flush();
+ tmpFile.finishWrite(fos); // This also closes the output stream.
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write shutdown checkpoints", e);
+ if (fos != null) {
+ tmpFile.failWrite(fos); // This also closes the output stream.
+ }
+ }
+ mBaseFile.renameTo(file);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index bc722f181650..2621d8b46494 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -64,10 +64,10 @@ public final class ShutdownThread extends Thread {
private static final int ACTION_DONE_POLL_WAIT_MS = 500;
private static final int RADIOS_STATE_POLL_SLEEP_MS = 100;
// maximum time we wait for the shutdown broadcast before going on.
- private static final int MAX_BROADCAST_TIME = 10*1000;
- private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
- private static final int MAX_RADIO_WAIT_TIME = 12*1000;
- private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
+ private static final int MAX_BROADCAST_TIME = 10 * 1000;
+ private static final int MAX_CHECK_POINTS_DUMP_WAIT_TIME = 20 * 1000;
+ private static final int MAX_RADIO_WAIT_TIME = 12 * 1000;
+ private static final int MAX_UNCRYPT_WAIT_TIME = 15 * 60 * 1000;
// constants for progress bar. the values are roughly estimated based on timeout.
private static final int BROADCAST_STOP_PERCENT = 2;
private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4;
@@ -105,8 +105,11 @@ public final class ShutdownThread extends Thread {
// Metrics that will be reported to tron after reboot
private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>();
- // File to use for save metrics
+ // File to use for saving shutdown metrics
private static final String METRICS_FILE_BASENAME = "/data/system/shutdown-metrics";
+ // File to use for saving shutdown check points
+ private static final String CHECK_POINTS_FILE_BASENAME =
+ "/data/system/shutdown-checkpoints/checkpoints";
// Metrics names to be persisted in shutdown-metrics file
private static String METRIC_SYSTEM_SERVER = "shutdown_system_server";
@@ -422,6 +425,11 @@ public final class ShutdownThread extends Thread {
metricShutdownStart();
metricStarted(METRIC_SYSTEM_SERVER);
+ // Start dumping check points for this shutdown in a separate thread.
+ Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread(
+ new File(CHECK_POINTS_FILE_BASENAME));
+ dumpCheckPointsThread.start();
+
BroadcastReceiver br = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
// We don't allow apps to cancel this, so ignore the result.
@@ -542,6 +550,14 @@ public final class ShutdownThread extends Thread {
uncrypt();
}
+ // Wait for the check points dump thread to finish, or kill it if not finished in time.
+ shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait");
+ try {
+ dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME);
+ } catch (InterruptedException ex) {
+ }
+ shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait
+
shutdownTimingLog.traceEnd(); // SystemServerShutdown
metricEnded(METRIC_SYSTEM_SERVER);
saveMetrics(mReboot, mReason);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 6ff069bd049d..dd287ca6ed00 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -49,6 +49,7 @@ import com.android.server.power.batterysaver.BatterySaverPolicy.Policy;
import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
+import com.android.server.power.batterysaver.BatterySavingStats.PlugState;
import java.util.ArrayList;
import java.util.Objects;
@@ -551,17 +552,14 @@ public class BatterySaverController implements BatterySaverPolicyListener {
: DozeState.NOT_DOZING;
synchronized (mLock) {
- if (mIsPluggedIn) {
- mBatterySavingStats.startCharging();
- return;
- }
mBatterySavingStats.transitionState(
getFullEnabledLocked() ? BatterySaverState.ON :
(getAdaptiveEnabledLocked() ? BatterySaverState.ADAPTIVE :
BatterySaverState.OFF),
isInteractive ? InteractiveState.INTERACTIVE :
InteractiveState.NON_INTERACTIVE,
- dozeMode);
+ dozeMode,
+ mIsPluggedIn ? PlugState.PLUGGED : PlugState.UNPLUGGED);
}
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 3dbc0072f8b4..05695d919910 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -17,8 +17,8 @@ package com.android.server.power.batterysaver;
import android.os.BatteryManagerInternal;
import android.os.SystemClock;
-import android.util.ArrayMap;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -93,6 +93,20 @@ public class BatterySavingStats {
}
}
+ /** Whether the device is plugged in or not. */
+ interface PlugState {
+ int UNPLUGGED = 0;
+ int PLUGGED = 1;
+
+ int SHIFT = DozeState.SHIFT + DozeState.BITS;
+ int BITS = 1;
+ int MASK = (1 << BITS) - 1;
+
+ static int fromIndex(int index) {
+ return (index >> SHIFT) & MASK;
+ }
+ }
+
/**
* Various stats in each state.
*/
@@ -140,7 +154,6 @@ public class BatterySavingStats {
private BatteryManagerInternal mBatteryManagerInternal;
private static final int STATE_NOT_INITIALIZED = -1;
- private static final int STATE_CHARGING = -2;
/**
* Current state, one of STATE_* or values returned by {@link #statesToIndex}.
@@ -153,20 +166,26 @@ public class BatterySavingStats {
*/
@VisibleForTesting
@GuardedBy("mLock")
- final ArrayMap<Integer, Stat> mStats = new ArrayMap<>();
+ final SparseArray<Stat> mStats = new SparseArray<>();
@GuardedBy("mLock")
private int mBatterySaverEnabledCount = 0;
@GuardedBy("mLock")
- private boolean mIsBatterySaverEnabled;
-
- @GuardedBy("mLock")
private long mLastBatterySaverEnabledTime = 0;
@GuardedBy("mLock")
private long mLastBatterySaverDisabledTime = 0;
+ @GuardedBy("mLock")
+ private int mAdaptiveBatterySaverEnabledCount = 0;
+
+ @GuardedBy("mLock")
+ private long mLastAdaptiveBatterySaverEnabledTime = 0;
+
+ @GuardedBy("mLock")
+ private long mLastAdaptiveBatterySaverDisabledTime = 0;
+
/** Visible for unit tests */
@VisibleForTesting
public BatterySavingStats(Object lock) {
@@ -189,10 +208,11 @@ public class BatterySavingStats {
*/
@VisibleForTesting
static int statesToIndex(
- int batterySaverState, int interactiveState, int dozeState) {
+ int batterySaverState, int interactiveState, int dozeState, int plugState) {
int ret = batterySaverState & BatterySaverState.MASK;
ret |= (interactiveState & InteractiveState.MASK) << InteractiveState.SHIFT;
ret |= (dozeState & DozeState.MASK) << DozeState.SHIFT;
+ ret |= (plugState & PlugState.MASK) << PlugState.SHIFT;
return ret;
}
@@ -204,12 +224,11 @@ public class BatterySavingStats {
switch (state) {
case STATE_NOT_INITIALIZED:
return "NotInitialized";
- case STATE_CHARGING:
- return "Charging";
}
return "BS=" + BatterySaverState.fromIndex(state)
+ ",I=" + InteractiveState.fromIndex(state)
- + ",D=" + DozeState.fromIndex(state);
+ + ",D=" + DozeState.fromIndex(state)
+ + ",P=" + PlugState.fromIndex(state);
}
/**
@@ -230,8 +249,9 @@ public class BatterySavingStats {
/**
* @return {@link Stat} fo a given state triplet.
*/
- private Stat getStat(int batterySaverState, int interactiveState, int dozeState) {
- return getStat(statesToIndex(batterySaverState, interactiveState, dozeState));
+ private Stat getStat(int batterySaverState, int interactiveState, int dozeState,
+ int plugState) {
+ return getStat(statesToIndex(batterySaverState, interactiveState, dozeState, plugState));
}
@VisibleForTesting
@@ -258,26 +278,17 @@ public class BatterySavingStats {
}
/**
- * Called from the outside whenever any of the states changes, when the device is not plugged
- * in.
+ * Called from the outside whenever any of the states change.
*/
- public void transitionState(int batterySaverState, int interactiveState, int dozeState) {
+ void transitionState(int batterySaverState, int interactiveState, int dozeState,
+ int plugState) {
synchronized (mLock) {
final int newState = statesToIndex(
- batterySaverState, interactiveState, dozeState);
+ batterySaverState, interactiveState, dozeState, plugState);
transitionStateLocked(newState);
}
}
- /**
- * Called from the outside when the device is plugged in.
- */
- public void startCharging() {
- synchronized (mLock) {
- transitionStateLocked(STATE_CHARGING);
- }
- }
-
@GuardedBy("mLock")
private void transitionStateLocked(int newState) {
if (mCurrentState == newState) {
@@ -287,17 +298,33 @@ public class BatterySavingStats {
final int batteryLevel = injectBatteryLevel();
final int batteryPercent = injectBatteryPercent();
- final boolean oldBatterySaverEnabled =
- BatterySaverState.fromIndex(mCurrentState) != BatterySaverState.OFF;
- final boolean newBatterySaverEnabled =
- BatterySaverState.fromIndex(newState) != BatterySaverState.OFF;
- if (oldBatterySaverEnabled != newBatterySaverEnabled) {
- mIsBatterySaverEnabled = newBatterySaverEnabled;
- if (newBatterySaverEnabled) {
- mBatterySaverEnabledCount++;
- mLastBatterySaverEnabledTime = injectCurrentTime();
- } else {
- mLastBatterySaverDisabledTime = injectCurrentTime();
+ final int oldBatterySaverState = mCurrentState < 0
+ ? BatterySaverState.OFF : BatterySaverState.fromIndex(mCurrentState);
+ final int newBatterySaverState = newState < 0
+ ? BatterySaverState.OFF : BatterySaverState.fromIndex(newState);
+ if (oldBatterySaverState != newBatterySaverState) {
+ switch (newBatterySaverState) {
+ case BatterySaverState.ON:
+ mBatterySaverEnabledCount++;
+ mLastBatterySaverEnabledTime = now;
+ if (oldBatterySaverState == BatterySaverState.ADAPTIVE) {
+ mLastAdaptiveBatterySaverDisabledTime = now;
+ }
+ break;
+ case BatterySaverState.OFF:
+ if (oldBatterySaverState == BatterySaverState.ON) {
+ mLastBatterySaverDisabledTime = now;
+ } else {
+ mLastAdaptiveBatterySaverDisabledTime = now;
+ }
+ break;
+ case BatterySaverState.ADAPTIVE:
+ mAdaptiveBatterySaverEnabledCount++;
+ mLastAdaptiveBatterySaverEnabledTime = now;
+ if (oldBatterySaverState == BatterySaverState.ON) {
+ mLastBatterySaverDisabledTime = now;
+ }
+ break;
}
}
@@ -377,7 +404,18 @@ public class BatterySavingStats {
pw.print(indent);
pw.print("Battery Saver is currently: ");
- pw.println(mIsBatterySaverEnabled ? "ON" : "OFF");
+ switch (BatterySaverState.fromIndex(mCurrentState)) {
+ case BatterySaverState.OFF:
+ pw.println("OFF");
+ break;
+ case BatterySaverState.ON:
+ pw.println("ON");
+ break;
+ case BatterySaverState.ADAPTIVE:
+ pw.println("ADAPTIVE");
+ break;
+ }
+
if (mLastBatterySaverEnabledTime > 0) {
pw.print(indent);
pw.print(" ");
@@ -400,9 +438,34 @@ public class BatterySavingStats {
pw.print(indent);
pw.print(" ");
- pw.print("Times enabled: ");
+ pw.print("Times full enabled: ");
pw.println(mBatterySaverEnabledCount);
+ if (mLastAdaptiveBatterySaverEnabledTime > 0) {
+ pw.print(indent);
+ pw.print(" ");
+ pw.print("Last ADAPTIVE ON time: ");
+ pw.print(sdf.format(
+ new Date(now - nowElapsed + mLastAdaptiveBatterySaverEnabledTime)));
+ pw.print(" ");
+ TimeUtils.formatDuration(mLastAdaptiveBatterySaverEnabledTime, nowElapsed, pw);
+ pw.println();
+ }
+ if (mLastAdaptiveBatterySaverDisabledTime > 0) {
+ pw.print(indent);
+ pw.print(" ");
+ pw.print("Last ADAPTIVE OFF time: ");
+ pw.print(sdf.format(
+ new Date(now - nowElapsed + mLastAdaptiveBatterySaverDisabledTime)));
+ pw.print(" ");
+ TimeUtils.formatDuration(mLastAdaptiveBatterySaverDisabledTime, nowElapsed, pw);
+ pw.println();
+ }
+ pw.print(indent);
+ pw.print(" ");
+ pw.print("Times adaptive enabled: ");
+ pw.println(mAdaptiveBatterySaverEnabledCount);
+
pw.println();
pw.print(indent);
@@ -436,8 +499,10 @@ public class BatterySavingStats {
pw.print(interactiveLabel);
pw.print(": ");
- final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState);
- final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState);
+ final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState,
+ PlugState.UNPLUGGED);
+ final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState,
+ PlugState.UNPLUGGED);
pw.println(String.format("%6dm %6dmAh(%3d%%) %8.1fmAh/h %6dm %6dmAh(%3d%%) %8.1fmAh/h",
offStat.totalMinutes(),
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 88938b204f6c..c8e36481c738 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -206,6 +206,16 @@ public class AppDataRollbackHelper {
}
/**
+ * Deletes snapshots of the credential encrypted apex data directories for the specified user,
+ * for the given rollback id. This method will be a no-op if the user is not unlocked.
+ */
+ public void destroyApexCeSnapshots(int userId, int rollbackId) {
+ if (!isUserCredentialLocked(userId)) {
+ mApexManager.destroyCeSnapshots(userId, rollbackId);
+ }
+ }
+
+ /**
* 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.
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index c3477804fb64..2db6043f1c0a 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -42,6 +42,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.ext.SdkExtensions;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -61,9 +62,9 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.function.Consumer;
-
/**
* Information about a rollback available for a set of atomically installed packages.
*
@@ -699,11 +700,13 @@ class Rollback {
void delete(AppDataRollbackHelper dataHelper) {
assertInWorkerThread();
boolean containsApex = false;
+ Set<Integer> apexUsers = new ArraySet<>();
for (PackageRollbackInfo pkgInfo : info.getPackages()) {
+ List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers();
if (pkgInfo.isApex()) {
containsApex = true;
+ apexUsers.addAll(snapshottedUsers);
} else {
- List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers();
for (int i = 0; i < snapshottedUsers.size(); i++) {
// Destroy app data snapshot.
int userId = snapshottedUsers.get(i);
@@ -714,6 +717,9 @@ class Rollback {
}
if (containsApex) {
dataHelper.destroyApexDeSnapshots(info.getRollbackId());
+ for (int user : apexUsers) {
+ dataHelper.destroyApexCeSnapshots(user, info.getRollbackId());
+ }
}
RollbackStore.deleteRollback(this);
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 4f18d07a843f..6ba675db0aed 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -640,7 +640,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
}
for (String apexPackageName : apexPackageNames) {
- // We will not recieve notifications when an apex is updated,
+ // We will not receive notifications when an apex is updated,
// so check now in case any rollbacks ought to be expired. The
// onPackagedReplace function is safe to call if the package
// hasn't actually been updated.
@@ -827,7 +827,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
}
/**
- * Do code and userdata backups to enable rollback of the given session.
+ * Do code and user-data backups to enable rollback of the given session.
* In case of multiPackage sessions, <code>session</code> should be one of
* the child sessions, not the parent session.
*
@@ -915,7 +915,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
}
}
- /**
+ /*
* The order is important here! Always enable the embedded apk-in-apex (if any) before
* enabling the embedding apex. Otherwise the rollback object might be in an inconsistent
* state where an embedding apex is successfully enabled while one of its embedded
@@ -1323,7 +1323,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
assertInWorkerThread();
int rollbackId = allocateRollbackId();
final int userId;
- if (parentSession.getUser() == UserHandle.ALL) {
+ if (parentSession.getUser().equals(UserHandle.ALL)) {
userId = UserHandle.SYSTEM.getIdentifier();
} else {
userId = parentSession.getUser().getIdentifier();
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/README.md b/services/core/java/com/android/server/soundtrigger_middleware/README.md
new file mode 100644
index 000000000000..416548d9bc5e
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/README.md
@@ -0,0 +1,19 @@
+# Sound Trigger Middleware
+TODO: Add component description.
+
+## Notes about thread synchronization
+This component has some tricky thread synchronization considerations due to its layered design and
+due to the fact that it is involved in both in-bound and out-bound calls from / to
+external components. To avoid potential deadlocks, a strict locking order must be ensured whenever
+nesting locks. The order is:
+- `SoundTriggerMiddlewareValidation` lock.
+- Audio policy service lock. This one is external - it should be assumed to be held whenever we're
+ inside the `ExternalCaptureStateTracker.setCaptureState()` call stack *AND* to be acquired from
+ within our calls into `AudioSessionProvider.acquireSession()`.
+- `SoundTriggerModule` lock.
+
+This dictates careful consideration of callbacks going from `SoundTriggerModule` to
+`SoundTriggerMiddlewareValidation` and especially those coming from the `setCaptureState()` path.
+We always invoke those calls outside of the `SoundTriggerModule` lock, so we can lock
+`SoundTriggerMiddlewareValidation`. However, in the `setCaptureState()` case, we have to use atomics
+in `SoundTriggerMiddlewareValidation` and avoid the lock.
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
index d98ad74a9d6b..761858ccd238 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
@@ -21,6 +21,7 @@ import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.hardware.soundtrigger.V2_3.ModelParameterRange;
import android.hardware.soundtrigger.V2_3.Properties;
import android.hardware.soundtrigger.V2_3.RecognitionConfig;
+import android.os.DeadObjectException;
import android.os.IHwBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -191,8 +192,13 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
}
private static RuntimeException handleException(RuntimeException e) {
- Log.e(TAG, "Exception caught from HAL, rebooting HAL");
- rebootHal();
+ if (e.getCause() instanceof DeadObjectException) {
+ // Server is dead, no need to reboot.
+ Log.e(TAG, "HAL died");
+ } else {
+ Log.e(TAG, "Exception caught from HAL, rebooting HAL");
+ rebootHal();
+ }
throw e;
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
new file mode 100644
index 000000000000..8e5ecee8262b
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
+import android.hardware.soundtrigger.V2_3.ModelParameterRange;
+import android.hardware.soundtrigger.V2_3.Properties;
+import android.hardware.soundtrigger.V2_3.RecognitionConfig;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * An {@link ISoundTriggerHw2} decorator that would enforce deadlines on all calls and reboot the
+ * HAL whenever they expire.
+ */
+public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
+ private static final long TIMEOUT_MS = 1000;
+ private static final String TAG = "SoundTriggerHw2Watchdog";
+
+ private final @NonNull
+ ISoundTriggerHw2 mUnderlying;
+ private final @NonNull
+ Timer mTimer;
+
+ public SoundTriggerHw2Watchdog(@NonNull ISoundTriggerHw2 underlying) {
+ mUnderlying = Objects.requireNonNull(underlying);
+ mTimer = new Timer("SoundTriggerHw2Watchdog");
+ }
+
+ @Override
+ public Properties getProperties() {
+ try (Watchdog ignore = new Watchdog()) {
+ return mUnderlying.getProperties();
+ }
+ }
+
+ @Override
+ public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
+ int cookie) {
+ try (Watchdog ignore = new Watchdog()) {
+ return mUnderlying.loadSoundModel(soundModel, callback, cookie);
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
+ int cookie) {
+ try (Watchdog ignore = new Watchdog()) {
+ return mUnderlying.loadPhraseSoundModel(soundModel, callback, cookie);
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ try (Watchdog ignore = new Watchdog()) {
+ mUnderlying.unloadSoundModel(modelHandle);
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ try (Watchdog ignore = new Watchdog()) {
+ mUnderlying.stopRecognition(modelHandle);
+ }
+ }
+
+ @Override
+ public void stopAllRecognitions() {
+ try (Watchdog ignore = new Watchdog()) {
+ mUnderlying.stopAllRecognitions();
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
+ int cookie) {
+ try (Watchdog ignore = new Watchdog()) {
+ mUnderlying.startRecognition(modelHandle, config, callback, cookie);
+ }
+ }
+
+ @Override
+ public void getModelState(int modelHandle) {
+ try (Watchdog ignore = new Watchdog()) {
+ mUnderlying.getModelState(modelHandle);
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ try (Watchdog ignore = new Watchdog()) {
+ return mUnderlying.getModelParameter(modelHandle, param);
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ try (Watchdog ignore = new Watchdog()) {
+ mUnderlying.setModelParameter(modelHandle, param, value);
+ }
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ try (Watchdog ignore = new Watchdog()) {
+ return mUnderlying.queryParameter(modelHandle, param);
+ }
+ }
+
+ @Override
+ public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
+ return mUnderlying.linkToDeath(recipient, cookie);
+ }
+
+ @Override
+ public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
+ return mUnderlying.unlinkToDeath(recipient);
+ }
+
+ @Override
+ public String interfaceDescriptor() throws RemoteException {
+ return mUnderlying.interfaceDescriptor();
+ }
+
+ private static void rebootHal() {
+ // This property needs to be defined in an init.rc script and trigger a HAL reboot.
+ SystemProperties.set("sys.audio.restart.hal", "1");
+ }
+
+ private class Watchdog implements AutoCloseable {
+ private final @NonNull
+ TimerTask mTask;
+ // This exception is used merely for capturing a stack trace at the time of creation.
+ private final @NonNull
+ Exception mException = new Exception();
+
+ Watchdog() {
+ mTask = new TimerTask() {
+ @Override
+ public void run() {
+ Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
+ rebootHal();
+ }
+ };
+ mTimer.schedule(mTask, TIMEOUT_MS);
+ }
+
+ @Override
+ public void close() {
+ mTask.cancel();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index f4c77a0b88ca..5d25d2cb554d 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -47,6 +47,9 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -328,7 +331,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
/** Activity state. */
- Activity activityState = Activity.LOADED;
+ private AtomicInteger mActivityState = new AtomicInteger(Activity.LOADED.ordinal());
/** Human-readable description of the model. */
final String description;
@@ -383,6 +386,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
parameterSupport.put(modelParam, range);
}
+
+ Activity getActivityState() {
+ return Activity.values()[mActivityState.get()];
+ }
+
+ void setActivityState(Activity activity) {
+ mActivityState.set(activity.ordinal());
+ }
}
/**
@@ -393,7 +404,13 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
IBinder.DeathRecipient {
private final ISoundTriggerCallback mCallback;
private ISoundTriggerModule mDelegate;
- private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
+ // While generally all the fields of this class must be changed under a lock, an exception
+ // is made for the specific case of changing a model state from ACTIVE to LOADED, which
+ // may happen as result of a recognition callback. This would happen atomically and is
+ // necessary in order to avoid deadlocks associated with locking from within callbacks
+ // possibly originating from the audio server.
+ private @NonNull
+ ConcurrentMap<Integer, ModelState> mLoadedModels = new ConcurrentHashMap<>();
private final int mHandle;
private ModuleStatus mState = ModuleStatus.ALIVE;
@@ -476,10 +493,9 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.activityState
- != ModelState.Activity.LOADED) {
+ if (modelState.getActivityState() != ModelState.Activity.LOADED) {
throw new IllegalStateException("Model with handle: " + modelHandle
- + " has invalid state for unloading: " + modelState.activityState);
+ + " has invalid state for unloading: " + modelState.getActivityState());
}
// From here on, every exception isn't client's fault.
@@ -509,19 +525,21 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.activityState
- != ModelState.Activity.LOADED) {
+ if (modelState.getActivityState() != ModelState.Activity.LOADED) {
throw new IllegalStateException("Model with handle: " + modelHandle
+ " has invalid state for starting recognition: "
- + modelState.activityState);
+ + modelState.getActivityState());
}
// From here on, every exception isn't client's fault.
try {
+ // Normally, we would set the state after the operation succeeds. However, since
+ // the activity state may be reset outside of the lock, we set it here first,
+ // and reset it in case of exception.
+ modelState.setActivityState(ModelState.Activity.ACTIVE);
mDelegate.startRecognition(modelHandle, config);
- modelState.activityState =
- ModelState.Activity.ACTIVE;
} catch (Exception e) {
+ modelState.setActivityState(ModelState.Activity.LOADED);
throw handleException(e);
}
}
@@ -548,8 +566,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// From here on, every exception isn't client's fault.
try {
mDelegate.stopRecognition(modelHandle);
- modelState.activityState =
- ModelState.Activity.LOADED;
+ modelState.setActivityState(ModelState.Activity.LOADED);
} catch (Exception e) {
throw handleException(e);
}
@@ -719,7 +736,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
pw.print(entry.getKey());
pw.print('\t');
- pw.print(entry.getValue().activityState.name());
+ pw.print(entry.getValue().getActivityState().name());
pw.print('\t');
pw.print(entry.getValue().description);
pw.println();
@@ -735,48 +752,61 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
- synchronized (SoundTriggerMiddlewareValidation.this) {
- if (event.status != RecognitionStatus.FORCED) {
- mLoadedModels.get(modelHandle).activityState =
- ModelState.Activity.LOADED;
- }
- try {
- mCallback.onRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
+ // might be coming from the audio server (via setCaptureState()) while it is holding
+ // a lock that is also acquired while loading / unloading models. Thus, we require a
+ // strict locking order here, where obtaining our lock must always come first.
+ // To avoid this problem, we use an atomic model activity state. There is a risk of the
+ // model not being in the mLoadedModels map here, since it might have been stopped /
+ // unloaded while the event was in flight.
+ if (event.status != RecognitionStatus.FORCED) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState != null) {
+ modelState.setActivityState(ModelState.Activity.LOADED);
}
}
+ try {
+ mCallback.onRecognition(modelHandle, event);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
}
@Override
public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
- synchronized (SoundTriggerMiddlewareValidation.this) {
- if (event.common.status != RecognitionStatus.FORCED) {
- mLoadedModels.get(modelHandle).activityState =
- ModelState.Activity.LOADED;
- }
- try {
- mCallback.onPhraseRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
+ // might be coming from the audio server (via setCaptureState()) while it is holding
+ // a lock that is also acquired while loading / unloading models. Thus, we require a
+ // strict locking order here, where obtaining our lock must always come first.
+ // To avoid this problem, we use an atomic model activity state. There is a risk of the
+ // model not being in the mLoadedModels map here, since it might have been stopped /
+ // unloaded while the event was in flight.
+ if (event.common.status != RecognitionStatus.FORCED) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState != null) {
+ modelState.setActivityState(ModelState.Activity.LOADED);
}
}
+ try {
+ mCallback.onPhraseRecognition(modelHandle, event);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
}
@Override
public void onRecognitionAvailabilityChange(boolean available) {
- synchronized (SoundTriggerMiddlewareValidation.this) {
- try {
- mCallback.onRecognitionAvailabilityChange(available);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
+ // Not locking to avoid deadlocks (not affecting any state).
+ try {
+ mCallback.onRecognitionAvailabilityChange(available);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
}
}
@@ -804,10 +834,9 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// Gracefully stop all active recognitions and unload the models.
for (Map.Entry<Integer, ModelState> entry :
mLoadedModels.entrySet()) {
- if (entry.getValue().activityState
- == ModelState.Activity.ACTIVE) {
- mDelegate.stopRecognition(entry.getKey());
- }
+ // Idempotent call, no harm in calling even for models that are already
+ // stopped.
+ mDelegate.stopRecognition(entry.getKey());
mDelegate.unloadModel(entry.getKey());
}
// Detach.
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 522e5e189232..c382e1152e4b 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -42,6 +42,7 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -153,19 +154,29 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
*
* @param active true iff external capture is active.
*/
- synchronized void setExternalCaptureState(boolean active) {
- if (mProperties.concurrentCapture) {
- // If we support concurrent capture, we don't care about any of this.
- return;
- }
- mRecognitionAvailable = !active;
- if (!mRecognitionAvailable) {
- // Our module does not support recognition while a capture is active -
- // need to abort all active recognitions.
- for (Session session : mActiveSessions) {
- session.abortActiveRecognitions();
+ void setExternalCaptureState(boolean active) {
+ // We should never invoke callbacks while holding the lock, since this may deadlock with
+ // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
+ // the lock, but invoke them after releasing it.
+ List<Runnable> callbacks = new LinkedList<>();
+
+ synchronized (this) {
+ if (mProperties.concurrentCapture) {
+ // If we support concurrent capture, we don't care about any of this.
+ return;
+ }
+ mRecognitionAvailable = !active;
+ if (!mRecognitionAvailable) {
+ // Our module does not support recognition while a capture is active -
+ // need to abort all active recognitions.
+ for (Session session : mActiveSessions) {
+ session.abortActiveRecognitions(callbacks);
+ }
}
}
+ for (Runnable callback : callbacks) {
+ callback.run();
+ }
for (Session session : mActiveSessions) {
session.notifyRecognitionAvailability();
}
@@ -205,7 +216,9 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
* Attached to the HAL service via factory.
*/
private void attachToHal() {
- mHalService = new SoundTriggerHw2Enforcer(new SoundTriggerHw2Compat(mHalFactory.create()));
+ mHalService = new SoundTriggerHw2Enforcer(
+ new SoundTriggerHw2Watchdog(
+ new SoundTriggerHw2Compat(mHalFactory.create())));
mHalService.linkToDeath(this, 0);
}
@@ -329,9 +342,18 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
@Override
public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
+ // We should never invoke callbacks while holding the lock, since this may deadlock with
+ // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
+ // the lock, but invoke them after releasing it.
+ List<Runnable> callbacks = new LinkedList<>();
+
synchronized (SoundTriggerModule.this) {
checkValid();
- mLoadedModels.get(modelHandle).startRecognition(config);
+ mLoadedModels.get(modelHandle).startRecognition(config, callbacks);
+ }
+
+ for (Runnable callback : callbacks) {
+ callback.run();
}
}
@@ -377,10 +399,12 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
/**
* Abort all currently active recognitions.
+ * @param callbacks Will be appended with a list of callbacks that need to be invoked
+ * after this method returns, without holding the module lock.
*/
- private void abortActiveRecognitions() {
+ private void abortActiveRecognitions(@NonNull List<Runnable> callbacks) {
for (Model model : mLoadedModels.values()) {
- model.abortActiveRecognition();
+ model.abortActiveRecognition(callbacks);
}
}
@@ -407,7 +431,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
private void checkValid() {
if (mCallback == null) {
- throw new ServiceSpecificException(Status.DEAD_OBJECT);
+ throw new RecoverableException(Status.DEAD_OBJECT);
}
}
@@ -475,10 +499,11 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
return mSession.mSessionHandle;
}
- private void startRecognition(@NonNull RecognitionConfig config) {
+ private void startRecognition(@NonNull RecognitionConfig config,
+ @NonNull List<Runnable> callbacks) {
if (!mRecognitionAvailable) {
// Recognition is unavailable - send an abort event immediately.
- notifyAbort();
+ callbacks.add(this::notifyAbort);
return;
}
android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
@@ -525,8 +550,12 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
ConversionUtil.aidl2hidlModelParameter(modelParam)));
}
- /** Abort the recognition, if active. */
- private void abortActiveRecognition() {
+ /**
+ * Abort the recognition, if active.
+ * @param callbacks Will be appended with a list of callbacks that need to be invoked
+ * after this method returns, without holding the module lock.
+ */
+ private void abortActiveRecognition(List<Runnable> callbacks) {
// If we're inactive, do nothing.
if (getState() != ModelState.ACTIVE) {
return;
@@ -535,7 +564,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
stopRecognition();
// Notify the client that recognition has been aborted.
- notifyAbort();
+ callbacks.add(this::notifyAbort);
}
/** Notify the client that recognition has been aborted. */
@@ -577,42 +606,44 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
public void recognitionCallback(
@NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
int cookie) {
+ RecognitionEvent aidlEvent =
+ ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
+ aidlEvent.captureSession = mSession.mSessionHandle;
synchronized (SoundTriggerModule.this) {
- RecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
- aidlEvent.captureSession = mSession.mSessionHandle;
- try {
- mCallback.onRecognition(mHandle, aidlEvent);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
- }
if (aidlEvent.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
}
+ // The callback must be invoked outside of the lock.
+ try {
+ mCallback.onRecognition(mHandle, aidlEvent);
+ } catch (RemoteException e) {
+ // We're not expecting any exceptions here.
+ throw e.rethrowAsRuntimeException();
+ }
}
@Override
public void phraseRecognitionCallback(
@NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
int cookie) {
+ PhraseRecognitionEvent aidlEvent =
+ ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
+ aidlEvent.common.captureSession = mSession.mSessionHandle;
+
synchronized (SoundTriggerModule.this) {
- PhraseRecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
- aidlEvent.common.captureSession = mSession.mSessionHandle;
- try {
- mCallback.onPhraseRecognition(mHandle, aidlEvent);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
- }
if (aidlEvent.common.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
}
+
+ // The callback must be invoked outside of the lock.
+ try {
+ mCallback.onPhraseRecognition(mHandle, aidlEvent);
+ } catch (RemoteException e) {
+ // We're not expecting any exceptions here.
+ throw e.rethrowAsRuntimeException();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 09fd33d5b4ed..f74cd611e9d0 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -39,8 +39,6 @@ import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
import static android.util.MathUtils.constrain;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_TRUNCATE_TIMESTAMP;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
@@ -1100,25 +1098,21 @@ public class StatsPullAtomService extends SystemService {
final NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
for (int j = 0; j < size; j++) {
statsExt.stats.getValues(j, entry);
- StatsEvent.Builder e = StatsEvent.newBuilder();
- e.setAtomId(atomTag);
- switch (atomTag) {
- case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
- case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
- e.addBooleanAnnotation(ANNOTATION_ID_TRUNCATE_TIMESTAMP, true);
- break;
- default:
- }
- e.writeInt(entry.uid);
- e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
+ StatsEvent statsEvent;
+
if (statsExt.slicedByFgbg) {
- e.writeInt(entry.set);
+ // MobileBytesTransferByFgBg atom or WifiBytesTransferByFgBg atom.
+ statsEvent = FrameworkStatsLog.buildStatsEvent(
+ atomTag, entry.uid,
+ (entry.set > 0), entry.rxBytes, entry.rxPackets, entry.txBytes,
+ entry.txPackets);
+ } else {
+ // MobileBytesTransfer atom or WifiBytesTransfer atom.
+ statsEvent = FrameworkStatsLog.buildStatsEvent(
+ atomTag, entry.uid, entry.rxBytes,
+ entry.rxPackets, entry.txBytes, entry.txPackets);
}
- e.writeLong(entry.rxBytes);
- e.writeLong(entry.rxPackets);
- e.writeLong(entry.txBytes);
- e.writeLong(entry.txPackets);
- ret.add(e.build());
+ ret.add(statsEvent);
}
}
@@ -1127,46 +1121,38 @@ public class StatsPullAtomService extends SystemService {
final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
for (int i = 0; i < statsExt.stats.size(); i++) {
statsExt.stats.getValues(i, entry);
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED)
- .addBooleanAnnotation(ANNOTATION_ID_TRUNCATE_TIMESTAMP, true)
- .writeInt(entry.uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeBoolean(entry.metered == NetworkStats.METERED_YES)
- .writeInt(entry.tag)
- .writeLong(entry.rxBytes)
- .writeLong(entry.rxPackets)
- .writeLong(entry.txBytes)
- .writeLong(entry.txPackets)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.uid,
+ entry.metered == NetworkStats.METERED_YES, entry.tag, entry.rxBytes,
+ entry.rxPackets, entry.txBytes, entry.txPackets));
}
}
private void addDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt,
@NonNull List<StatsEvent> pulledData) {
+
+ // Workaround for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}.
+ // 5G NSA mode means the primary cell is LTE with a secondary connection to an
+ // NR cell. To mitigate risk, NetworkStats is currently storing this state as
+ // a fake RAT type rather than storing the boolean separately.
+ final boolean is5GNsa = statsExt.ratType == NetworkTemplate.NETWORK_TYPE_5G_NSA;
+ // Report NR connected in 5G non-standalone mode, or if the RAT type is NR to begin with.
+ final boolean isNR = is5GNsa || statsExt.ratType == TelephonyManager.NETWORK_TYPE_NR;
+
final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
for (int i = 0; i < statsExt.stats.size(); i++) {
statsExt.stats.getValues(i, entry);
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER)
- .addBooleanAnnotation(ANNOTATION_ID_TRUNCATE_TIMESTAMP, true)
- .writeInt(entry.set)
- .writeLong(entry.rxBytes)
- .writeLong(entry.rxPackets)
- .writeLong(entry.txBytes)
- .writeLong(entry.txPackets)
- .writeInt(statsExt.ratType)
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER, entry.set, entry.rxBytes,
+ entry.rxPackets, entry.txBytes, entry.txPackets,
+ is5GNsa ? TelephonyManager.NETWORK_TYPE_LTE : statsExt.ratType,
// Fill information about subscription, these cannot be null since invalid data
// would be filtered when adding into subInfo list.
- .writeString(statsExt.subInfo.mcc)
- .writeString(statsExt.subInfo.mnc)
- .writeInt(statsExt.subInfo.carrierId)
- .writeInt(statsExt.subInfo.isOpportunistic
+ statsExt.subInfo.mcc, statsExt.subInfo.mnc, statsExt.subInfo.carrierId,
+ statsExt.subInfo.isOpportunistic
? DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC
- : DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC)
- .build();
- pulledData.add(e);
+ : DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC,
+ isNR));
}
}
@@ -1427,14 +1413,8 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SKIP;
}
for (UidTraffic traffic : info.getUidTraffic()) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(traffic.getUid())
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeLong(traffic.getRxBytes())
- .writeLong(traffic.getTxBytes())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, traffic.getUid(), traffic.getRxBytes(), traffic.getTxBytes()));
}
return StatsManager.PULL_SUCCESS;
}
@@ -1455,14 +1435,8 @@ public class StatsPullAtomService extends SystemService {
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
String name = ent.getKey();
KernelWakelockStats.Entry kws = ent.getValue();
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeString(name)
- .writeInt(kws.mCount)
- .writeInt(kws.mVersion)
- .writeLong(kws.mTotalTime)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, name, kws.mCount, kws.mVersion, kws.mTotalTime));
}
return StatsManager.PULL_SUCCESS;
}
@@ -1485,13 +1459,8 @@ public class StatsPullAtomService extends SystemService {
long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
if (clusterTimeMs != null) {
for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(cluster)
- .writeInt(speed)
- .writeLong(clusterTimeMs[speed])
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, cluster, speed, clusterTimeMs[speed]));
}
}
}
@@ -1514,14 +1483,8 @@ public class StatsPullAtomService extends SystemService {
int pullCpuTimePerUidLocked(int atomTag, List<StatsEvent> pulledData) {
mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> {
long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeLong(userTimeUs)
- .writeLong(systemTimeUs)
- .build();
- pulledData.add(e);
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(atomTag, uid, userTimeUs, systemTimeUs));
});
return StatsManager.PULL_SUCCESS;
}
@@ -1545,14 +1508,8 @@ public class StatsPullAtomService extends SystemService {
mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
if (cpuFreqTimeMs[freqIndex] != 0) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeInt(freqIndex)
- .writeLong(cpuFreqTimeMs[freqIndex])
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, uid, freqIndex, cpuFreqTimeMs[freqIndex]));
}
}
});
@@ -1576,13 +1533,7 @@ public class StatsPullAtomService extends SystemService {
int pullCpuActiveTimeLocked(int atomTag, List<StatsEvent> pulledData) {
mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeLong(cpuActiveTimesMs)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, uid, cpuActiveTimesMs));
});
return StatsManager.PULL_SUCCESS;
}
@@ -1605,14 +1556,8 @@ public class StatsPullAtomService extends SystemService {
int pullCpuClusterTimeLocked(int atomTag, List<StatsEvent> pulledData) {
mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
for (int i = 0; i < cpuClusterTimesMs.length; i++) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeInt(i)
- .writeLong(cpuClusterTimesMs[i])
- .build();
- pulledData.add(e);
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(atomTag, uid, i, cpuClusterTimesMs[i]));
}
});
return StatsManager.PULL_SUCCESS;
@@ -1651,16 +1596,12 @@ public class StatsPullAtomService extends SystemService {
if (wifiInfo == null) {
return StatsManager.PULL_SKIP;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(wifiInfo.getTimeSinceBootMillis())
- .writeInt(wifiInfo.getStackState())
- .writeLong(wifiInfo.getControllerTxDurationMillis())
- .writeLong(wifiInfo.getControllerRxDurationMillis())
- .writeLong(wifiInfo.getControllerIdleDurationMillis())
- .writeLong(wifiInfo.getControllerEnergyUsedMicroJoules())
- .build();
- pulledData.add(e);
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(atomTag, wifiInfo.getTimeSinceBootMillis(),
+ wifiInfo.getStackState(), wifiInfo.getControllerTxDurationMillis(),
+ wifiInfo.getControllerRxDurationMillis(),
+ wifiInfo.getControllerIdleDurationMillis(),
+ wifiInfo.getControllerEnergyUsedMicroJoules()));
} catch (RuntimeException e) {
Slog.e(TAG, "failed to getWifiActivityEnergyInfoAsync", e);
return StatsManager.PULL_SKIP;
@@ -1689,19 +1630,15 @@ public class StatsPullAtomService extends SystemService {
if (modemInfo == null) {
return StatsManager.PULL_SKIP;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(modemInfo.getTimestamp())
- .writeLong(modemInfo.getSleepTimeMillis())
- .writeLong(modemInfo.getIdleTimeMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis())
- .writeLong(modemInfo.getReceiveTimeMillis())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, modemInfo.getTimestamp(),
+ modemInfo.getSleepTimeMillis(), modemInfo.getIdleTimeMillis(),
+ modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis(),
+ modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis(),
+ modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis(),
+ modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis(),
+ modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis(),
+ modemInfo.getReceiveTimeMillis(),
+ -1 /*`energy_used` field name deprecated, use -1 to indicate as unused.*/));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1723,16 +1660,10 @@ public class StatsPullAtomService extends SystemService {
if (info == null) {
return StatsManager.PULL_SKIP;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(info.getTimeStamp())
- .writeInt(info.getBluetoothStackState())
- .writeLong(info.getControllerTxTimeMillis())
- .writeLong(info.getControllerRxTimeMillis())
- .writeLong(info.getControllerIdleTimeMillis())
- .writeLong(info.getControllerEnergyUsed())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, info.getTimeStamp(),
+ info.getBluetoothStackState(), info.getControllerTxTimeMillis(),
+ info.getControllerRxTimeMillis(), info.getControllerIdleTimeMillis(),
+ info.getControllerEnergyUsed()));
return StatsManager.PULL_SUCCESS;
}
@@ -1751,11 +1682,7 @@ public class StatsPullAtomService extends SystemService {
}
int pullSystemElapsedRealtimeLocked(int atomTag, List<StatsEvent> pulledData) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(SystemClock.elapsedRealtime())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, SystemClock.elapsedRealtime()));
return StatsManager.PULL_SUCCESS;
}
@@ -1770,11 +1697,7 @@ public class StatsPullAtomService extends SystemService {
}
int pullSystemUptimeLocked(int atomTag, List<StatsEvent> pulledData) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(SystemClock.uptimeMillis())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, SystemClock.uptimeMillis()));
return StatsManager.PULL_SUCCESS;
}
@@ -1801,22 +1724,10 @@ public class StatsPullAtomService extends SystemService {
if (memoryStat == null) {
continue;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(processMemoryState.uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(processMemoryState.processName)
- .writeInt(processMemoryState.oomScore)
- .writeLong(memoryStat.pgfault)
- .writeLong(memoryStat.pgmajfault)
- .writeLong(memoryStat.rssInBytes)
- .writeLong(memoryStat.cacheInBytes)
- .writeLong(memoryStat.swapInBytes)
- .writeLong(-1) // unused
- .writeLong(-1) // unused
- .writeInt(-1) // unused
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, processMemoryState.uid,
+ processMemoryState.processName, processMemoryState.oomScore, memoryStat.pgfault,
+ memoryStat.pgmajfault, memoryStat.rssInBytes, memoryStat.cacheInBytes,
+ memoryStat.swapInBytes, -1 /*unused*/, -1 /*unused*/, -1 /*unused*/));
}
return StatsManager.PULL_SUCCESS;
}
@@ -1840,16 +1751,11 @@ public class StatsPullAtomService extends SystemService {
if (snapshot == null) {
continue;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(managedProcess.uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(managedProcess.processName)
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, managedProcess.uid,
+ managedProcess.processName,
// RSS high-water mark in bytes.
- .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L)
- .writeInt(snapshot.rssHighWaterMarkInKilobytes)
- .build();
- pulledData.add(e);
+ snapshot.rssHighWaterMarkInKilobytes * 1024L,
+ snapshot.rssHighWaterMarkInKilobytes));
}
// Complement the data with native system processes
SparseArray<String> processCmdlines = getProcessCmdlines();
@@ -1860,16 +1766,11 @@ public class StatsPullAtomService extends SystemService {
if (snapshot == null) {
continue;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(snapshot.uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(processCmdlines.valueAt(i))
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, snapshot.uid,
+ processCmdlines.valueAt(i),
// RSS high-water mark in bytes.
- .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L)
- .writeInt(snapshot.rssHighWaterMarkInKilobytes)
- .build();
- pulledData.add(e);
+ snapshot.rssHighWaterMarkInKilobytes * 1024L,
+ snapshot.rssHighWaterMarkInKilobytes));
}
// Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
SystemProperties.set("sys.rss_hwm_reset.on", "1");
@@ -1895,19 +1796,10 @@ public class StatsPullAtomService extends SystemService {
if (snapshot == null) {
continue;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(managedProcess.uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(managedProcess.processName)
- .writeInt(managedProcess.pid)
- .writeInt(managedProcess.oomScore)
- .writeInt(snapshot.rssInKilobytes)
- .writeInt(snapshot.anonRssInKilobytes)
- .writeInt(snapshot.swapInKilobytes)
- .writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, managedProcess.uid,
+ managedProcess.processName, managedProcess.pid, managedProcess.oomScore,
+ snapshot.rssInKilobytes, snapshot.anonRssInKilobytes, snapshot.swapInKilobytes,
+ snapshot.anonRssInKilobytes + snapshot.swapInKilobytes));
}
// Complement the data with native system processes. Given these measurements can be taken
// in response to LMKs happening, we want to first collect the managed app stats (to
@@ -1921,19 +1813,11 @@ public class StatsPullAtomService extends SystemService {
if (snapshot == null) {
continue;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(snapshot.uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(processCmdlines.valueAt(i))
- .writeInt(pid)
- .writeInt(-1001) // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
- .writeInt(snapshot.rssInKilobytes)
- .writeInt(snapshot.anonRssInKilobytes)
- .writeInt(snapshot.swapInKilobytes)
- .writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, snapshot.uid,
+ processCmdlines.valueAt(i), pid,
+ -1001 /*Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.*/,
+ snapshot.rssInKilobytes, snapshot.anonRssInKilobytes, snapshot.swapInKilobytes,
+ snapshot.anonRssInKilobytes + snapshot.swapInKilobytes));
}
return StatsManager.PULL_SUCCESS;
}
@@ -1950,11 +1834,7 @@ public class StatsPullAtomService extends SystemService {
int pullSystemIonHeapSizeLocked(int atomTag, List<StatsEvent> pulledData) {
final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(systemIonHeapSizeInBytes)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, systemIonHeapSizeInBytes));
return StatsManager.PULL_SUCCESS;
}
@@ -1973,11 +1853,7 @@ public class StatsPullAtomService extends SystemService {
int pullIonHeapSizeLocked(int atomTag, List<StatsEvent> pulledData) {
int ionHeapSizeInKilobytes = (int) getIonHeapsSizeKb();
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(ionHeapSizeInKilobytes)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, ionHeapSizeInKilobytes));
return StatsManager.PULL_SUCCESS;
}
@@ -1994,16 +1870,10 @@ public class StatsPullAtomService extends SystemService {
int pullProcessSystemIonHeapSizeLocked(int atomTag, List<StatsEvent> pulledData) {
List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
for (IonAllocations allocations : result) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(getUidForPid(allocations.pid))
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(readCmdlineFromProcfs(allocations.pid))
- .writeInt((int) (allocations.totalSizeInBytes / 1024))
- .writeInt(allocations.count)
- .writeInt((int) (allocations.maxSizeInBytes / 1024))
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, getUidForPid(allocations.pid),
+ readCmdlineFromProcfs(allocations.pid),
+ (int) (allocations.totalSizeInBytes / 1024), allocations.count,
+ (int) (allocations.maxSizeInBytes / 1024)));
}
return StatsManager.PULL_SUCCESS;
}
@@ -2027,14 +1897,8 @@ public class StatsPullAtomService extends SystemService {
try {
Temperature temperatures[] = thermalService.getCurrentTemperatures();
for (Temperature temp : temperatures) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(temp.getType())
- .writeString(temp.getName())
- .writeInt((int) (temp.getValue() * 10))
- .writeInt(temp.getStatus())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, temp.getType(),
+ temp.getName(), (int) (temp.getValue() * 10), temp.getStatus()));
}
} catch (RemoteException e) {
// Should not happen.
@@ -2065,13 +1929,8 @@ public class StatsPullAtomService extends SystemService {
try {
CoolingDevice devices[] = thermalService.getCurrentCoolingDevices();
for (CoolingDevice device : devices) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(device.getType())
- .writeString(device.getName())
- .writeInt((int) (device.getValue()))
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, device.getType(), device.getName(), (int) (device.getValue())));
}
} catch (RemoteException e) {
// Should not happen.
@@ -2107,25 +1966,12 @@ public class StatsPullAtomService extends SystemService {
List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
binderStats.reset();
for (ExportedCallStat callStat : callStats) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(callStat.workSourceUid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(callStat.className)
- .writeString(callStat.methodName)
- .writeLong(callStat.callCount)
- .writeLong(callStat.exceptionCount)
- .writeLong(callStat.latencyMicros)
- .writeLong(callStat.maxLatencyMicros)
- .writeLong(callStat.cpuTimeMicros)
- .writeLong(callStat.maxCpuTimeMicros)
- .writeLong(callStat.maxReplySizeBytes)
- .writeLong(callStat.maxRequestSizeBytes)
- .writeLong(callStat.recordedCallCount)
- .writeInt(callStat.screenInteractive ? 1 : 0)
- .writeInt(callStat.callingUid)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, callStat.workSourceUid,
+ callStat.className, callStat.methodName, callStat.callCount,
+ callStat.exceptionCount, callStat.latencyMicros, callStat.maxLatencyMicros,
+ callStat.cpuTimeMicros, callStat.maxCpuTimeMicros, callStat.maxReplySizeBytes,
+ callStat.maxRequestSizeBytes, callStat.recordedCallCount,
+ callStat.screenInteractive, callStat.callingUid));
}
return StatsManager.PULL_SUCCESS;
}
@@ -2152,12 +1998,8 @@ public class StatsPullAtomService extends SystemService {
// TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
// can reset the exception stats.
for (Map.Entry<String, Integer> entry : exceptionStats.entrySet()) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeString(entry.getKey())
- .writeInt(entry.getValue())
- .build();
- pulledData.add(e);
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(atomTag, entry.getKey(), entry.getValue()));
}
return StatsManager.PULL_SUCCESS;
}
@@ -2184,26 +2026,12 @@ public class StatsPullAtomService extends SystemService {
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
looperStats.reset();
for (LooperStats.ExportedEntry entry : entries) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(entry.workSourceUid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(entry.handlerClassName)
- .writeString(entry.threadName)
- .writeString(entry.messageName)
- .writeLong(entry.messageCount)
- .writeLong(entry.exceptionCount)
- .writeLong(entry.recordedMessageCount)
- .writeLong(entry.totalLatencyMicros)
- .writeLong(entry.cpuUsageMicros)
- .writeBoolean(entry.isInteractive)
- .writeLong(entry.maxCpuUsageMicros)
- .writeLong(entry.maxLatencyMicros)
- .writeLong(entry.recordedDelayMessageCount)
- .writeLong(entry.delayMillis)
- .writeLong(entry.maxDelayMillis)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, entry.workSourceUid,
+ entry.handlerClassName, entry.threadName, entry.messageName, entry.messageCount,
+ entry.exceptionCount, entry.recordedMessageCount, entry.totalLatencyMicros,
+ entry.cpuUsageMicros, entry.isInteractive, entry.maxCpuUsageMicros,
+ entry.maxLatencyMicros, entry.recordedDelayMessageCount, entry.delayMillis,
+ entry.maxDelayMillis));
}
return StatsManager.PULL_SUCCESS;
}
@@ -2264,13 +2092,7 @@ public class StatsPullAtomService extends SystemService {
}
// Add info pulledData.
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(latency)
- .writeBoolean(fileBased)
- .writeInt(writeSpeed)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, latency, fileBased, writeSpeed));
return StatsManager.PULL_SUCCESS;
}
@@ -2289,29 +2111,17 @@ public class StatsPullAtomService extends SystemService {
StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__DATA)
- .writeLong(statFsData.getAvailableBytes())
- .writeLong(statFsData.getTotalBytes())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__DATA, statFsData.getAvailableBytes(),
+ statFsData.getTotalBytes()));
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE)
- .writeLong(statFsCache.getAvailableBytes())
- .writeLong(statFsCache.getTotalBytes())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE,
+ statFsCache.getAvailableBytes(), statFsCache.getTotalBytes()));
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM)
- .writeLong(statFsSystem.getAvailableBytes())
- .writeLong(statFsSystem.getTotalBytes())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM,
+ statFsSystem.getAvailableBytes(), statFsSystem.getTotalBytes()));
return StatsManager.PULL_SUCCESS;
}
@@ -2342,15 +2152,10 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SKIP;
}
for (int i = 0; i < length; i++) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeString(pkg_names.getString(i))
- .writeLong(app_sizes.optLong(i, /* fallback */ -1L))
- .writeLong(app_data_sizes.optLong(i, /* fallback */ -1L))
- .writeLong(app_cache_sizes.optLong(i, /* fallback */ -1L))
- .writeLong(cache_time)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pkg_names.getString(i),
+ app_sizes.optLong(i, /* fallback */ -1L),
+ app_data_sizes.optLong(i, /* fallback */ -1L),
+ app_cache_sizes.optLong(i, /* fallback */ -1L), cache_time));
}
} catch (IOException | JSONException e) {
Slog.w(TAG, "Unable to read diskstats cache file within pullAppSize");
@@ -2376,86 +2181,45 @@ public class StatsPullAtomService extends SystemService {
long cacheTime = json.optLong(
DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, /* fallback */ -1L);
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE)
- .writeLong(json.optLong(
- DiskStatsFileLogger.APP_SIZE_AGG_KEY, /* fallback */ -1L))
- .writeLong(cacheTime)
- .build();
- pulledData.add(e);
-
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE)
- .writeLong(json.optLong(
- DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, /* fallback */ -1L))
- .writeLong(cacheTime)
- .build();
- pulledData.add(e);
-
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE)
- .writeLong(json.optLong(
- DiskStatsFileLogger.APP_CACHE_AGG_KEY, /* fallback */ -1L))
- .writeLong(cacheTime)
- .build();
- pulledData.add(e);
-
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS)
- .writeLong(json.optLong(
- DiskStatsFileLogger.PHOTOS_KEY, /* fallback */ -1L))
- .writeLong(cacheTime)
- .build();
- pulledData.add(e);
-
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS)
- .writeLong(
- json.optLong(DiskStatsFileLogger.VIDEOS_KEY, /* fallback */ -1L))
- .writeLong(cacheTime)
- .build();
- pulledData.add(e);
-
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__AUDIO)
- .writeLong(json.optLong(
- DiskStatsFileLogger.AUDIO_KEY, /* fallback */ -1L))
- .writeLong(cacheTime)
- .build();
- pulledData.add(e);
-
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS)
- .writeLong(
- json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, /* fallback */ -1L))
- .writeLong(cacheTime)
- .build();
- pulledData.add(e);
-
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM)
- .writeLong(json.optLong(
- DiskStatsFileLogger.SYSTEM_KEY, /* fallback */ -1L))
- .writeLong(cacheTime)
- .build();
- pulledData.add(e);
-
- e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__OTHER)
- .writeLong(json.optLong(
- DiskStatsFileLogger.MISC_KEY, /* fallback */ -1L))
- .writeLong(cacheTime)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE,
+ json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, /* fallback */ -1L),
+ cacheTime));
+
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE,
+ json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, /* fallback */ -1L),
+ cacheTime));
+
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE,
+ json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, /* fallback */ -1L),
+ cacheTime));
+
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS,
+ json.optLong(DiskStatsFileLogger.PHOTOS_KEY, /* fallback */ -1L), cacheTime));
+
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS,
+ json.optLong(DiskStatsFileLogger.VIDEOS_KEY, /* fallback */ -1L), cacheTime));
+
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__AUDIO,
+ json.optLong(DiskStatsFileLogger.AUDIO_KEY, /* fallback */ -1L), cacheTime));
+
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS,
+ json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, /* fallback */ -1L),
+ cacheTime));
+
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM,
+ json.optLong(DiskStatsFileLogger.SYSTEM_KEY, /* fallback */ -1L), cacheTime));
+
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__OTHER,
+ json.optLong(DiskStatsFileLogger.MISC_KEY, /* fallback */ -1L), cacheTime));
} catch (IOException | JSONException e) {
Slog.w(TAG, "Unable to read diskstats cache file within pullCategorySize");
return StatsManager.PULL_SKIP;
@@ -2519,12 +2283,7 @@ public class StatsPullAtomService extends SystemService {
} else {
return StatsManager.PULL_SKIP;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(userId)
- .writeInt(numEnrolled)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, userId, numEnrolled));
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -2575,13 +2334,15 @@ public class StatsPullAtomService extends SystemService {
lastHighWaterMark, section, true, statsFiles, procStats);
procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
- for (ProtoOutputStream proto : protoStreams) {
- if (proto.getBytes().length > 0) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeByteArray(proto.getBytes())
- .build();
- pulledData.add(e);
+ for (int i = 0; i < protoStreams.length; i++) {
+ byte[] bytes = protoStreams[i].getBytes(); // cache the value
+ if (bytes.length > 0) {
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, bytes,
+ // This is a shard ID, and is specified in the metric definition to be
+ // a dimension. This will result in statsd using RANDOM_ONE_SAMPLE to
+ // keep all the shards, as it thinks each shard is a different dimension
+ // of data.
+ i));
}
}
@@ -2634,26 +2395,13 @@ public class StatsPullAtomService extends SystemService {
}
int pullDiskIOLocked(int atomTag, List<StatsEvent> pulledData) {
- mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
- fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
- fgFsync, bgFsync) -> {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeLong(fgCharsRead)
- .writeLong(fgCharsWrite)
- .writeLong(fgBytesRead)
- .writeLong(fgBytesWrite)
- .writeLong(bgCharsRead)
- .writeLong(bgCharsWrite)
- .writeLong(bgBytesRead)
- .writeLong(bgBytesWrite)
- .writeLong(fgFsync)
- .writeLong(bgFsync)
- .build();
- pulledData.add(e);
- });
+ mStoragedUidIoStatsReader.readAbsolute(
+ (uid, fgCharsRead, fgCharsWrite, fgBytesRead, fgBytesWrite, bgCharsRead,
+ bgCharsWrite, bgBytesRead, bgBytesWrite, fgFsync, bgFsync) -> {
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, uid, fgCharsRead,
+ fgCharsWrite, fgBytesRead, fgBytesWrite, bgCharsRead, bgCharsWrite,
+ bgBytesRead, bgBytesWrite, fgFsync, bgFsync));
+ });
return StatsManager.PULL_SUCCESS;
}
@@ -2672,11 +2420,7 @@ public class StatsPullAtomService extends SystemService {
ProtoOutputStream proto = new ProtoOutputStream();
powerProfile.dumpDebug(proto);
proto.flush();
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeByteArray(proto.getBytes())
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, proto.getBytes()));
return StatsManager.PULL_SUCCESS;
}
@@ -2702,15 +2446,8 @@ public class StatsPullAtomService extends SystemService {
mProcessCpuTracker.update();
for (int i = 0; i < mProcessCpuTracker.countStats(); i++) {
ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(st.uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(st.name)
- .writeLong(st.base_utime)
- .writeLong(st.base_stime)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, st.uid, st.name, st.base_utime, st.base_stime));
}
return StatsManager.PULL_SUCCESS;
}
@@ -2760,27 +2497,27 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SKIP;
}
- StatsEvent.Builder e = StatsEvent.newBuilder();
- e.setAtomId(atomTag);
- e.writeInt(processCpuUsage.uid);
- e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- e.writeInt(processCpuUsage.processId);
- e.writeInt(threadCpuUsage.threadId);
- e.writeString(processCpuUsage.processName);
- e.writeString(threadCpuUsage.threadName);
+ int[] frequencies = new int[CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES];
+ int[] usageTimesMillis = new int[CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES];
for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES; k++) {
if (k < cpuFrequencies.length) {
- e.writeInt(cpuFrequencies[k]);
- e.writeInt(threadCpuUsage.usageTimesMillis[k]);
+ frequencies[k] = cpuFrequencies[k];
+ usageTimesMillis[k] = threadCpuUsage.usageTimesMillis[k];
} else {
// If we have no more frequencies to write, we still must write empty data.
// We know that this data is empty (and not just zero) because all
// frequencies are expected to be greater than zero
- e.writeInt(0);
- e.writeInt(0);
+ frequencies[k] = 0;
+ usageTimesMillis[k] = 0;
}
}
- pulledData.add(e.build());
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, processCpuUsage.uid,
+ processCpuUsage.processId, threadCpuUsage.threadId,
+ processCpuUsage.processName, threadCpuUsage.threadName, frequencies[0],
+ usageTimesMillis[0], frequencies[1], usageTimesMillis[1], frequencies[2],
+ usageTimesMillis[2], frequencies[3], usageTimesMillis[3], frequencies[4],
+ usageTimesMillis[4], frequencies[5], usageTimesMillis[5], frequencies[6],
+ usageTimesMillis[6], frequencies[7], usageTimesMillis[7]));
}
}
return StatsManager.PULL_SUCCESS;
@@ -2828,11 +2565,8 @@ public class StatsPullAtomService extends SystemService {
int pullDeviceCalculatedPowerUseLocked(int atomTag, List<StatsEvent> pulledData) {
BatteryStatsHelper bsHelper = getBatteryStatsHelper();
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()))
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower())));
return StatsManager.PULL_SUCCESS;
}
@@ -2856,13 +2590,8 @@ public class StatsPullAtomService extends SystemService {
if (bs.drainType != bs.drainType.APP) {
continue;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(bs.uidObj.getUid())
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, bs.uidObj.getUid(), milliAmpHrsToNanoAmpSecs(bs.totalPowerMah)));
}
return StatsManager.PULL_SUCCESS;
}
@@ -2890,12 +2619,8 @@ public class StatsPullAtomService extends SystemService {
if (bs.drainType == bs.drainType.USER) {
continue; // This is not supported. We purposefully calculate over USER_ALL.
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(bs.drainType.ordinal())
- .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, bs.drainType.ordinal(), milliAmpHrsToNanoAmpSecs(bs.totalPowerMah)));
}
return StatsManager.PULL_SUCCESS;
}
@@ -2918,28 +2643,16 @@ public class StatsPullAtomService extends SystemService {
final long clockDiffMillis = mDebugElapsedClockPreviousValue == 0
? 0 : elapsedMillis - mDebugElapsedClockPreviousValue;
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(mDebugElapsedClockPullCount)
- .writeLong(elapsedMillis)
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, mDebugElapsedClockPullCount,
+ elapsedMillis,
// Log it twice to be able to test multi-value aggregation from ValueMetric.
- .writeLong(elapsedMillis)
- .writeLong(clockDiffMillis)
- .writeInt(1 /* always set */)
- .build();
- pulledData.add(e);
+ elapsedMillis, clockDiffMillis, 1 /* always set */));
if (mDebugElapsedClockPullCount % 2 == 1) {
- StatsEvent e2 = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(mDebugElapsedClockPullCount)
- .writeLong(elapsedMillis)
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, mDebugElapsedClockPullCount,
+ elapsedMillis,
// Log it twice to be able to test multi-value aggregation from ValueMetric.
- .writeLong(elapsedMillis)
- .writeLong(clockDiffMillis)
- .writeInt(2 /* set on odd pulls */)
- .build();
- pulledData.add(e2);
+ elapsedMillis, clockDiffMillis, 2 /* set on odd pulls */));
}
mDebugElapsedClockPullCount++;
@@ -2969,16 +2682,13 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SKIP;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeLong(mDebugFailingElapsedClockPullCount)
- .writeLong(elapsedMillis)
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ mDebugFailingElapsedClockPullCount, elapsedMillis,
// Log it twice to be able to test multi-value aggregation from ValueMetric.
- .writeLong(elapsedMillis)
- .writeLong(mDebugFailingElapsedClockPreviousValue == 0
- ? 0 : elapsedMillis - mDebugFailingElapsedClockPreviousValue)
- .build();
- pulledData.add(e);
+ elapsedMillis,
+ mDebugFailingElapsedClockPreviousValue == 0
+ ? 0
+ : elapsedMillis - mDebugFailingElapsedClockPreviousValue));
mDebugFailingElapsedClockPreviousValue = elapsedMillis;
return StatsManager.PULL_SUCCESS;
@@ -2995,19 +2705,9 @@ public class StatsPullAtomService extends SystemService {
}
int pullBuildInformationLocked(int atomTag, List<StatsEvent> pulledData) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeString(Build.FINGERPRINT)
- .writeString(Build.BRAND)
- .writeString(Build.PRODUCT)
- .writeString(Build.DEVICE)
- .writeString(Build.VERSION.RELEASE_OR_CODENAME)
- .writeString(Build.ID)
- .writeString(Build.VERSION.INCREMENTAL)
- .writeString(Build.TYPE)
- .writeString(Build.TAGS)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, Build.FINGERPRINT, Build.BRAND,
+ Build.PRODUCT, Build.DEVICE, Build.VERSION.RELEASE_OR_CODENAME, Build.ID,
+ Build.VERSION.INCREMENTAL, Build.TYPE, Build.TAGS));
return StatsManager.PULL_SUCCESS;
}
@@ -3053,14 +2753,8 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SKIP;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(pkg.applicationInfo.uid)
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeString(holderName)
- .writeString(roleName)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, pkg.applicationInfo.uid, holderName, roleName));
}
}
}
@@ -3138,19 +2832,24 @@ public class StatsPullAtomService extends SystemService {
permName = permName.substring(COMMON_PERMISSION_PREFIX.length());
}
- StatsEvent.Builder e = StatsEvent.newBuilder();
- e.setAtomId(atomTag);
- e.writeString(permName);
- e.writeInt(pkg.applicationInfo.uid);
- e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
+ StatsEvent e;
if (atomTag == FrameworkStatsLog.DANGEROUS_PERMISSION_STATE) {
- e.writeString("");
+ e = FrameworkStatsLog.buildStatsEvent(atomTag, permName,
+ pkg.applicationInfo.uid, "",
+ (pkg.requestedPermissionsFlags[permNum]
+ & REQUESTED_PERMISSION_GRANTED)
+ != 0,
+ permissionFlags);
+ } else {
+ // DangerousPermissionStateSampled atom.
+ e = FrameworkStatsLog.buildStatsEvent(atomTag, permName,
+ pkg.applicationInfo.uid,
+ (pkg.requestedPermissionsFlags[permNum]
+ & REQUESTED_PERMISSION_GRANTED)
+ != 0,
+ permissionFlags);
}
- e.writeBoolean((pkg.requestedPermissionsFlags[permNum]
- & REQUESTED_PERMISSION_GRANTED) != 0);
- e.writeInt(permissionFlags);
-
- pulledData.add(e.build());
+ pulledData.add(e);
}
}
}
@@ -3182,11 +2881,7 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SKIP;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeString(tzDbVersion)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, tzDbVersion));
return StatsManager.PULL_SUCCESS;
}
@@ -3228,13 +2923,8 @@ public class StatsPullAtomService extends SystemService {
externalStorageType = StorageEnums.OTHER;
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(externalStorageType)
- .writeInt(volumeType)
- .writeLong(diskInfo.size)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, externalStorageType, volumeType, diskInfo.size));
}
}
return StatsManager.PULL_SUCCESS;
@@ -3285,12 +2975,8 @@ public class StatsPullAtomService extends SystemService {
// App is installed on external storage.
if (externalStorageType != -1) {
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(externalStorageType)
- .writeString(appInfo.packageName)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, externalStorageType, appInfo.packageName));
}
}
return StatsManager.PULL_SUCCESS;
@@ -3333,16 +3019,10 @@ public class StatsPullAtomService extends SystemService {
mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED, 1, userId);
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeBoolean(unlockKeyguardEnabled != 0)
- .writeBoolean(unlockDismissesKeyguard != 0)
- .writeBoolean(unlockAttentionRequired != 0)
- .writeBoolean(unlockAppEnabled != 0)
- .writeBoolean(unlockAlwaysRequireConfirmation != 0)
- .writeBoolean(unlockDiversityRequired != 0)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ unlockKeyguardEnabled != 0, unlockDismissesKeyguard != 0,
+ unlockAttentionRequired != 0, unlockAppEnabled != 0,
+ unlockAlwaysRequireConfirmation != 0, unlockDiversityRequired != 0));
}
} finally {
Binder.restoreCallingIdentity(callingToken);
@@ -3423,27 +3103,29 @@ public class StatsPullAtomService extends SystemService {
if (entry.mHash >= samplingRate) {
continue;
}
- StatsEvent.Builder e = StatsEvent.newBuilder();
- e.setAtomId(atomTag);
- e.writeInt(entry.mUid);
- e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- e.writeString(entry.mPackageName);
+ StatsEvent e;
if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
- e.writeString(entry.mAttributionTag);
- }
- e.writeInt(entry.mOp.getOpCode());
- e.writeLong(entry.mOp.getForegroundAccessCount(OP_FLAGS_PULLED));
- e.writeLong(entry.mOp.getBackgroundAccessCount(OP_FLAGS_PULLED));
- e.writeLong(entry.mOp.getForegroundRejectCount(OP_FLAGS_PULLED));
- e.writeLong(entry.mOp.getBackgroundRejectCount(OP_FLAGS_PULLED));
- e.writeLong(entry.mOp.getForegroundAccessDuration(OP_FLAGS_PULLED));
- e.writeLong(entry.mOp.getBackgroundAccessDuration(OP_FLAGS_PULLED));
- e.writeBoolean(mDangerousAppOpsList.contains(entry.mOp.getOpCode()));
-
- if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
- e.writeInt(samplingRate);
+ e = FrameworkStatsLog.buildStatsEvent(atomTag, entry.mUid, entry.mPackageName,
+ entry.mAttributionTag, entry.mOp.getOpCode(),
+ entry.mOp.getForegroundAccessCount(OP_FLAGS_PULLED),
+ entry.mOp.getBackgroundAccessCount(OP_FLAGS_PULLED),
+ entry.mOp.getForegroundRejectCount(OP_FLAGS_PULLED),
+ entry.mOp.getBackgroundRejectCount(OP_FLAGS_PULLED),
+ entry.mOp.getForegroundAccessDuration(OP_FLAGS_PULLED),
+ entry.mOp.getBackgroundAccessDuration(OP_FLAGS_PULLED),
+ mDangerousAppOpsList.contains(entry.mOp.getOpCode()), samplingRate);
+ } else {
+ // AppOps atom.
+ e = FrameworkStatsLog.buildStatsEvent(atomTag, entry.mUid, entry.mPackageName,
+ entry.mOp.getOpCode(), entry.mOp.getForegroundAccessCount(OP_FLAGS_PULLED),
+ entry.mOp.getBackgroundAccessCount(OP_FLAGS_PULLED),
+ entry.mOp.getForegroundRejectCount(OP_FLAGS_PULLED),
+ entry.mOp.getBackgroundRejectCount(OP_FLAGS_PULLED),
+ entry.mOp.getForegroundAccessDuration(OP_FLAGS_PULLED),
+ entry.mOp.getBackgroundAccessDuration(OP_FLAGS_PULLED),
+ mDangerousAppOpsList.contains(entry.mOp.getOpCode()));
}
- pulledData.add(e.build());
+ pulledData.add(e);
}
if (pulledData.size() > DIMENSION_KEY_SIZE_HARD_LIMIT) {
int adjustedSamplingRate = constrain(
@@ -3604,22 +3286,11 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- StatsEvent.Builder e = StatsEvent.newBuilder();
- e.setAtomId(atomTag);
- e.writeInt(message.getUid());
- e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- e.writeString(message.getPackageName());
- e.writeString("");
- if (message.getAttributionTag() == null) {
- e.writeString("");
- } else {
- e.writeString(message.getAttributionTag());
- }
- e.writeString(message.getMessage());
- e.writeInt(message.getSamplingStrategy());
- e.writeInt(AppOpsManager.strOpToOp(message.getOp()));
-
- pulledData.add(e.build());
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, message.getUid(),
+ message.getPackageName(), "",
+ message.getAttributionTag() == null ? "" : message.getAttributionTag(),
+ message.getMessage(), message.getSamplingStrategy(),
+ AppOpsManager.strOpToOp(message.getOp())));
} catch (Throwable t) {
// TODO: catch exceptions at a more granular level
Slog.e(TAG, "Could not read runtime appop access message", t);
@@ -3635,11 +3306,7 @@ public class StatsPullAtomService extends SystemService {
InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0));
int[] len = new int[1];
byte[] stats = readFully(stream, len);
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeByteArray(Arrays.copyOf(stats, len[0]))
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, Arrays.copyOf(stats, len[0])));
}
static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
@@ -3802,11 +3469,7 @@ public class StatsPullAtomService extends SystemService {
throw new IllegalStateException("Invalid atomTag in healthHal puller: "
+ atomTag);
}
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeInt(pulledValue)
- .build();
- pulledData.add(e);
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue));
});
} catch (RemoteException | IllegalStateException e) {
return StatsManager.PULL_SKIP;
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 734b71824490..de06c92e8dae 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -49,11 +49,8 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.SystemService;
-import com.android.server.pm.InstructionSets;
import com.android.server.pm.PackageManagerService;
-import dalvik.system.VMRuntime;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -214,7 +211,7 @@ public class DeviceStorageMonitorService extends SystemService {
newLevel = State.LEVEL_FULL;
} else if (usableBytes <= lowBytes) {
newLevel = State.LEVEL_LOW;
- } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
+ } else if (StorageManager.UUID_DEFAULT.equals(uuid)
&& usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
newLevel = State.LEVEL_LOW;
} else {
@@ -261,15 +258,6 @@ public class DeviceStorageMonitorService extends SystemService {
};
}
- private static boolean isBootImageOnDisk() {
- for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
- if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
- return false;
- }
- }
- return true;
- }
-
@Override
public void onStart() {
final Context context = getContext();
@@ -481,15 +469,8 @@ public class DeviceStorageMonitorService extends SystemService {
final CharSequence title = context.getText(
com.android.internal.R.string.low_internal_storage_view_title);
- final CharSequence details;
- if (StorageManager.UUID_DEFAULT.equals(uuid)) {
- details = context.getText(isBootImageOnDisk()
- ? com.android.internal.R.string.low_internal_storage_view_text
- : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
- } else {
- details = context.getText(
- com.android.internal.R.string.low_internal_storage_view_text);
- }
+ final CharSequence details = context.getText(
+ com.android.internal.R.string.low_internal_storage_view_text);
PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
null, UserHandle.CURRENT);
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 37df5481d3c4..6dc1d6921dbb 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -53,16 +53,14 @@ public final class StorageSessionController {
private final Context mContext;
@GuardedBy("mLock")
private final SparseArray<StorageUserConnection> mConnections = new SparseArray<>();
- private final boolean mIsFuseEnabled;
private volatile ComponentName mExternalStorageServiceComponent;
private volatile String mExternalStorageServicePackageName;
private volatile int mExternalStorageServiceAppId;
private volatile boolean mIsResetting;
- public StorageSessionController(Context context, boolean isFuseEnabled) {
+ public StorageSessionController(Context context) {
mContext = Objects.requireNonNull(context);
- mIsFuseEnabled = isFuseEnabled;
}
/**
@@ -361,6 +359,6 @@ public final class StorageSessionController {
}
private boolean shouldHandle(@Nullable VolumeInfo vol) {
- return mIsFuseEnabled && !mIsResetting && (vol == null || isEmulatedOrPublic(vol));
+ return !mIsResetting && (vol == null || isEmulatedOrPublic(vol));
}
}
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index ed5706752cb2..361a5067b9bf 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -34,6 +34,7 @@ import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.RemoteCallback;
import android.os.UserHandle;
+import android.os.UserManagerInternal;
import android.os.storage.StorageManagerInternal;
import android.os.storage.StorageVolume;
import android.service.storage.ExternalStorageService;
@@ -62,19 +63,25 @@ import java.util.concurrent.TimeoutException;
public final class StorageUserConnection {
private static final String TAG = "StorageUserConnection";
- public static final int REMOTE_TIMEOUT_SECONDS = 20;
+ private static final int DEFAULT_REMOTE_TIMEOUT_SECONDS = 20;
+ // TODO(b/161702661): Workaround for demo user to have shorter timeout.
+ // This allows the DevicePolicyManagerService#enableSystemApp call to succeed without ANR.
+ private static final int DEMO_USER_REMOTE_TIMEOUT_SECONDS = 5;
private final Object mLock = new Object();
private final Context mContext;
private final int mUserId;
private final StorageSessionController mSessionController;
private final ActiveConnection mActiveConnection = new ActiveConnection();
+ private final boolean mIsDemoUser;
@GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
mContext = Objects.requireNonNull(context);
mUserId = Preconditions.checkArgumentNonnegative(userId);
mSessionController = controller;
+ mIsDemoUser = LocalServices.getService(UserManagerInternal.class)
+ .getUserInfo(userId).isDemo();
}
/**
@@ -200,7 +207,8 @@ public final class StorageUserConnection {
private void waitForLatch(CountDownLatch latch, String reason) throws TimeoutException {
try {
- if (!latch.await(REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+ if (!latch.await(mIsDemoUser ? DEMO_USER_REMOTE_TIMEOUT_SECONDS
+ : DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
// TODO(b/140025078): Call ActivityManager ANR API?
Slog.wtf(TAG, "Failed to bind to the ExternalStorageService for user " + mUserId);
throw new TimeoutException("Latch wait for " + reason + " elapsed");
diff --git a/services/core/java/com/android/server/textclassifier/IconsContentProvider.java b/services/core/java/com/android/server/textclassifier/IconsContentProvider.java
index 9b3176d9df67..183e920e4620 100644
--- a/services/core/java/com/android/server/textclassifier/IconsContentProvider.java
+++ b/services/core/java/com/android/server/textclassifier/IconsContentProvider.java
@@ -27,6 +27,7 @@ import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.os.UserHandle;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.textclassifier.IconsUriHelper.ResourceInfo;
@@ -34,6 +35,7 @@ import com.android.server.textclassifier.IconsUriHelper.ResourceInfo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Arrays;
/**
* A content provider that is used to access icons returned from the TextClassifier service.
@@ -46,32 +48,40 @@ import java.io.OutputStream;
public final class IconsContentProvider extends ContentProvider {
private static final String TAG = "IconsContentProvider";
+ private static final String MIME_TYPE = "image/png";
+
+ private final PipeDataWriter<Pair<ResourceInfo, Integer>> mWriter =
+ (writeSide, uri, mimeType, bundle, args) -> {
+ try (OutputStream out = new AutoCloseOutputStream(writeSide)) {
+ final ResourceInfo res = args.first;
+ final int userId = args.second;
+ final Drawable drawable = Icon.createWithResource(res.packageName, res.id)
+ .loadDrawableAsUser(getContext(), userId);
+ getBitmap(drawable).compress(Bitmap.CompressFormat.PNG, 100, out);
+ } catch (Exception e) {
+ Log.e(TAG, "Error retrieving icon for uri: " + uri, e);
+ }
+ };
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) {
+ final ResourceInfo res = IconsUriHelper.getInstance().getResourceInfo(uri);
+ if (res == null) {
+ Log.e(TAG, "No icon found for uri: " + uri);
+ return null;
+ }
+
try {
- final ResourceInfo res = IconsUriHelper.getInstance().getResourceInfo(uri);
- final Drawable drawable = Icon.createWithResource(res.packageName, res.id)
- .loadDrawableAsUser(getContext(), UserHandle.getCallingUserId());
- final byte[] data = getBitmapData(drawable);
- final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- final ParcelFileDescriptor readSide = pipe[0];
- final ParcelFileDescriptor writeSide = pipe[1];
- try (OutputStream out = new AutoCloseOutputStream(writeSide)) {
- out.write(data);
- return readSide;
- }
- } catch (IOException | RuntimeException e) {
- Log.e(TAG, "Error retrieving icon for uri: " + uri, e);
+ final Pair<ResourceInfo, Integer> args = new Pair(res, UserHandle.getCallingUserId());
+ return openPipeHelper(uri, MIME_TYPE, /* bundle= */ null, args, mWriter);
+ } catch (IOException e) {
+ Log.e(TAG, "Error opening pipe helper for icon at uri: " + uri, e);
}
+
return null;
}
- /**
- * Returns the bitmap data for the specified drawable.
- */
- @VisibleForTesting
- public static byte[] getBitmapData(Drawable drawable) {
+ private static Bitmap getBitmap(Drawable drawable) {
if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
throw new IllegalStateException("The icon is zero-sized");
}
@@ -85,16 +95,24 @@ public final class IconsContentProvider extends ContentProvider {
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
- final ByteArrayOutputStream stream = new ByteArrayOutputStream();
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
- final byte[] byteArray = stream.toByteArray();
- bitmap.recycle();
- return byteArray;
+ return bitmap;
+ }
+
+ /**
+ * Returns true if the drawables are considered the same.
+ */
+ @VisibleForTesting
+ public static boolean sameIcon(Drawable one, Drawable two) {
+ final ByteArrayOutputStream stream1 = new ByteArrayOutputStream();
+ getBitmap(one).compress(Bitmap.CompressFormat.PNG, 100, stream1);
+ final ByteArrayOutputStream stream2 = new ByteArrayOutputStream();
+ getBitmap(two).compress(Bitmap.CompressFormat.PNG, 100, stream2);
+ return Arrays.equals(stream1.toByteArray(), stream2.toByteArray());
}
@Override
public String getType(Uri uri) {
- return "image/png";
+ return MIME_TYPE;
}
@Override
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 32f8da8d4a77..3406bd99c883 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -28,6 +28,7 @@ import android.database.ContentObserver;
import android.os.Binder;
import android.os.Handler;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
@@ -139,7 +140,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- mTimeDetectorStrategy.dump(pw, args);
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ mTimeDetectorStrategy.dump(ipw, args);
+ ipw.flush();
}
private void enforceSuggestTelephonyTimePermission() {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 9a39d2418994..e943978cfc14 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -17,25 +17,25 @@
package com.android.server.timedetector;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.os.TimestampedValue;
+import android.util.IndentingPrintWriter;
-import java.io.PrintWriter;
+import com.android.server.timezonedetector.Dumpable;
/**
* The interface for the class that implements the time detection algorithm used by the
* {@link TimeDetectorService}.
*
* <p>Most calls will be handled by a single thread but that is not true for all calls. For example
- * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
- * handle thread safety.
+ * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread so
+ * implementations must handle thread safety.
*
* @hide
*/
-public interface TimeDetectorStrategy {
+public interface TimeDetectorStrategy extends Dumpable {
/**
* The interface used by the strategy to interact with the surrounding service.
@@ -94,9 +94,6 @@ public interface TimeDetectorStrategy {
/** Handle the auto-time setting being toggled on or off. */
void handleAutoTimeDetectionChanged();
- /** Dump debug information. */
- void dump(@NonNull PrintWriter pw, @Nullable String[] args);
-
// Utility methods below are to be moved to a better home when one becomes more obvious.
/**
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index ceb548551635..fe0e82e66093 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -24,16 +24,15 @@ import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.os.TimestampedValue;
+import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.server.timezonedetector.ArrayMapWithHistory;
import com.android.server.timezonedetector.ReferenceWithHistory;
-import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -203,8 +202,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
}
@Override
- public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ public synchronized void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
ipw.println("TimeDetectorStrategy:");
ipw.increaseIndent(); // level 1
@@ -232,7 +230,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
ipw.decreaseIndent(); // level 2
ipw.decreaseIndent(); // level 1
- ipw.flush();
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index bbd1ae60cb62..fd5c6e91a885 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -37,6 +37,10 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.i18n.timezone.TimeZoneDataFiles;
+import com.android.i18n.timezone.TimeZoneFinder;
+import com.android.i18n.timezone.TzDataSetVersion;
+import com.android.i18n.timezone.ZoneInfoDb;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.EventLogTags;
import com.android.server.SystemService;
@@ -46,10 +50,6 @@ import com.android.timezone.distro.StagedDistroOperation;
import com.android.timezone.distro.TimeZoneDistro;
import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
-import libcore.timezone.TimeZoneDataFiles;
-import libcore.timezone.TimeZoneFinder;
-import libcore.timezone.TzDataSetVersion;
-import libcore.timezone.ZoneInfoDb;
import java.io.File;
import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java b/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java
index 3274f0e1112f..d6fdddf4ee43 100644
--- a/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java
+++ b/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java
@@ -20,10 +20,10 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
/**
* A partial decorator for {@link ArrayMap} that records historic values for each mapping for
diff --git a/services/core/java/com/android/server/timezonedetector/Dumpable.java b/services/core/java/com/android/server/timezonedetector/Dumpable.java
new file mode 100644
index 000000000000..5603c38bd0ae
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/Dumpable.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+
+/** An interface for components that can write their internal state to dumpsys logs. */
+public interface Dumpable {
+
+ /** Dump internal state. */
+ void dump(@NonNull IndentingPrintWriter pw, @Nullable String[] args);
+
+ /**
+ * An interface that can be used expose when one component allows another to be registered so
+ * that it is dumped at the same time.
+ */
+ interface Container {
+
+ /**
+ * Registers the supplied {@link Dumpable}. When the implementation is dumped
+ * {@link Dumpable#dump(IndentingPrintWriter, String[])} should be called on the
+ * {@code dumpable}.
+ */
+ void addDumpable(@NonNull Dumpable dumpable);
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
new file mode 100644
index 000000000000..118899add968
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringTokenizer;
+
+/**
+ * A time zone suggestion from a geolocation source.
+ *
+ * <p> Geolocation-based suggestions have the following properties:
+ *
+ * <ul>
+ * <li>{@code zoneIds}. When not {@code null}, {@code zoneIds} contains a list of suggested time
+ * zone IDs, e.g. ["America/Phoenix", "America/Denver"]. Usually there will be a single zoneId.
+ * When there are multiple, this indicates multiple answers are possible for the current
+ * location / accuracy, i.e. if there is a nearby time zone border. The detection logic
+ * receiving the suggestion is expected to use the first element in the absence of other
+ * information, but one of the others may be used if there is supporting evidence / preferences
+ * such as a device setting or corroborating signals from another source.
+ * <br />{@code zoneIds} can be empty if the current location has been determined to have no
+ * time zone. For example, oceans or disputed areas. This is considered a strong signal and the
+ * received need not look for time zone from other sources.
+ * <br />{@code zoneIds} can be {@code null} to indicate that the geolocation source has entered
+ * an "un-opinionated" state and any previous suggestion is being withdrawn. This indicates the
+ * source cannot provide a valid suggestion due to technical limitations. For example, a
+ * geolocation source may become un-opinionated if the device's location is no longer known with
+ * sufficient accuracy, or if the location is known but no time zone can be determined because
+ * no time zone mapping information is available.</li>
+ * <li>{@code debugInfo} contains debugging metadata associated with the suggestion. This is
+ * used to record why the suggestion exists and how it was obtained. This information exists
+ * only to aid in debugging and therefore is used by {@link #toString()}, but it is not for use
+ * in detection logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
+ * </li>
+ * </ul>
+ *
+ * @hide
+ */
+public final class GeolocationTimeZoneSuggestion {
+
+ @NonNull private final List<String> mZoneIds;
+ @Nullable private ArrayList<String> mDebugInfo;
+
+ public GeolocationTimeZoneSuggestion(@Nullable List<String> zoneIds) {
+ if (zoneIds == null) {
+ // Unopinionated
+ mZoneIds = null;
+ } else {
+ mZoneIds = Collections.unmodifiableList(new ArrayList<>(zoneIds));
+ }
+ }
+
+ /**
+ * Returns the zone Ids being suggested. See {@link GeolocationTimeZoneSuggestion} for details.
+ */
+ @Nullable
+ public List<String> getZoneIds() {
+ return mZoneIds;
+ }
+
+ /** Returns debug information. See {@link GeolocationTimeZoneSuggestion} for details. */
+ @NonNull
+ public List<String> getDebugInfo() {
+ return mDebugInfo == null
+ ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+ }
+
+ /**
+ * Associates information with the instance that can be useful for debugging / logging. The
+ * information is present in {@link #toString()} but is not considered for
+ * {@link #equals(Object)} and {@link #hashCode()}.
+ */
+ public void addDebugInfo(String... debugInfos) {
+ if (mDebugInfo == null) {
+ mDebugInfo = new ArrayList<>();
+ }
+ mDebugInfo.addAll(Arrays.asList(debugInfos));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ GeolocationTimeZoneSuggestion
+ that = (GeolocationTimeZoneSuggestion) o;
+ return Objects.equals(mZoneIds, that.mZoneIds);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mZoneIds);
+ }
+
+ @Override
+ public String toString() {
+ return "GeolocationTimeZoneSuggestion{"
+ + "mZoneIds=" + mZoneIds
+ + ", mDebugInfo=" + mDebugInfo
+ + '}';
+ }
+
+ /** @hide */
+ public static GeolocationTimeZoneSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) {
+ String zoneIdsString = null;
+ String opt;
+ while ((opt = cmd.getNextArg()) != null) {
+ switch (opt) {
+ case "--zone_ids": {
+ zoneIdsString = cmd.getNextArgRequired();
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+ }
+ List<String> zoneIds = parseZoneIdsArg(zoneIdsString);
+ GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(zoneIds);
+ suggestion.addDebugInfo("Command line injection");
+ return suggestion;
+ }
+
+ private static List<String> parseZoneIdsArg(String zoneIdsString) {
+ if ("UNCERTAIN".equals(zoneIdsString)) {
+ return null;
+ } else if ("EMPTY".equals(zoneIdsString)) {
+ return Collections.emptyList();
+ } else {
+ ArrayList<String> zoneIds = new ArrayList<>();
+ StringTokenizer tokenizer = new StringTokenizer(zoneIdsString, ",");
+ while (tokenizer.hasMoreTokens()) {
+ zoneIds.add(tokenizer.nextToken());
+ }
+ return zoneIds;
+ }
+ }
+
+ /** @hide */
+ public static void printCommandLineOpts(@NonNull PrintWriter pw) {
+ pw.println("Geolocation suggestion options:");
+ pw.println(" --zone_ids {UNCERTAIN|EMPTY|<Olson ID>+}");
+ pw.println();
+ pw.println("See " + GeolocationTimeZoneSuggestion.class.getName()
+ + " for more information");
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
index 165419a7a38b..b63df05ad7a1 100644
--- a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
+++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
@@ -19,8 +19,7 @@ package com.android.server.timezonedetector;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-
-import com.android.internal.util.IndentingPrintWriter;
+import android.util.IndentingPrintWriter;
import java.util.ArrayDeque;
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
new file mode 100644
index 000000000000..3d9ec6475a8b
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import android.annotation.NonNull;
+
+/**
+ * The internal (in-process) system server API for the {@link
+ * com.android.server.timezonedetector.TimeZoneDetectorService}.
+ *
+ * @hide
+ */
+public interface TimeZoneDetectorInternal extends Dumpable.Container {
+
+ /**
+ * Suggests the current time zone, determined using geolocation, to the detector. The
+ * detector may ignore the signal based on system settings, whether better information is
+ * available, and so on.
+ */
+ void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion);
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
new file mode 100644
index 000000000000..4464f7d136e3
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * The real {@link TimeZoneDetectorInternal} local service implementation.
+ *
+ * @hide
+ */
+public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInternal {
+
+ @NonNull private final Context mContext;
+ @NonNull private final Handler mHandler;
+ @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
+
+ static TimeZoneDetectorInternalImpl create(@NonNull Context context, @NonNull Handler handler,
+ @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
+ return new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy);
+ }
+
+ @VisibleForTesting
+ public TimeZoneDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler,
+ @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
+ mContext = Objects.requireNonNull(context);
+ mHandler = Objects.requireNonNull(handler);
+ mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy);
+ }
+
+ @Override
+ public void addDumpable(@NonNull Dumpable dumpable) {
+ mTimeZoneDetectorStrategy.addDumpable(dumpable);
+ }
+
+ @Override
+ public void suggestGeolocationTimeZone(
+ @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
+ Objects.requireNonNull(timeZoneSuggestion);
+
+ mHandler.post(
+ () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index aa83bf4c8b0b..3ec61fdda917 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -36,6 +36,7 @@ import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -48,6 +49,7 @@ import com.android.server.timezonedetector.TimeZoneDetectorStrategy.StrategyList
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.Objects;
/**
@@ -63,9 +65,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
private static final String TAG = "TimeZoneDetectorService";
/**
- * Handles the lifecycle for {@link TimeZoneDetectorService}.
+ * Handles the service lifecycle for {@link TimeZoneDetectorService} and
+ * {@link TimeZoneDetectorInternalImpl}.
*/
- public static class Lifecycle extends SystemService {
+ public static final class Lifecycle extends SystemService {
public Lifecycle(@NonNull Context context) {
super(context);
@@ -73,10 +76,21 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
@Override
public void onStart() {
- TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext());
+ // Obtain / create the shared dependencies.
+ Context context = getContext();
+ Handler handler = FgThread.getHandler();
+ TimeZoneDetectorStrategy timeZoneDetectorStrategy =
+ TimeZoneDetectorStrategyImpl.create(context);
+
+ // Create and publish the local service for use by internal callers.
+ TimeZoneDetectorInternal internal =
+ TimeZoneDetectorInternalImpl.create(context, handler, timeZoneDetectorStrategy);
+ publishLocalService(TimeZoneDetectorInternal.class, internal);
// Publish the binder service so it can be accessed from other (appropriately
// permissioned) processes.
+ TimeZoneDetectorService service =
+ TimeZoneDetectorService.create(context, handler, timeZoneDetectorStrategy);
publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
}
}
@@ -94,11 +108,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
@NonNull
private final ArrayList<ConfigListenerInfo> mConfigurationListeners = new ArrayList<>();
- private static TimeZoneDetectorService create(@NonNull Context context) {
- final TimeZoneDetectorStrategy timeZoneDetectorStrategy =
- TimeZoneDetectorStrategyImpl.create(context);
+ private static TimeZoneDetectorService create(
+ @NonNull Context context, @NonNull Handler handler,
+ @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
- Handler handler = FgThread.getHandler();
TimeZoneDetectorService service =
new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
@@ -107,7 +120,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
new ContentObserver(handler) {
public void onChange(boolean selfChange) {
- service.handleAutoTimeZoneDetectionChanged();
+ service.handleAutoTimeZoneConfigChanged();
}
});
return service;
@@ -176,20 +189,14 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
int userId = UserHandle.getCallingUserId();
ConfigListenerInfo listenerInfo = new ConfigListenerInfo(userId, listener);
- final IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- synchronized (mConfigurationListeners) {
- Slog.i(TAG, "Configuration listener died: " + listenerInfo);
- mConfigurationListeners.remove(listenerInfo);
- }
- }
- };
synchronized (mConfigurationListeners) {
+ if (mConfigurationListeners.contains(listenerInfo)) {
+ return;
+ }
try {
- // Remove the record of the listener if the client process dies.
- listener.asBinder().linkToDeath(deathRecipient, 0 /* flags */);
+ // Ensure the reference to the listener is removed if the client process dies.
+ listenerInfo.linkToDeath();
// Only add the listener if we can linkToDeath().
mConfigurationListeners.add(listenerInfo);
@@ -199,6 +206,31 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
}
}
+ @Override
+ public void removeConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) {
+ enforceManageTimeZoneDetectorConfigurationPermission();
+ Objects.requireNonNull(listener);
+ int userId = UserHandle.getCallingUserId();
+
+ synchronized (mConfigurationListeners) {
+ ConfigListenerInfo toRemove = new ConfigListenerInfo(userId, listener);
+ Iterator<ConfigListenerInfo> listenerIterator = mConfigurationListeners.iterator();
+ while (listenerIterator.hasNext()) {
+ ConfigListenerInfo currentListenerInfo = listenerIterator.next();
+ if (currentListenerInfo.equals(toRemove)) {
+ listenerIterator.remove();
+
+ // Stop listening for the client process to die.
+ try {
+ currentListenerInfo.unlinkToDeath();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to unlinkToDeath() for listener=" + listener, e);
+ }
+ }
+ }
+ }
+ }
+
void handleConfigurationChanged() {
// Note: we could trigger an async time zone detection operation here via a call to
// handleAutoTimeZoneDetectionChanged(), but that is triggered in response to the underlying
@@ -224,6 +256,16 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
}
}
+ /** Provided for command-line access. This is not exposed as a binder API. */
+ void suggestGeolocationTimeZone(
+ @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
+ enforceSuggestGeolocationTimeZonePermission();
+ Objects.requireNonNull(timeZoneSuggestion);
+
+ mHandler.post(
+ () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
+ }
+
@Override
public boolean suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
enforceSuggestManualTimeZonePermission();
@@ -251,12 +293,14 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- mTimeZoneDetectorStrategy.dump(pw, args);
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ mTimeZoneDetectorStrategy.dump(ipw, args);
+ ipw.flush();
}
/** Internal method for handling the auto time zone configuration being changed. */
@VisibleForTesting
- public void handleAutoTimeZoneDetectionChanged() {
+ public void handleAutoTimeZoneConfigChanged() {
mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneConfigChanged);
}
@@ -267,6 +311,14 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
"manage time and time zone configuration");
}
+ private void enforceSuggestGeolocationTimeZonePermission() {
+ // The associated method is only used for the shell command interface, it's not possible to
+ // call it via Binder, and Shell currently can set the time zone directly anyway.
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SET_TIME_ZONE,
+ "suggest geolocation time zone");
+ }
+
private void enforceSuggestTelephonyTimeZonePermission() {
mContext.enforceCallingPermission(
android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE,
@@ -287,7 +339,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
this, in, out, err, args, callback, resultReceiver);
}
- private static class ConfigListenerInfo {
+ private class ConfigListenerInfo implements IBinder.DeathRecipient {
private final @UserIdInt int mUserId;
private final ITimeZoneConfigurationListener mListener;
@@ -305,6 +357,40 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
return mListener;
}
+ void linkToDeath() throws RemoteException {
+ mListener.asBinder().linkToDeath(this, 0 /* flags */);
+ }
+
+ void unlinkToDeath() throws RemoteException {
+ mListener.asBinder().unlinkToDeath(this, 0 /* flags */);
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mConfigurationListeners) {
+ Slog.i(TAG, "Configuration listener client died: " + this);
+ mConfigurationListeners.remove(this);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ConfigListenerInfo that = (ConfigListenerInfo) o;
+ return mUserId == that.mUserId
+ && mListener.equals(that.mListener);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUserId, mListener);
+ }
+
@Override
public String toString() {
return "ConfigListenerInfo{"
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index 8ff2a1b6364b..3230ef192b4b 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -20,6 +20,8 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.os.ShellCommand;
import java.io.PrintWriter;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
/** Implemented the shell command interface for {@link TimeZoneDetectorService}. */
class TimeZoneDetectorShellCommand extends ShellCommand {
@@ -37,50 +39,44 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
}
switch (cmd) {
- case "suggest_telephony_time_zone":
- return runSuggestTelephonyTimeZone();
+ case "suggest_geo_location_time_zone":
+ return runSuggestGeolocationTimeZone();
case "suggest_manual_time_zone":
return runSuggestManualTimeZone();
+ case "suggest_telephony_time_zone":
+ return runSuggestTelephonyTimeZone();
default: {
return handleDefaultCommands(cmd);
}
}
}
- private int runSuggestTelephonyTimeZone() {
- final PrintWriter pw = getOutPrintWriter();
- try {
- TelephonyTimeZoneSuggestion suggestion = null;
- String opt;
- while ((opt = getNextArg()) != null) {
- if ("--suggestion".equals(opt)) {
- suggestion = TelephonyTimeZoneSuggestion.parseCommandLineArg(this);
- } else {
- pw.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
- if (suggestion == null) {
- pw.println("Error: suggestion not specified");
- return 1;
- }
- mInterface.suggestTelephonyTimeZone(suggestion);
- pw.println("Suggestion " + suggestion + " injected.");
- return 0;
- } catch (RuntimeException e) {
- pw.println(e.toString());
- return 1;
- }
+ private int runSuggestGeolocationTimeZone() {
+ return runSuggestTimeZone(
+ () -> GeolocationTimeZoneSuggestion.parseCommandLineArg(this),
+ mInterface::suggestGeolocationTimeZone);
}
private int runSuggestManualTimeZone() {
+ return runSuggestTimeZone(
+ () -> ManualTimeZoneSuggestion.parseCommandLineArg(this),
+ mInterface::suggestManualTimeZone);
+ }
+
+ private int runSuggestTelephonyTimeZone() {
+ return runSuggestTimeZone(
+ () -> TelephonyTimeZoneSuggestion.parseCommandLineArg(this),
+ mInterface::suggestTelephonyTimeZone);
+ }
+
+ private <T> int runSuggestTimeZone(Supplier<T> suggestionParser, Consumer<T> invoker) {
final PrintWriter pw = getOutPrintWriter();
try {
- ManualTimeZoneSuggestion suggestion = null;
+ T suggestion = null;
String opt;
while ((opt = getNextArg()) != null) {
if ("--suggestion".equals(opt)) {
- suggestion = ManualTimeZoneSuggestion.parseCommandLineArg(this);
+ suggestion = suggestionParser.get();
} else {
pw.println("Error: Unknown option: " + opt);
return 1;
@@ -90,7 +86,7 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
pw.println("Error: suggestion not specified");
return 1;
}
- mInterface.suggestManualTimeZone(suggestion);
+ invoker.accept(suggestion);
pw.println("Suggestion " + suggestion + " injected.");
return 0;
} catch (RuntimeException e) {
@@ -105,10 +101,14 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
pw.println("Time Zone Detector (time_zone_detector) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" suggest_telephony_time_zone");
- pw.println(" --suggestion <telephony suggestion opts>");
+ pw.println(" suggest_geolocation_time_zone");
+ pw.println(" --suggestion <geolocation suggestion opts>");
pw.println(" suggest_manual_time_zone");
pw.println(" --suggestion <manual suggestion opts>");
+ pw.println(" suggest_telephony_time_zone");
+ pw.println(" --suggestion <telephony suggestion opts>");
+ pw.println();
+ GeolocationTimeZoneSuggestion.printCommandLineOpts(pw);
pw.println();
ManualTimeZoneSuggestion.printCommandLineOpts(pw);
pw.println();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 5ea8169e5b37..f947a6554412 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -21,8 +21,7 @@ import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.app.timezonedetector.TimeZoneCapabilities;
import android.app.timezonedetector.TimeZoneConfiguration;
-
-import java.io.PrintWriter;
+import android.util.IndentingPrintWriter;
/**
* The interface for the class that implements the time detection algorithm used by the
@@ -32,12 +31,12 @@ import java.io.PrintWriter;
* and what to set it to.
*
* <p>Most calls will be handled by a single thread but that is not true for all calls. For example
- * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
- * handle thread safety.
+ * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread so
+ * implementations mustvhandle thread safety.
*
* @hide
*/
-public interface TimeZoneDetectorStrategy {
+public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
/** A listener for strategy events. */
interface StrategyListener {
@@ -67,11 +66,13 @@ public interface TimeZoneDetectorStrategy {
@UserIdInt int userId, @NonNull TimeZoneConfiguration configuration);
/**
- * Suggests a time zone for the device, determined from the user's manually entered information.
- * Returns {@code false} if the suggestion was invalid, or the device configuration prevented
- * the suggestion being used, {@code true} if the suggestion was accepted. A suggestion that is
- * valid but does not change the time zone because it matches the current device time zone is
- * considered accepted.
+ * Suggests zero, one or more time zones for the device, or withdraws a previous suggestion if
+ * {@link GeolocationTimeZoneSuggestion#getZoneIds()} is {@code null}.
+ */
+ void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion suggestion);
+
+ /**
+ * Suggests a time zone for the device using manually-entered (i.e. user sourced) information.
*/
boolean suggestManualTimeZone(
@UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion);
@@ -89,9 +90,4 @@ public interface TimeZoneDetectorStrategy {
* Called when there has been a change to the automatic time zone detection configuration.
*/
void handleAutoTimeZoneConfigChanged();
-
- /**
- * Dumps internal state such as field values.
- */
- void dump(PrintWriter pw, String[] args);
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index c41b335578a3..9c36c3921e3e 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -32,14 +32,15 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.app.timezonedetector.TimeZoneCapabilities;
import android.app.timezonedetector.TimeZoneConfiguration;
import android.content.Context;
+import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -201,6 +202,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
mSuggestionBySlotIndex = new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+ @GuardedBy("this")
+ private final List<Dumpable> mDumpables = new ArrayList<>();
+
/**
* Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
*/
@@ -284,6 +288,16 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
}
@Override
+ public synchronized void suggestGeolocationTimeZone(
+ @NonNull GeolocationTimeZoneSuggestion suggestion) {
+ Objects.requireNonNull(suggestion);
+
+ // TODO Implement this.
+ throw new UnsupportedOperationException(
+ "Geo-location time zone detection is not currently implemented");
+ }
+
+ @Override
public synchronized boolean suggestManualTimeZone(
@UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion) {
Objects.requireNonNull(suggestion);
@@ -501,12 +515,16 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
}
}
+ @Override
+ public synchronized void addDumpable(@NonNull Dumpable dumpable) {
+ mDumpables.add(dumpable);
+ }
+
/**
* Dumps internal state such as field values.
*/
@Override
- public synchronized void dump(PrintWriter pw, String[] args) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ public synchronized void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
ipw.println("TimeZoneDetectorStrategy:");
ipw.increaseIndent(); // level 1
@@ -525,7 +543,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
mSuggestionBySlotIndex.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.decreaseIndent(); // level 1
- ipw.flush();
+
+ for (Dumpable dumpable : mDumpables) {
+ dumpable.dump(ipw, args);
+ }
}
/**
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 2394bafc09de..fd3c1f97df8b 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1469,7 +1469,7 @@ public class TrustManagerService extends SystemService {
if (userId > 0) {
return userId;
} else {
- Slog.wtf(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
+ Log.w(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
return -100;
}
}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 5f6323369d0a..f5e1602ee6be 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -51,6 +51,7 @@ import android.app.AppGlobals;
import android.app.GrantedUriPermission;
import android.app.IUriGrantsManager;
import android.content.ClipData;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -114,7 +115,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
private static final boolean DEBUG = false;
private static final String TAG = "UriGrantsManagerService";
// Maximum number of persisted Uri grants a package is allowed
- private static final int MAX_PERSISTED_URI_GRANTS = 128;
+ private static final int MAX_PERSISTED_URI_GRANTS = 512;
private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true;
private final Object mLock = new Object();
@@ -698,6 +699,11 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
final UriPermission perm = findOrCreateUriPermissionLocked(
sourcePkg, targetPkg, targetUid, grantUri);
perm.initPersistedModes(modeFlags, createdTime);
+ mPmInternal.grantImplicitAccess(
+ targetUserId, null,
+ UserHandle.getAppId(targetUid),
+ pi.applicationInfo.uid,
+ false /* direct */);
}
} else {
Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
@@ -1171,6 +1177,9 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
// grant, we can skip generating any bookkeeping; when any advanced
// features have been requested, we proceed below to make sure the
// provider supports granting permissions
+ mPmInternal.grantImplicitAccess(
+ UserHandle.getUserId(targetUid), null,
+ UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false);
return -1;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 55962fc883d9..90f87b16e70d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1160,9 +1160,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
};
- private Runnable mTryToRebindRunnable = () -> {
- tryToRebind();
- };
+ private Runnable mTryToRebindRunnable = this::tryToRebind;
WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
mInfo = info;
@@ -1295,14 +1293,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// a short time in the future, specifically to allow any pending package
// update message on this same looper thread to be processed.
if (!mWallpaper.wallpaperUpdating) {
- mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
+ mContext.getMainThreadHandler().postDelayed(mDisconnectRunnable,
1000);
}
}
}
}
- public void scheduleTimeoutLocked() {
+ private void scheduleTimeoutLocked() {
// If we didn't reset it right away, do so after we couldn't connect to
// it for an extended amount of time to avoid having a black wallpaper.
final Handler fgHandler = FgThread.getHandler();
@@ -1342,11 +1340,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private void processDisconnect(final ServiceConnection connection) {
+ private Runnable mDisconnectRunnable = () -> {
synchronized (mLock) {
// The wallpaper disappeared. If this isn't a system-default one, track
// crashes and fall back to default if it continues to misbehave.
- if (connection == mWallpaper.connection) {
+ if (this == mWallpaper.connection) {
final ComponentName wpService = mWallpaper.wallpaperComponent;
if (!mWallpaper.wallpaperUpdating
&& mWallpaper.userId == mCurrentUserId
@@ -1374,7 +1372,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
}
- }
+ };
/**
* Called by a live wallpaper if its colors have changed.
@@ -2786,6 +2784,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperConnection.DisplayConnector::disconnectLocked);
wallpaper.connection.mService = null;
wallpaper.connection.mDisplayConnector.clear();
+
+ FgThread.getHandler().removeCallbacks(wallpaper.connection.mResetRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(
+ wallpaper.connection.mDisconnectRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(
+ wallpaper.connection.mTryToRebindRunnable);
+
wallpaper.connection = null;
if (wallpaper == mLastWallpaper) mLastWallpaper = null;
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index a4304a96854a..ecba3f9c27c4 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -858,8 +858,7 @@ final class AccessibilityController {
mTempLayer = 0;
mDisplayContent.forAllWindows((w) -> {
if (w.isOnScreen() && w.isVisibleLw()
- && (w.mAttrs.alpha != 0)
- && !w.mWinAnimator.mEnterAnimationPending) {
+ && (w.mAttrs.alpha != 0)) {
mTempLayer++;
outWindows.put(mTempLayer, w);
}
@@ -896,6 +895,7 @@ final class AccessibilityController {
.setName(SURFACE_TITLE)
.setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
+ .setCallsite("ViewportWindow")
.build();
} catch (OutOfResourcesException oore) {
/* ignore */
@@ -1357,7 +1357,12 @@ final class AccessibilityController {
addedWindows.clear();
// Gets the top focused display Id and window token for supporting multi-display.
- topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
+ // If this top focused display is an embedded one, using its parent display as the
+ // top focused display.
+ final DisplayContent topFocusedDisplayContent =
+ mService.mRoot.getTopFocusedDisplayContent();
+ topFocusedDisplayId = isEmbeddedDisplay(topFocusedDisplayContent) ? mDisplayId
+ : topFocusedDisplayContent.getDisplayId();
topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
}
mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 189b21fb81a6..7565d8f9647c 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -396,8 +396,7 @@ class ActivityMetricsLogger {
mLastLogTimeSecs = now;
mWindowState = WINDOW_STATE_INVALID;
- ActivityStack stack =
- mSupervisor.mRootWindowContainer.getTopDisplayFocusedStack();
+ Task stack = mSupervisor.mRootWindowContainer.getTopDisplayFocusedStack();
if (stack == null) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 28977f9cd683..63ece0465993 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -116,7 +116,6 @@ import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT;
import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT;
import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
import static com.android.server.wm.ActivityRecordProto.FROZEN_BOUNDS;
-import static com.android.server.wm.ActivityRecordProto.IDENTIFIER;
import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
@@ -137,18 +136,6 @@ import static com.android.server.wm.ActivityRecordProto.VISIBLE;
import static com.android.server.wm.ActivityRecordProto.VISIBLE_REQUESTED;
import static com.android.server.wm.ActivityRecordProto.VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW;
import static com.android.server.wm.ActivityRecordProto.WINDOW_TOKEN;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
-import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESTARTING_PROCESS;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
@@ -193,6 +180,18 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+import static com.android.server.wm.Task.ActivityState.DESTROYED;
+import static com.android.server.wm.Task.ActivityState.DESTROYING;
+import static com.android.server.wm.Task.ActivityState.FINISHING;
+import static com.android.server.wm.Task.ActivityState.INITIALIZING;
+import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.Task.ActivityState.PAUSING;
+import static com.android.server.wm.Task.ActivityState.RESTARTING_PROCESS;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.Task.ActivityState.STARTED;
+import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.Task.ActivityState.STOPPING;
+import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -268,6 +267,7 @@ import android.os.storage.StorageManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
@@ -309,8 +309,8 @@ import com.android.server.protolog.common.ProtoLog;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
-import com.android.server.wm.ActivityStack.ActivityState;
import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.server.wm.Task.ActivityState;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.utils.InsetUtils;
@@ -658,6 +658,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// TODO: Have a WindowContainer state for tracking exiting/deferred removal.
boolean mIsExiting;
+ // Force an app transition to be ran in the case the visibility of the app did not change.
+ // We use this for the case of moving a Root Task to the back with multiple activities, and the
+ // top activity enters PIP; the bottom activity's visibility stays the same, but we need to
+ // run the transition.
+ boolean mRequestForceTransition;
boolean mEnteringAnimation;
@@ -1150,7 +1155,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void updateMultiWindowMode() {
- if (task == null || task.getStack() == null || !attachedToProcess()) {
+ if (task == null || task.getRootTask() == null || !attachedToProcess()) {
return;
}
@@ -1175,7 +1180,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
- if (task == null || task.getStack() == null || !attachedToProcess()) {
+ if (task == null || task.getRootTask() == null || !attachedToProcess()) {
return;
}
@@ -1216,8 +1221,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
this.task = task;
}
- ActivityStack getStack() {
- return task != null ? task.getStack() : null;
+ Task getStack() {
+ return task != null ? task.getRootTask() : null;
}
@Override
@@ -1264,10 +1269,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (getDisplayContent() != null) {
getDisplayContent().mClosingApps.remove(this);
}
- } else if (mLastParent != null && mLastParent.getStack() != null) {
- task.getStack().mExitingActivities.remove(this);
+ } else if (mLastParent != null && mLastParent.getRootTask() != null) {
+ task.getRootTask().mExitingActivities.remove(this);
}
- final ActivityStack stack = getStack();
+ final Task stack = getStack();
// If we reparent, make sure to remove ourselves from the old animation registry.
if (mAnimatingActivityRegistry != null) {
@@ -1400,6 +1405,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
}
+ /**
+ * @return {@code true} if there is a letterbox and any part of that letterbox overlaps with
+ * the given {@code rect}.
+ */
+ boolean isLetterboxOverlappingWith(Rect rect) {
+ return mLetterbox != null && mLetterbox.isOverlappingWith(rect);
+ }
+
static class Token extends IApplicationToken.Stub {
private WeakReference<ActivityRecord> weakActivity;
private final String name;
@@ -2054,23 +2067,28 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
static boolean canLaunchDreamActivity(String packageName) {
- final DreamManagerInternal dreamManager =
- LocalServices.getService(DreamManagerInternal.class);
-
- // Verify that the package is the current active dream. The getActiveDreamComponent()
- // call path does not acquire the DreamManager lock and thus is safe to use.
- final ComponentName activeDream = dreamManager.getActiveDreamComponent(false /* doze */);
- if (activeDream == null || activeDream.getPackageName() == null
- || !activeDream.getPackageName().equals(packageName)) {
+ if (packageName == null) {
return false;
}
- // Verify that the device is dreaming.
if (!LocalServices.getService(ActivityTaskManagerInternal.class).isDreaming()) {
return false;
}
- return true;
+ final DreamManagerInternal dreamManager =
+ LocalServices.getService(DreamManagerInternal.class);
+
+ // Verify that the package is the current active dream or doze component. The
+ // getActiveDreamComponent() call path does not acquire the DreamManager lock and thus
+ // is safe to use.
+ final ComponentName activeDream = dreamManager.getActiveDreamComponent(false /* doze */);
+ final ComponentName activeDoze = dreamManager.getActiveDreamComponent(true /* doze */);
+ return TextUtils.equals(packageName, getPackageName(activeDream))
+ || TextUtils.equals(packageName, getPackageName(activeDoze));
+ }
+
+ private static String getPackageName(ComponentName componentName) {
+ return componentName != null ? componentName.getPackageName() : null;
}
private void setActivityType(boolean componentSpecified, int launchedFromUid, Intent intent,
@@ -2107,8 +2125,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/** @return Root task of this activity, null if there is no task. */
- ActivityStack getRootTask() {
- return task != null ? (ActivityStack) task.getRootTask() : null;
+ Task getRootTask() {
+ return task != null ? task.getRootTask() : null;
}
int getRootTaskId() {
@@ -2116,7 +2134,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
DisplayContent getDisplay() {
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
return stack != null ? stack.getDisplay() : null;
}
@@ -2186,7 +2204,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
boolean isInStackLocked() {
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
return stack != null && stack.isInTask(this) != null;
}
@@ -2396,7 +2414,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (stack == null) {
Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: activity="
+ this + " task=" + task);
@@ -2528,7 +2546,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return FINISH_RESULT_CANCELLED;
}
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
final boolean mayAdjustTop = (isState(RESUMED) || stack.mResumedActivity == null)
&& stack.isFocusedStackOnDisplay();
final boolean shouldAdjustGlobalFocus = mayAdjustTop
@@ -2558,10 +2576,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// We are finishing the top focused activity and its task has nothing to be focused so
// the next focusable task should be focused.
- if (mayAdjustTop && ((ActivityStack) task).topRunningActivity(true /* focusableOnly */)
+ if (mayAdjustTop && task.topRunningActivity(true /* focusableOnly */)
== null) {
task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */,
- shouldAdjustGlobalFocus);
+ shouldAdjustGlobalFocus);
}
finishActivityResults(resultCode, resultData, resultGrants);
@@ -2581,7 +2599,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
}
- getDisplay().mDisplayContent.prepareAppTransition(transit, false);
+ mDisplayContent.prepareAppTransition(transit, false);
// When finishing the activity preemptively take the snapshot before the app window
// is marked as hidden and any configuration changes take place
@@ -2606,6 +2624,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (endTask) {
mAtmService.getLockTaskController().clearLockedTask(task);
+ // This activity was in the top focused stack and this is the last activity in
+ // that task, give this activity a higher layer so it can stay on top before the
+ // closing task transition be executed.
+ if (mayAdjustTop) {
+ mNeedsZBoost = true;
+ mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */);
+ }
}
} else if (!isState(PAUSING)) {
if (mVisibleRequested) {
@@ -2680,7 +2705,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean isCurrentVisible = mVisibleRequested || isState(PAUSED);
if (isCurrentVisible) {
- final ActivityStack stack = getStack();
+ final Task stack = getStack();
final ActivityRecord activity = stack.mResumedActivity;
boolean ensureVisibility = false;
if (activity != null && !activity.occludesParent()) {
@@ -2750,7 +2775,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Make sure the record is cleaned out of other places.
mStackSupervisor.mStoppingActivities.remove(this);
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
final TaskDisplayArea taskDisplayArea = getDisplayArea();
// TODO(b/137329632): Exclude current activity when looking for the next one with
// DisplayContent#topRunningActivity().
@@ -2910,7 +2935,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean safelyDestroy(String reason) {
if (isDestroyable()) {
if (DEBUG_SWITCH) {
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState()
+ " resumed=" + stack.mResumedActivity
+ " pausing=" + stack.mPausingActivity
@@ -2927,8 +2952,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
null /* resultData */, null /* resultGrants */);
makeFinishingLocked();
if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE) {
- Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack callers="
- + Debug.getCallers(5));
+ Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack, reason="
+ + reason + ", callers=" + Debug.getCallers(5));
}
takeFromHistory();
@@ -3052,6 +3077,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// from the client it-self to the parent surface (owned by us).
detachChildren();
+ clearAllDrawn();
+
mPendingRelaunchCount++;
}
@@ -3147,11 +3174,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
- boolean checkCompleteDeferredRemoval() {
+ boolean handleCompleteDeferredRemoval() {
if (mIsExiting) {
removeIfPossible();
}
- return super.checkCompleteDeferredRemoval();
+ return super.handleCompleteDeferredRemoval();
}
void onRemovedFromDisplay() {
@@ -3202,7 +3229,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
}
- final ActivityStack stack = getStack();
+ final Task stack = getStack();
if (delayed && !isEmpty()) {
// set the token aside because it has an active animation to be finished
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
@@ -3560,10 +3587,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
WindowState getImeTargetBelowWindow(WindowState w) {
final int index = mChildren.indexOf(w);
if (index > 0) {
- final WindowState target = mChildren.get(index - 1);
- if (target.canBeImeTarget()) {
- return target;
- }
+ return mChildren.get(index - 1)
+ .getWindow(WindowState::canBeImeTarget);
}
return null;
}
@@ -3718,7 +3743,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final boolean isSleeping() {
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
return stack != null ? stack.shouldSleepActivities() : mAtmService.isSleepingLocked();
}
@@ -4186,6 +4211,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mUseTransferredAnimation) {
return false;
}
+ // If it was set to true, reset the last request to force the transition.
+ mRequestForceTransition = false;
return super.applyAnimation(lp, transit, enter, isVoiceInteraction, sources);
}
@@ -4329,7 +4356,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// transition animation
// * or this is an opening app and windows are being replaced (e.g. freeform window to
// normal window).
- return isVisible() != visible || (!isVisible() && mIsExiting)
+ return isVisible() != visible || mRequestForceTransition || (!isVisible() && mIsExiting)
|| (visible && forAllWindows(WindowState::waitingForReplacement, true));
}
@@ -4551,7 +4578,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return visibleIgnoringKeyguard;
}
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (stack == null) {
return false;
}
@@ -4588,13 +4615,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
boolean shouldBeVisible() {
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (stack == null) {
return false;
}
- final boolean behindFullscreenActivity = stack.checkBehindFullscreenActivity(
- this, null /* handleBehindFullscreenActivity */);
+ final boolean behindFullscreenActivity = !stack.shouldBeVisible(null /* starting */)
+ || stack.getOccludingActivityAbove(this) != null;
return shouldBeVisible(behindFullscreenActivity, false /* ignoringKeyguard */);
}
@@ -4609,7 +4636,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If this activity is paused, tell it to now show its window.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Making visible and scheduling visibility: " + this);
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
try {
if (stack.mTranslucentActivityWaiting != null) {
updateOptionsLocked(returningOptions);
@@ -4886,7 +4913,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mStackSupervisor.reportResumedActivityLocked(this);
resumeKeyDispatchingLocked();
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
mStackSupervisor.mNoAnimActivities.clear();
// Mark the point when the activity is resuming
@@ -4914,7 +4941,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
"Activity paused: token=" + appToken + ", timeout=" + timeout);
- final ActivityStack stack = getStack();
+ final Task stack = getStack();
if (stack != null) {
removePauseTimeout();
@@ -4979,7 +5006,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void stopIfPossible() {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (isNoHistory()) {
if (!finishing) {
if (!stack.shouldSleepActivities()) {
@@ -5036,7 +5063,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void activityStopped(Bundle newIcicle, PersistableBundle newPersistentState,
CharSequence description) {
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
final boolean isStopping = mState == STOPPING;
if (!isStopping && mState != RESTARTING_PROCESS) {
Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
@@ -5086,7 +5113,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mStackSupervisor.mStoppingActivities.add(this);
}
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
// If we already have a few activities waiting to stop, then give up on things going idle
// and start clearing them out. Or if r is the last of activity of the last task the stack
// will be empty and must be cleared immediately.
@@ -5122,7 +5149,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (stack == null) {
return false;
}
@@ -5138,7 +5165,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void finishLaunchTickingLocked() {
launchTickTime = 0;
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (stack == null) {
return;
}
@@ -5569,7 +5596,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// First find the real culprit... if this activity has stopped, then the key dispatching
// timeout should not be caused by this.
if (stopped) {
- final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
+ final Task stack = mRootWindowContainer.getTopDisplayFocusedStack();
if (stack == null) {
return this;
}
@@ -5633,7 +5660,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return (r != null) ? r.getRootTask().isInTask(r) : null;
}
- static ActivityStack getStackLocked(IBinder token) {
+ static Task getStackLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
return r.getRootTask();
@@ -5646,7 +5673,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* {@link android.view.Display#INVALID_DISPLAY} if not attached.
*/
int getDisplayId() {
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (stack == null) {
return INVALID_DISPLAY;
}
@@ -5658,7 +5685,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// This would be redundant.
return false;
}
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (isState(RESUMED) || stack == null || this == stack.mPausingActivity || !mHaveState
|| !stopped) {
// We're not ready for this kind of thing.
@@ -5918,7 +5945,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.i(WM_DEBUG_APP_TRANSITIONS_ANIM, "Creating animation bounds layer");
final SurfaceControl.Builder builder = makeAnimationLeash()
.setParent(getAnimationLeashParent())
- .setName(getSurfaceControl() + " - animation-bounds");
+ .setName(getSurfaceControl() + " - animation-bounds")
+ .setCallsite("ActivityRecord.createAnimationBoundsLayer");
final SurfaceControl boundsLayer = builder.build();
t.show(boundsLayer);
return boundsLayer;
@@ -5979,7 +6007,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
getTransit(), task)) {
task.getBounds(mTmpRect);
} else {
- final ActivityStack stack = getStack();
+ final Task stack = getStack();
if (stack == null) {
return;
}
@@ -6113,7 +6141,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");
mTransit = TRANSIT_UNSET;
mTransitFlags = 0;
- mNeedsZBoost = false;
mNeedsAnimationBoundsLayer = false;
setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
@@ -6803,7 +6830,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void applyAspectRatio(Rect outBounds, Rect containingAppBounds,
Rect containingBounds) {
final float maxAspectRatio = info.maxAspectRatio;
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
final float minAspectRatio = info.minAspectRatio;
if (task == null || stack == null || (inMultiWindowMode() && !shouldUseSizeCompatMode())
@@ -6909,7 +6936,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
boolean ignoreVisibility) {
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (stack.mConfigWillChange) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check (will change): " + this);
@@ -7452,7 +7479,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* Determines whether this ActivityRecord can turn the screen on. It checks whether the flag
* {@link ActivityRecord#getTurnScreenOnFlag} is set and checks whether the ActivityRecord
- * should be visible depending on Keyguard state
+ * should be visible depending on Keyguard and window state.
*
* @return true if the screen can be turned on, false otherwise.
*/
@@ -7460,9 +7487,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (!getTurnScreenOnFlag()) {
return false;
}
- final ActivityStack stack = getRootTask();
- return stack != null &&
- stack.checkKeyguardVisibility(this, true /* shouldBeVisible */,
+ final Task stack = getRootTask();
+ return stack != null
+ && !stack.inMultiWindowMode()
+ && stack.checkKeyguardVisibility(this, true /* shouldBeVisible */,
stack.topRunningActivity() == this /* isTop */);
}
@@ -7504,22 +7532,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
- * @return {@code true} if this is the resumed activity on its current display, {@code false}
+ * @return {@code true} if this is the focused activity on its current display, {@code false}
* otherwise.
*/
- boolean isResumedActivityOnDisplay() {
+ boolean isFocusedActivityOnDisplay() {
final DisplayContent display = getDisplay();
if (display == null) {
return false;
}
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity();
- if (resumedActivity != null) {
- return resumedActivity == this;
- }
- }
- return false;
+ return display.forAllTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.getFocusedActivity() == this);
}
@@ -7606,7 +7628,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
bounds.dumpDebug(proto, FROZEN_BOUNDS);
}
- writeIdentifierToProto(proto, IDENTIFIER);
proto.write(STATE, mState.toString());
proto.write(FRONT_OF_TASK, isRootOfTask());
if (hasProcess()) {
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 5dfc261480f2..8540fa7cf347 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.Task.ActivityState.PAUSING;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
import android.util.ArraySet;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
deleted file mode 100644
index da64b2b45ad0..000000000000
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ /dev/null
@@ -1,3367 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
-import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
-import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
-import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
-import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
-import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
-import static com.android.server.wm.TaskProto.BOUNDS;
-import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
-import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
-import static com.android.server.wm.TaskProto.DISPLAY_ID;
-import static com.android.server.wm.TaskProto.FILLS_PARENT;
-import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.wm.TaskProto.MIN_HEIGHT;
-import static com.android.server.wm.TaskProto.MIN_WIDTH;
-import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
-import static com.android.server.wm.TaskProto.REAL_ACTIVITY;
-import static com.android.server.wm.TaskProto.RESIZE_MODE;
-import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.TaskProto.ROOT_TASK_ID;
-import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
-import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import static java.lang.Integer.MAX_VALUE;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityOptions;
-import android.app.AppGlobals;
-import android.app.IActivityController;
-import android.app.RemoteAction;
-import android.app.ResultInfo;
-import android.app.servertransaction.ActivityResultItem;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.NewIntentItem;
-import android.app.servertransaction.PauseActivityItem;
-import android.app.servertransaction.ResumeActivityItem;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.service.voice.IVoiceInteractionSession;
-import android.util.Log;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledFunction;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.Watchdog;
-import com.android.server.am.ActivityManagerService;
-import com.android.server.am.ActivityManagerService.ItemMatcher;
-import com.android.server.am.AppTimeTracker;
-import com.android.server.uri.NeededUriGrants;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
-
-/**
- * State and management of a single stack of activities.
- */
-class ActivityStack extends Task {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
- static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
- private static final String TAG_APP = TAG + POSTFIX_APP;
- static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
- private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
- private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
- private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
- private static final String TAG_STACK = TAG + POSTFIX_STACK;
- private static final String TAG_STATES = TAG + POSTFIX_STATES;
- private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
- static final String TAG_TASKS = TAG + POSTFIX_TASKS;
- private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
- private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
- static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
-
- // Set to false to disable the preview that is shown while a new activity
- // is being started.
- private static final boolean SHOW_APP_STARTING_PREVIEW = true;
-
- // How long to wait for all background Activities to redraw following a call to
- // convertToTranslucent().
- private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
-
- @IntDef(prefix = {"STACK_VISIBILITY"}, value = {
- STACK_VISIBILITY_VISIBLE,
- STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- STACK_VISIBILITY_INVISIBLE,
- })
- @interface StackVisibility {}
-
- /** Stack is visible. No other stacks on top that fully or partially occlude it. */
- static final int STACK_VISIBILITY_VISIBLE = 0;
-
- /** Stack is partially occluded by other translucent stack(s) on top of it. */
- static final int STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
-
- /** Stack is completely invisible. */
- static final int STACK_VISIBILITY_INVISIBLE = 2;
-
- enum ActivityState {
- INITIALIZING,
- STARTED,
- RESUMED,
- PAUSING,
- PAUSED,
- STOPPING,
- STOPPED,
- FINISHING,
- DESTROYING,
- DESTROYED,
- RESTARTING_PROCESS
- }
-
- // The topmost Activity passed to convertToTranslucent(). When non-null it means we are
- // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
- // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
- // Activity in mTranslucentActivityWaiting is notified via
- // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
- // background activity being drawn then the same call will be made with a true value.
- ActivityRecord mTranslucentActivityWaiting = null;
- ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<>();
-
- /**
- * Set when we know we are going to be calling updateConfiguration()
- * soon, so want to skip intermediate config checks.
- */
- boolean mConfigWillChange;
-
- /**
- * Used to keep resumeTopActivityUncheckedLocked() from being entered recursively
- */
- boolean mInResumeTopActivity = false;
-
- int mCurrentUser;
-
- /** For comparison with DisplayContent bounds. */
- private Rect mTmpRect = new Rect();
- private Rect mTmpRect2 = new Rect();
-
- /** Detach this stack from its display when animation completes. */
- // TODO: maybe tie this to WindowContainer#removeChild some how...
- // TODO: This is no longer set. Okay to remove or was the set removed by accident?
- private boolean mDeferRemoval;
-
- // If this is true, we are in the bounds animating mode. The task will be down or upscaled to
- // perfectly fit the region it would have been cropped to. We may also avoid certain logic we
- // would otherwise apply while resizing, while resizing in the bounds animating mode.
- private boolean mBoundsAnimating = false;
- // Set when an animation has been requested but has not yet started from the UI thread. This is
- // cleared when the animation actually starts.
- private boolean mBoundsAnimatingRequested = false;
- private Rect mBoundsAnimationTarget = new Rect();
- private Rect mBoundsAnimationSourceHintBounds = new Rect();
-
- Rect mPreAnimationBounds = new Rect();
-
- private final AnimatingActivityRegistry mAnimatingActivityRegistry =
- new AnimatingActivityRegistry();
-
- private boolean mTopActivityOccludesKeyguard;
- private ActivityRecord mTopDismissingKeyguardActivity;
-
- private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
-
- private final Handler mHandler;
-
- private class ActivityStackHandler extends Handler {
-
- ActivityStackHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case TRANSLUCENT_TIMEOUT_MSG: {
- synchronized (mAtmService.mGlobalLock) {
- notifyActivityDrawnLocked(null);
- }
- } break;
- }
- }
- }
-
- private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
- private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
- new EnsureActivitiesVisibleHelper(this);
- private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
- new EnsureVisibleActivitiesConfigHelper();
- private class EnsureVisibleActivitiesConfigHelper {
- private boolean mUpdateConfig;
- private boolean mPreserveWindow;
- private boolean mBehindFullscreen;
-
- void reset(boolean preserveWindow) {
- mPreserveWindow = preserveWindow;
- mUpdateConfig = false;
- mBehindFullscreen = false;
- }
-
- void process(ActivityRecord start, boolean preserveWindow) {
- if (start == null || !start.mVisibleRequested) {
- return;
- }
- reset(preserveWindow);
-
- final PooledFunction f = PooledLambda.obtainFunction(
- EnsureVisibleActivitiesConfigHelper::processActivity, this,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
- f.recycle();
-
- if (mUpdateConfig) {
- // Ensure the resumed state of the focus activity if we updated the configuration of
- // any activity.
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
- }
-
- boolean processActivity(ActivityRecord r) {
- mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
- mBehindFullscreen |= r.occludesParent();
- return mBehindFullscreen;
- }
- }
-
- private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
- new CheckBehindFullscreenActivityHelper();
- private class CheckBehindFullscreenActivityHelper {
- private boolean mAboveTop;
- private boolean mBehindFullscreenActivity;
- private ActivityRecord mToCheck;
- private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
- private boolean mHandlingOccluded;
-
- private void reset(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- mToCheck = toCheck;
- mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
- mAboveTop = true;
- mBehindFullscreenActivity = false;
-
- if (!shouldBeVisible(null)) {
- // The stack is not visible, so no activity in it should be displaying a starting
- // window. Mark all activities below top and behind fullscreen.
- mAboveTop = false;
- mBehindFullscreenActivity = true;
- }
-
- mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
- }
-
- boolean process(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- reset(toCheck, handleBehindFullscreenActivity);
-
- if (!mHandlingOccluded && mBehindFullscreenActivity) {
- return true;
- }
-
- final ActivityRecord topActivity = topRunningActivity();
- final PooledFunction f = PooledLambda.obtainFunction(
- CheckBehindFullscreenActivityHelper::processActivity, this,
- PooledLambda.__(ActivityRecord.class), topActivity);
- forAllActivities(f);
- f.recycle();
-
- return mBehindFullscreenActivity;
- }
-
- /** Returns {@code true} to stop the outer loop and indicate the result is computed. */
- private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
- if (mAboveTop) {
- if (r == topActivity) {
- if (r == mToCheck) {
- // It is the top activity in a visible stack.
- mBehindFullscreenActivity = false;
- return true;
- }
- mAboveTop = false;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
-
- if (mHandlingOccluded) {
- // Iterating through all occluded activities.
- if (mBehindFullscreenActivity) {
- mHandleBehindFullscreenActivity.accept(r);
- }
- } else if (r == mToCheck) {
- return true;
- } else if (mBehindFullscreenActivity) {
- // It is occluded before {@param toCheck} is found.
- return true;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
- }
-
- // TODO: Can we just loop through WindowProcessController#mActivities instead of doing this?
- private final RemoveHistoryRecordsForApp mRemoveHistoryRecordsForApp =
- new RemoveHistoryRecordsForApp();
- private class RemoveHistoryRecordsForApp {
- private boolean mHasVisibleActivities;
- private boolean mIsProcessRemoved;
- private WindowProcessController mApp;
- private ArrayList<ActivityRecord> mToRemove = new ArrayList<>();
-
- boolean process(WindowProcessController app) {
- mToRemove.clear();
- mHasVisibleActivities = false;
- mApp = app;
- mIsProcessRemoved = app.isRemoved();
- if (mIsProcessRemoved) {
- // The package of the died process should be force-stopped, so make its activities
- // as finishing to prevent the process from being started again if the next top
- // (or being visible) activity also resides in the same process.
- app.makeFinishingForProcessRemoved();
- }
-
- final PooledConsumer c = PooledLambda.obtainConsumer(
- RemoveHistoryRecordsForApp::addActivityToRemove, this,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(c);
- c.recycle();
-
- while (!mToRemove.isEmpty()) {
- processActivity(mToRemove.remove(0));
- }
-
- mApp = null;
- return mHasVisibleActivities;
- }
-
- private void addActivityToRemove(ActivityRecord r) {
- if (r.app == mApp) {
- mToRemove.add(r);
- }
- }
-
- private void processActivity(ActivityRecord r) {
- if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Record " + r + ": app=" + r.app);
-
- if (r.app != mApp) {
- return;
- }
- if (r.isVisible() || r.mVisibleRequested) {
- // While an activity launches a new activity, it's possible that the old
- // activity is already requested to be hidden (mVisibleRequested=false), but
- // this visibility is not yet committed, so isVisible()=true.
- mHasVisibleActivities = true;
- }
- final boolean remove;
- if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
- || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
- && r.launchCount < 3 && !r.finishing) {
- // If the process crashed during a resize, always try to relaunch it, unless
- // it has failed more than twice. Skip activities that's already finishing
- // cleanly by itself.
- remove = false;
- } else if ((!r.hasSavedState() && !r.stateNotNeeded
- && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
- // Don't currently have state for the activity, or
- // it is finishing -- always remove it.
- remove = true;
- } else if (!r.mVisibleRequested && r.launchCount > 2
- && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
- // We have launched this activity too many times since it was
- // able to run, so give up and remove it.
- // (Note if the activity is visible, we don't remove the record.
- // We leave the dead window on the screen but the process will
- // not be restarted unless user explicitly tap on it.)
- remove = true;
- } else {
- // The process may be gone, but the activity lives on!
- remove = false;
- }
- if (remove) {
- if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
- "Removing activity " + r + " from stack "
- + ": hasSavedState=" + r.hasSavedState()
- + " stateNotNeeded=" + r.stateNotNeeded
- + " finishing=" + r.finishing
- + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
- if (!r.finishing || mIsProcessRemoved) {
- Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
- EventLogTags.writeWmFinishActivity(r.mUserId,
- System.identityHashCode(r), r.getTask().mTaskId,
- r.shortComponentName, "proc died without state saved");
- }
- } else {
- // We have the current state for this activity, so
- // it can be restarted later when needed.
- if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
- if (DEBUG_APP) Slog.v(TAG_APP,
- "Clearing app during removeHistory for activity " + r);
- r.app = null;
- // Set nowVisible to previous visible state. If the app was visible while
- // it died, we leave the dead window on screen so it's basically visible.
- // This is needed when user later tap on the dead window, we need to stop
- // other apps when user transfers focus to the restarted activity.
- r.nowVisible = r.mVisibleRequested;
- }
- r.cleanUp(true /* cleanServices */, true /* setState */);
- if (remove) {
- r.removeFromHistory("appDied");
- }
- }
- }
-
- ActivityStack(ActivityTaskManagerService atmService, int id, int activityType,
- ActivityInfo info, Intent intent, boolean createdByOrganizer) {
- this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
- null /*taskDescription*/, null /*stack*/);
- mCreatedByOrganizer = createdByOrganizer;
- setActivityType(activityType);
- }
-
- ActivityStack(ActivityTaskManagerService atmService, int id, ActivityInfo info, Intent _intent,
- IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
- ActivityManager.TaskDescription _taskDescription, ActivityStack stack) {
- this(atmService, id, _intent, null /*_affinityIntent*/, null /*_affinity*/,
- null /*_rootAffinity*/, null /*_realActivity*/, null /*_origActivity*/,
- false /*_rootWasReset*/, false /*_autoRemoveRecents*/, false /*_askedCompatMode*/,
- UserHandle.getUserId(info.applicationInfo.uid), 0 /*_effectiveUid*/,
- null /*_lastDescription*/, System.currentTimeMillis(),
- true /*neverRelinquishIdentity*/,
- _taskDescription != null ? _taskDescription : new ActivityManager.TaskDescription(),
- id, INVALID_TASK_ID, INVALID_TASK_ID,
- info.applicationInfo.uid, info.packageName, null, info.resizeMode,
- info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
- false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info,
- _voiceSession, _voiceInteractor, stack);
- }
-
- ActivityStack(ActivityTaskManagerService atmService, int id, Intent _intent,
- Intent _affinityIntent, String _affinity, String _rootAffinity,
- ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
- boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId, int _effectiveUid,
- String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity,
- ActivityManager.TaskDescription _lastTaskDescription, int taskAffiliation,
- int prevTaskId, int nextTaskId, int callingUid,
- String callingPackage, @Nullable String callingFeatureId, int resizeMode,
- boolean supportsPictureInPicture, boolean _realActivitySuspended,
- boolean userSetupComplete, int minWidth, int minHeight,
- ActivityInfo info, IVoiceInteractionSession _voiceSession,
- IVoiceInteractor _voiceInteractor, ActivityStack stack) {
- super(atmService, id, _intent, _affinityIntent, _affinity, _rootAffinity,
- _realActivity, _origActivity, _rootWasReset, _autoRemoveRecents, _askedCompatMode,
- _userId, _effectiveUid, _lastDescription, lastTimeMoved, neverRelinquishIdentity,
- _lastTaskDescription, taskAffiliation, prevTaskId, nextTaskId,
- callingUid, callingPackage, callingFeatureId, resizeMode, supportsPictureInPicture,
- _realActivitySuspended, userSetupComplete, minWidth, minHeight, info, _voiceSession,
- _voiceInteractor, stack);
-
- EventLogTags.writeWmStackCreated(id);
- mHandler = new ActivityStackHandler(mStackSupervisor.mLooper);
- mCurrentUser = mAtmService.mAmInternal.getCurrentUserId();
- }
-
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- // Calling Task#onConfigurationChanged() for leaf task since the ops in this method are
- // particularly for ActivityStack, like preventing bounds changes when inheriting certain
- // windowing mode.
- if (!isRootTask()) {
- super.onConfigurationChanged(newParentConfig);
- return;
- }
-
- final int prevWindowingMode = getWindowingMode();
- final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
- final int prevRotation = getWindowConfiguration().getRotation();
- final Rect newBounds = mTmpRect;
- // Initialize the new bounds by previous bounds as the input and output for calculating
- // override bounds in pinned (pip) or split-screen mode.
- getBounds(newBounds);
-
- super.onConfigurationChanged(newParentConfig);
-
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
- if (taskDisplayArea == null) {
- return;
- }
-
- if (prevWindowingMode != getWindowingMode()) {
- taskDisplayArea.onStackWindowingModeChanged(this);
- }
-
- final DisplayContent display = getDisplay();
- if (display == null ) {
- return;
- }
-
- final boolean windowingModeChanged = prevWindowingMode != getWindowingMode();
- final int overrideWindowingMode = getRequestedOverrideWindowingMode();
- // Update bounds if applicable
- boolean hasNewOverrideBounds = false;
- // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if ((overrideWindowingMode != WINDOWING_MODE_PINNED)
- && !getRequestedOverrideBounds().isEmpty()) {
- // If the parent (display) has rotated, rotate our bounds to best-fit where their
- // bounds were on the pre-rotated display.
- final int newRotation = getWindowConfiguration().getRotation();
- final boolean rotationChanged = prevRotation != newRotation;
- if (rotationChanged) {
- display.mDisplayContent.rotateBounds(
- newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation,
- newBounds);
- hasNewOverrideBounds = true;
- }
- }
-
- if (windowingModeChanged) {
- taskDisplayArea.onStackWindowingModeChanged(this);
- }
- if (hasNewOverrideBounds) {
- if (inSplitScreenWindowingMode()) {
- setBounds(newBounds);
- } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
- // For pinned stack, resize is now part of the {@link WindowContainerTransaction}
- resize(new Rect(newBounds), PRESERVE_WINDOWS, true /* deferResume */);
- }
- }
- if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
- // Since always on top is only on when the stack is freeform or pinned, the state
- // can be toggled when the windowing mode changes. We must make sure the stack is
- // placed properly when always on top state changes.
- taskDisplayArea.positionStackAtTop(this, false /* includingParents */);
- }
- }
-
- @Override
- public void setWindowingMode(int windowingMode) {
- // Reset the cached result of toString()
- stringName = null;
-
- // Calling Task#setWindowingMode() for leaf task since this is the a specialization of
- // {@link #setWindowingMode(int)} for ActivityStack.
- if (!isRootTask()) {
- super.setWindowingMode(windowingMode);
- return;
- }
-
- setWindowingMode(windowingMode, false /* creating */);
- }
-
- /**
- * Specialization of {@link #setWindowingMode(int)} for this subclass.
- *
- * @param preferredWindowingMode the preferred windowing mode. This may not be honored depending
- * on the state of things. For example, WINDOWING_MODE_UNDEFINED will resolve to the
- * previous non-transient mode if this stack is currently in a transient mode.
- * @param creating {@code true} if this is being run during ActivityStack construction.
- */
- void setWindowingMode(int preferredWindowingMode, boolean creating) {
- mWmService.inSurfaceTransaction(() -> setWindowingModeInSurfaceTransaction(
- preferredWindowingMode, creating));
- }
-
- private void setWindowingModeInSurfaceTransaction(int preferredWindowingMode,
- boolean creating) {
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
- if (taskDisplayArea == null) {
- Slog.d(TAG, "taskDisplayArea is null, bail early");
- return;
- }
- final int currentMode = getWindowingMode();
- final int currentOverrideMode = getRequestedOverrideWindowingMode();
- final Task topTask = getTopMostTask();
- int windowingMode = preferredWindowingMode;
-
- // Need to make sure windowing mode is supported. If we in the process of creating the stack
- // no need to resolve the windowing mode again as it is already resolved to the right mode.
- if (!creating) {
- if (!taskDisplayArea.isValidWindowingMode(windowingMode, null /* ActivityRecord */,
- topTask, getActivityType())) {
- windowingMode = WINDOWING_MODE_UNDEFINED;
- }
- }
-
- final boolean alreadyInSplitScreenMode = taskDisplayArea.isSplitScreenModeActivated();
-
- if (creating && alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
- && isActivityTypeStandardOrUndefined()) {
- // If the stack is being created explicitly in fullscreen mode, dismiss split-screen
- // and display a warning toast about it.
- mAtmService.getTaskChangeNotificationController()
- .notifyActivityDismissingDockedStack();
- taskDisplayArea.onSplitScreenModeDismissed(this);
- }
-
- if (currentMode == windowingMode) {
- // You are already in the window mode, so we can skip most of the work below. However,
- // it's possible that we have inherited the current windowing mode from a parent. So,
- // fulfill this method's contract by setting the override mode directly.
- getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
- return;
- }
-
- final ActivityRecord topActivity = getTopNonFinishingActivity();
-
- // For now, assume that the Stack's windowing mode is what will actually be used
- // by it's activities. In the future, there may be situations where this doesn't
- // happen; so at that point, this message will need to handle that.
- int likelyResolvedMode = windowingMode;
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- final ConfigurationContainer parent = getParent();
- likelyResolvedMode = parent != null ? parent.getWindowingMode()
- : WINDOWING_MODE_FULLSCREEN;
- }
- if (currentMode == WINDOWING_MODE_PINNED) {
- mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
- }
- if (likelyResolvedMode == WINDOWING_MODE_PINNED
- && taskDisplayArea.getRootPinnedTask() != null) {
- // Can only have 1 pip at a time, so replace an existing pip
- taskDisplayArea.getRootPinnedTask().dismissPip();
- }
- if (likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
- && topActivity != null && !topActivity.noDisplay
- && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) {
- // Inform the user that they are starting an app that may not work correctly in
- // multi-window mode.
- final String packageName = topActivity.info.applicationInfo.packageName;
- mAtmService.getTaskChangeNotificationController().notifyActivityForcedResizable(
- topTask.mTaskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
- }
-
- mAtmService.deferWindowLayout();
- try {
- if (topActivity != null) {
- mStackSupervisor.mNoAnimActivities.add(topActivity);
- }
- super.setWindowingMode(windowingMode);
- // setWindowingMode triggers an onConfigurationChanged cascade which can result in a
- // different resolved windowing mode (usually when preferredWindowingMode is UNDEFINED).
- windowingMode = getWindowingMode();
-
- if (creating) {
- // Nothing else to do if we don't have a window container yet. E.g. call from ctor.
- return;
- }
-
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && alreadyInSplitScreenMode) {
- // We already have a split-screen stack in this display, so just move the tasks over.
- // TODO: Figure-out how to do all the stuff in
- // AMS.setTaskWindowingModeSplitScreenPrimary
- throw new IllegalArgumentException("Setting primary split-screen windowing mode"
- + " while there is already one isn't currently supported");
- //return;
- }
- } finally {
- mAtmService.continueWindowLayout();
- }
-
- mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
-
- @Override
- public boolean isCompatible(int windowingMode, int activityType) {
- // TODO: Should we just move this to ConfigurationContainer?
- if (activityType == ACTIVITY_TYPE_UNDEFINED) {
- // Undefined activity types end up in a standard stack once the stack is created on a
- // display, so they should be considered compatible.
- activityType = ACTIVITY_TYPE_STANDARD;
- }
- return super.isCompatible(windowingMode, activityType);
- }
-
- /** Resume next focusable stack after reparenting to another display. */
- void postReparent() {
- adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */,
- true /* moveDisplayToTop */);
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- // Update visibility of activities before notifying WM. This way it won't try to resize
- // windows that are no longer visible.
- mRootWindowContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
- }
-
- DisplayContent getDisplay() {
- return getDisplayContent();
- }
-
- /** @return true if the stack can only contain one task */
- boolean isSingleTaskInstance() {
- final DisplayContent display = getDisplay();
- return display != null && display.isSingleTaskInstance();
- }
-
- final boolean isHomeOrRecentsStack() {
- return isActivityTypeHome() || isActivityTypeRecents();
- }
-
- final boolean isOnHomeDisplay() {
- return getDisplayId() == DEFAULT_DISPLAY;
- }
-
- void moveToFront(String reason) {
- moveToFront(reason, null);
- }
-
- /**
- * @param reason The reason for moving the stack to the front.
- * @param task If non-null, the task will be moved to the top of the stack.
- * */
- void moveToFront(String reason, Task task) {
- if (!isAttached()) {
- return;
- }
-
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
-
- if (inSplitScreenSecondaryWindowingMode()) {
- // If the stack is in split-screen secondary mode, we need to make sure we move the
- // primary split-screen stack forward in the case it is currently behind a fullscreen
- // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't
- // cutting between them.
- // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
- final ActivityStack topFullScreenStack =
- taskDisplayArea.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- if (topFullScreenStack != null) {
- final ActivityStack primarySplitScreenStack =
- taskDisplayArea.getRootSplitScreenPrimaryTask();
- if (primarySplitScreenStack != null
- && taskDisplayArea.getIndexOf(topFullScreenStack)
- > taskDisplayArea.getIndexOf(primarySplitScreenStack)) {
- primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
- }
- }
- }
-
- if (!isActivityTypeHome() && returnsToHomeStack()) {
- // Make sure the home stack is behind this stack since that is where we should return to
- // when this stack is no longer visible.
- taskDisplayArea.moveHomeStackToFront(reason + " returnToHome");
- }
-
- if (isRootTask()) {
- taskDisplayArea.positionStackAtTop(this, false /* includingParents */, reason);
- }
- if (task == null) {
- task = this;
- }
- task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
- }
-
- /**
- * This moves 'task' to the back of this task and also recursively moves this task to the back
- * of its parents (if applicable).
- *
- * @param reason The reason for moving the stack to the back.
- * @param task If non-null, the task will be moved to the bottom of the stack.
- **/
- void moveToBack(String reason, Task task) {
- if (!isAttached()) {
- return;
- }
- final TaskDisplayArea displayArea = getDisplayArea();
- if (!mCreatedByOrganizer) {
- // If this is just a normal task, so move to back of parent and then move 'task' to
- // back of this.
- final WindowContainer parent = getParent();
- final Task parentTask = parent != null ? parent.asTask() : null;
- if (parentTask != null) {
- ((ActivityStack) parentTask).moveToBack(reason, this);
- } else {
- displayArea.positionStackAtBottom(this, reason);
- }
- if (task != null && task != this) {
- positionChildAtBottom(task);
- }
- return;
- }
- if (task == null || task == this) {
- return;
- }
- // This is a created-by-organizer task. In this case, let the organizer deal with this
- // task's ordering. However, we still need to move 'task' to back. The intention is that
- // this ends up behind the home-task so that it is made invisible; so, if the home task
- // is not a child of this, reparent 'task' to the back of the home task's actual parent.
- displayArea.positionTaskBehindHome((ActivityStack) task);
- }
-
- // TODO: Should each user have there own stacks?
- @Override
- void switchUser(int userId) {
- if (mCurrentUser == userId) {
- return;
- }
- mCurrentUser = userId;
-
- super.switchUser(userId);
- forAllLeafTasks((t) -> {
- if (t.showToCurrentUser() && t != this) {
- mChildren.remove(t);
- mChildren.add(t);
- }
- }, true /* traverseTopToBottom */);
- }
-
- void minimalResumeActivityLocked(ActivityRecord r) {
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + r + " (starting new instance)"
- + " callers=" + Debug.getCallers(5));
- r.setState(RESUMED, "minimalResumeActivityLocked");
- r.completeResumeLocked();
- }
-
- private void clearLaunchTime(ActivityRecord r) {
- // Make sure that there is no activity waiting for this to launch.
- if (!mStackSupervisor.mWaitingActivityLaunched.isEmpty()) {
- mStackSupervisor.removeIdleTimeoutForActivity(r);
- mStackSupervisor.scheduleIdleTimeout(r);
- }
- }
-
- void awakeFromSleepingLocked() {
- // Ensure activities are no longer sleeping.
- forAllActivities((Consumer<ActivityRecord>) (r) -> r.setSleeping(false));
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
- if (mPausingActivity != null) {
- Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
- mPausingActivity.activityPaused(true);
- }
- }
-
- void checkReadyForSleep() {
- if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
- mStackSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
- }
- }
-
- /**
- * Tries to put the activities in the stack to sleep.
- *
- * If the stack is not in a state where its activities can be put to sleep, this function will
- * start any necessary actions to move the stack into such a state. It is expected that this
- * function get called again when those actions complete.
- *
- * @param shuttingDown true when the called because the device is shutting down.
- * @return true if the stack finished going to sleep, false if the stack only started the
- * process of going to sleep (checkReadyForSleep will be called when that process finishes).
- */
- boolean goToSleepIfPossible(boolean shuttingDown) {
- boolean shouldSleep = true;
-
- if (mResumedActivity != null) {
- // Still have something resumed; can't sleep until it is paused.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep needs to pause " + mResumedActivity);
- if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
- "Sleep => pause with userLeaving=false");
-
- startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */);
- shouldSleep = false ;
- } else if (mPausingActivity != null) {
- // Still waiting for something to pause; can't sleep yet.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
- shouldSleep = false;
- }
-
- if (!shuttingDown) {
- if (containsActivityFromStack(mStackSupervisor.mStoppingActivities)) {
- // Still need to tell some activities to stop; can't sleep yet.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
- + mStackSupervisor.mStoppingActivities.size() + " activities");
-
- mStackSupervisor.scheduleIdle();
- shouldSleep = false;
- }
- }
-
- if (shouldSleep) {
- goToSleep();
- }
-
- return shouldSleep;
- }
-
- void goToSleep() {
- // Make sure all visible activities are now sleeping. This will update the activity's
- // visibility and onStop() will be called.
- forAllActivities((r) -> {
- if (r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING, STOPPED)) {
- r.setSleeping(true);
- }
- });
-
- // Ensure visibility after updating sleep states without updating configuration,
- // as activities are about to be sent to sleep.
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
- }
-
- private boolean containsActivityFromStack(List<ActivityRecord> rs) {
- for (ActivityRecord r : rs) {
- if (r.getRootTask() == this) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Start pausing the currently resumed activity. It is an error to call this if there
- * is already an activity being paused or there is no resumed activity.
- *
- * @param userLeaving True if this should result in an onUserLeaving to the current activity.
- * @param uiSleeping True if this is happening with the user interface going to sleep (the
- * screen turning off).
- * @param resuming The activity we are currently trying to resume or null if this is not being
- * called as part of resuming the top activity, so we shouldn't try to instigate
- * a resume here if not null.
- * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
- * it to tell us when it is done.
- */
- final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
- ActivityRecord resuming) {
- if (mPausingActivity != null) {
- Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
- + " state=" + mPausingActivity.getState());
- if (!shouldSleepActivities()) {
- // Avoid recursion among check for sleep and complete pause during sleeping.
- // Because activity will be paused immediately after resume, just let pause
- // be completed by the order of activity paused from clients.
- completePauseLocked(false, resuming);
- }
- }
- ActivityRecord prev = mResumedActivity;
-
- if (prev == null) {
- if (resuming == null) {
- Slog.wtf(TAG, "Trying to pause when nothing is resumed");
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
- return false;
- }
-
- if (prev == resuming) {
- Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
- return false;
- }
-
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSING: " + prev);
- else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
- mPausingActivity = prev;
- mLastPausedActivity = prev;
- mLastNoHistoryActivity = prev.isNoHistory() ? prev : null;
- prev.setState(PAUSING, "startPausingLocked");
- prev.getTask().touchActiveTime();
- clearLaunchTime(prev);
-
- mAtmService.updateCpuStats();
-
- boolean pauseImmediately = false;
- if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
- // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
- // activity to be paused, while at the same time resuming the new resume activity
- // only if the previous activity can't go into Pip since we want to give Pip
- // activities a chance to enter Pip before resuming the next activity.
- final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
- "shouldResumeWhilePausing", userLeaving);
- if (!lastResumedCanPip) {
- pauseImmediately = true;
- }
- }
-
- if (prev.attachedToProcess()) {
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
- try {
- EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
- prev.shortComponentName, "userLeaving=" + userLeaving);
-
- mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
- prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately));
- } catch (Exception e) {
- // Ignore exception, if process died other code will cleanup.
- Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
- mLastPausedActivity = null;
- mLastNoHistoryActivity = null;
- }
- } else {
- mPausingActivity = null;
- mLastPausedActivity = null;
- mLastNoHistoryActivity = null;
- }
-
- // If we are not going to sleep, we want to ensure the device is
- // awake until the next activity is started.
- if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
- mStackSupervisor.acquireLaunchWakelock();
- }
-
- if (mPausingActivity != null) {
- // Have the window manager pause its key dispatching until the new
- // activity has started. If we're pausing the activity just because
- // the screen is being turned off and the UI is sleeping, don't interrupt
- // key dispatch; the same activity will pick it up again on wakeup.
- if (!uiSleeping) {
- prev.pauseKeyDispatchingLocked();
- } else if (DEBUG_PAUSE) {
- Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off");
- }
-
- if (pauseImmediately) {
- // If the caller said they don't want to wait for the pause, then complete
- // the pause now.
- completePauseLocked(false, resuming);
- return false;
-
- } else {
- prev.schedulePauseTimeout();
- return true;
- }
-
- } else {
- // This activity failed to schedule the
- // pause, so just treat it as being paused now.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
- if (resuming == null) {
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
- return false;
- }
- }
-
- @VisibleForTesting
- void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
- ActivityRecord prev = mPausingActivity;
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);
-
- if (prev != null) {
- prev.setWillCloseOrEnterPip(false);
- final boolean wasStopping = prev.isState(STOPPING);
- prev.setState(PAUSED, "completePausedLocked");
- if (prev.finishing) {
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
- prev = prev.completeFinishing("completePausedLocked");
- } else if (prev.hasProcess()) {
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
- + " wasStopping=" + wasStopping
- + " visibleRequested=" + prev.mVisibleRequested);
- if (prev.deferRelaunchUntilPaused) {
- // Complete the deferred relaunch that was waiting for pause to complete.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
- prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
- } else if (wasStopping) {
- // We are also stopping, the stop request must have gone soon after the pause.
- // We can't clobber it, because the stop confirmation will not be handled.
- // We don't need to schedule another stop, we only need to let it happen.
- prev.setState(STOPPING, "completePausedLocked");
- } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
- // Clear out any deferred client hide we might currently have.
- prev.setDeferHidingClient(false);
- // If we were visible then resumeTopActivities will release resources before
- // stopping.
- prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
- "completePauseLocked");
- }
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);
- prev = null;
- }
- // It is possible the activity was freezing the screen before it was paused.
- // In that case go ahead and remove the freeze this activity has on the screen
- // since it is no longer visible.
- if (prev != null) {
- prev.stopFreezingScreenLocked(true /*force*/);
- }
- mPausingActivity = null;
- }
-
- if (resumeNext) {
- final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
- if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {
- mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
- } else {
- checkReadyForSleep();
- final ActivityRecord top = topStack != null ? topStack.topRunningActivity() : null;
- if (top == null || (prev != null && top != prev)) {
- // If there are no more activities available to run, do resume anyway to start
- // something. Also if the top activity on the stack is not the just paused
- // activity, we need to go ahead and resume it to ensure we complete an
- // in-flight app switch.
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
- }
- }
-
- if (prev != null) {
- prev.resumeKeyDispatchingLocked();
-
- if (prev.hasProcess() && prev.cpuTimeAtResume > 0) {
- final long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
- if (diff > 0) {
- final Runnable r = PooledLambda.obtainRunnable(
- ActivityManagerInternal::updateForegroundTimeIfOnBattery,
- mAtmService.mAmInternal, prev.info.packageName,
- prev.info.applicationInfo.uid,
- diff);
- mAtmService.mH.post(r);
- }
- }
- prev.cpuTimeAtResume = 0; // reset it
- }
-
- mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
-
- // Notify when the task stack has changed, but only if visibilities changed (not just
- // focus). Also if there is an active pinned stack - we always want to notify it about
- // task stack changes, because its positioning may depend on it.
- if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
- || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
- mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
- mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
- }
- }
-
- boolean isTopStackInDisplayArea() {
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
- return taskDisplayArea != null && taskDisplayArea.isTopStack(this);
- }
-
- /**
- * @return {@code true} if this is the focused stack on its current display, {@code false}
- * otherwise.
- */
- boolean isFocusedStackOnDisplay() {
- final DisplayContent display = getDisplay();
- return display != null && this == display.getFocusedStack();
- }
-
- /**
- * Make sure that all activities that need to be visible in the stack (that is, they
- * currently can be seen by the user) actually are and update their configuration.
- * @param starting The top most activity in the task.
- * The activity is either starting or resuming.
- * Caller should ensure starting activity is visible.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
- *
- */
- void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
- boolean preserveWindows) {
- ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
- }
-
- /**
- * Ensure visibility with an option to also update the configuration of visible activities.
- * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
- * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
- * @param starting The top most activity in the task.
- * The activity is either starting or resuming.
- * Caller should ensure starting activity is visible.
- * @param notifyClients Flag indicating whether the visibility updates should be sent to the
- * clients in {@link mEnsureActivitiesVisibleHelper}.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
- */
- // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
- void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
- boolean preserveWindows, boolean notifyClients) {
- mTopActivityOccludesKeyguard = false;
- mTopDismissingKeyguardActivity = null;
- mStackSupervisor.beginActivityVisibilityUpdate();
- try {
- mEnsureActivitiesVisibleHelper.process(
- starting, configChanges, preserveWindows, notifyClients);
-
- if (mTranslucentActivityWaiting != null &&
- mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
- // Nothing is getting drawn or everything was already visible, don't wait for timeout.
- notifyActivityDrawnLocked(null);
- }
- } finally {
- mStackSupervisor.endActivityVisibilityUpdate();
- }
- }
-
- /**
- * @return true if the top visible activity wants to occlude the Keyguard, false otherwise
- */
- boolean topActivityOccludesKeyguard() {
- return mTopActivityOccludesKeyguard;
- }
-
- /**
- * Returns true if this stack should be resized to match the bounds specified by
- * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
- */
- boolean shouldResizeStackWithLaunchBounds() {
- return inPinnedWindowingMode();
- }
-
- // TODO(NOW!)
- /**
- * Returns {@code true} if this is the top-most split-screen-primary or
- * split-screen-secondary stack, {@code false} otherwise.
- */
- boolean isTopSplitScreenStack() {
- return inSplitScreenWindowingMode()
- && this == getDisplayArea().getTopStackInWindowingMode(getWindowingMode());
- }
-
- /**
- * @return the top most visible activity that wants to dismiss Keyguard
- */
- ActivityRecord getTopDismissingKeyguardActivity() {
- return mTopDismissingKeyguardActivity;
- }
-
- /**
- * Checks whether {@param r} should be visible depending on Keyguard state and updates
- * {@link #mTopActivityOccludesKeyguard} and {@link #mTopDismissingKeyguardActivity} if
- * necessary.
- *
- * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
- */
- boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) {
- int displayId = getDisplayId();
- if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
-
- final boolean keyguardOrAodShowing = mStackSupervisor.getKeyguardController()
- .isKeyguardOrAodShowing(displayId);
- final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
- final boolean showWhenLocked = r.canShowWhenLocked();
- final boolean dismissKeyguard = r.containsDismissKeyguardWindow();
- if (shouldBeVisible) {
- if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
- mTopDismissingKeyguardActivity = r;
- }
-
- // Only the top activity may control occluded, as we can't occlude the Keyguard if the
- // top app doesn't want to occlude it.
- if (isTop) {
- mTopActivityOccludesKeyguard |= showWhenLocked;
- }
-
- final boolean canShowWithKeyguard = canShowWithInsecureKeyguard()
- && mStackSupervisor.getKeyguardController().canDismissKeyguard();
- if (canShowWithKeyguard) {
- return true;
- }
- }
- if (keyguardOrAodShowing) {
- // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
- // right away and AOD isn't visible.
- return shouldBeVisible && mStackSupervisor.getKeyguardController()
- .canShowActivityWhileKeyguardShowing(r, dismissKeyguard);
- } else if (keyguardLocked) {
- return shouldBeVisible && mStackSupervisor.getKeyguardController().canShowWhileOccluded(
- dismissKeyguard, showWhenLocked);
- } else {
- return shouldBeVisible;
- }
- }
-
- /**
- * Check if the display to which this stack is attached has
- * {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
- */
- boolean canShowWithInsecureKeyguard() {
- final DisplayContent displayContent = getDisplay();
- if (displayContent == null) {
- throw new IllegalStateException("Stack is not attached to any display, stackId="
- + getRootTaskId());
- }
-
- final int flags = displayContent.mDisplay.getFlags();
- return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
- }
-
- void checkTranslucentActivityWaiting(ActivityRecord top) {
- if (mTranslucentActivityWaiting != top) {
- mUndrawnActivitiesBelowTopTranslucent.clear();
- if (mTranslucentActivityWaiting != null) {
- // Call the callback with a timeout indication.
- notifyActivityDrawnLocked(null);
- mTranslucentActivityWaiting = null;
- }
- mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
- }
- }
-
- void convertActivityToTranslucent(ActivityRecord r) {
- mTranslucentActivityWaiting = r;
- mUndrawnActivitiesBelowTopTranslucent.clear();
- mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
- }
-
- /**
- * Called as activities below the top translucent activity are redrawn. When the last one is
- * redrawn notify the top activity by calling
- * {@link Activity#onTranslucentConversionComplete}.
- *
- * @param r The most recent background activity to be drawn. Or, if r is null then a timeout
- * occurred and the activity will be notified immediately.
- */
- void notifyActivityDrawnLocked(ActivityRecord r) {
- if ((r == null)
- || (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
- mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
- // The last undrawn activity below the top has just been drawn. If there is an
- // opaque activity at the top, notify it that it can become translucent safely now.
- final ActivityRecord waitingActivity = mTranslucentActivityWaiting;
- mTranslucentActivityWaiting = null;
- mUndrawnActivitiesBelowTopTranslucent.clear();
- mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
-
- if (waitingActivity != null) {
- mWmService.setWindowOpaqueLocked(waitingActivity.appToken, false);
- if (waitingActivity.attachedToProcess()) {
- try {
- waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
- waitingActivity.appToken, r != null);
- } catch (RemoteException e) {
- }
- }
- }
- }
- }
-
- /** @see ActivityRecord#cancelInitializing() */
- void cancelInitializingActivities() {
- // We don't want to clear starting window for activities that aren't behind fullscreen
- // activities as we need to display their starting window until they are done initializing.
- checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
- }
-
- /**
- * If an activity {@param toCheck} is given, this method returns {@code true} if the activity
- * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
- * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
- * activities to the function.
- */
- boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- return mCheckBehindFullscreenActivityHelper.process(
- toCheck, handleBehindFullscreenActivity);
- }
-
- /**
- * Ensure that the top activity in the stack is resumed.
- *
- * @param prev The previously resumed activity, for when in the process
- * of pausing; can be null to call from elsewhere.
- * @param options Activity options.
- *
- * @return Returns true if something is being resumed, or false if
- * nothing happened.
- *
- * NOTE: It is not safe to call this method directly as it can cause an activity in a
- * non-focused stack to be resumed.
- * Use {@link RootWindowContainer#resumeFocusedStacksTopActivities} to resume the
- * right activity for the current system state.
- */
- @GuardedBy("mService")
- boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
- if (mInResumeTopActivity) {
- // Don't even start recursing.
- return false;
- }
-
- boolean result = false;
- try {
- // Protect against recursion.
- mInResumeTopActivity = true;
- result = resumeTopActivityInnerLocked(prev, options);
-
- // When resuming the top activity, it may be necessary to pause the top activity (for
- // example, returning to the lock screen. We suppress the normal pause logic in
- // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the
- // end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here
- // to ensure any necessary pause logic occurs. In the case where the Activity will be
- // shown regardless of the lock screen, the call to
- // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.
- final ActivityRecord next = topRunningActivity(true /* focusableOnly */);
- if (next == null || !next.canTurnScreenOn()) {
- checkReadyForSleep();
- }
- } finally {
- mInResumeTopActivity = false;
- }
-
- return result;
- }
-
- @GuardedBy("mService")
- private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
- if (!mAtmService.isBooting() && !mAtmService.isBooted()) {
- // Not ready yet!
- return false;
- }
-
- // Find the next top-most activity to resume in this stack that is not finishing and is
- // focusable. If it is not focusable, we will fall into the case below to resume the
- // top activity in the next focusable task.
- ActivityRecord next = topRunningActivity(true /* focusableOnly */);
-
- final boolean hasRunningActivity = next != null;
-
- // TODO: Maybe this entire condition can get removed?
- if (hasRunningActivity && !isAttached()) {
- return false;
- }
-
- mRootWindowContainer.cancelInitializingActivities();
-
- // Remember how we'll process this pause/resume situation, and ensure
- // that the state is reset however we wind up proceeding.
- boolean userLeaving = mStackSupervisor.mUserLeaving;
- mStackSupervisor.mUserLeaving = false;
-
- if (!hasRunningActivity) {
- // There are no activities left in the stack, let's look somewhere else.
- return resumeNextFocusableActivityWhenStackIsEmpty(prev, options);
- }
-
- next.delayedResume = false;
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
-
- // If the top activity is the resumed one, nothing to do.
- if (mResumedActivity == next && next.isState(RESUMED)
- && taskDisplayArea.allResumedActivitiesComplete()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- if (DEBUG_STATES) Slog.d(TAG_STATES,
- "resumeTopActivityLocked: Top activity resumed " + next);
- return false;
- }
-
- if (!next.canResumeByCompat()) {
- return false;
- }
-
- // If we are currently pausing an activity, then don't do anything until that is done.
- final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
- if (!allPausedComplete) {
- if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) {
- Slog.v(TAG_PAUSE, "resumeTopActivityLocked: Skip resume: some activity pausing.");
- }
- return false;
- }
-
- // If we are sleeping, and there is no resumed activity, and the top activity is paused,
- // well that is the state we want.
- if (shouldSleepOrShutDownActivities()
- && mLastPausedActivity == next
- && mRootWindowContainer.allPausedActivitiesComplete()) {
- // If the current top activity may be able to occlude keyguard but the occluded state
- // has not been set, update visibility and check again if we should continue to resume.
- boolean nothingToResume = true;
- if (!mAtmService.mShuttingDown) {
- final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard
- && next.canShowWhenLocked();
- final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next
- && next.containsDismissKeyguardWindow();
-
- if (canShowWhenLocked || mayDismissKeyguard) {
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
- nothingToResume = shouldSleepActivities();
- } else if (next.currentLaunchCanTurnScreenOn() && next.canTurnScreenOn()) {
- nothingToResume = false;
- }
- }
- if (nothingToResume) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- if (DEBUG_STATES) Slog.d(TAG_STATES,
- "resumeTopActivityLocked: Going to sleep and all paused");
- return false;
- }
- }
-
- // Make sure that the user who owns this activity is started. If not,
- // we will just leave it as is because someone should be bringing
- // another user's activities to the top of the stack.
- if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
- Slog.w(TAG, "Skipping resume of top activity " + next
- + ": user " + next.mUserId + " is stopped");
- return false;
- }
-
- // The activity may be waiting for stop, but that is no longer
- // appropriate for it.
- mStackSupervisor.mStoppingActivities.remove(next);
- next.setSleeping(false);
-
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
-
- // If we are currently pausing an activity, then don't do anything until that is done.
- if (!mRootWindowContainer.allPausedActivitiesComplete()) {
- if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
- "resumeTopActivityLocked: Skip resume: some activity pausing.");
-
- return false;
- }
-
- mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
-
- ActivityRecord lastResumed = null;
- final ActivityStack lastFocusedStack = taskDisplayArea.getLastFocusedStack();
- if (lastFocusedStack != null && lastFocusedStack != this) {
- // So, why aren't we using prev here??? See the param comment on the method. prev doesn't
- // represent the last resumed activity. However, the last focus stack does if it isn't null.
- lastResumed = lastFocusedStack.mResumedActivity;
- if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {
- // The user isn't leaving if this stack is the multi-window mode and the last
- // focused stack should still be visible.
- if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
- + " next=" + next + " lastResumed=" + lastResumed);
- userLeaving = false;
- }
- }
-
- boolean pausing = taskDisplayArea.pauseBackStacks(userLeaving, next);
- if (mResumedActivity != null) {
- if (DEBUG_STATES) Slog.d(TAG_STATES,
- "resumeTopActivityLocked: Pausing " + mResumedActivity);
- pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next);
- }
- if (pausing) {
- if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
- "resumeTopActivityLocked: Skip resume: need to start pausing");
- // At this point we want to put the upcoming activity's process
- // at the top of the LRU list, since we know we will be needing it
- // very soon and it would be a waste to let it get killed if it
- // happens to be sitting towards the end.
- if (next.attachedToProcess()) {
- next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
- true /* activityChange */, false /* updateOomAdj */,
- false /* addPendingTopUid */);
- } else if (!next.isProcessRunning()) {
- // Since the start-process is asynchronous, if we already know the process of next
- // activity isn't running, we can start the process earlier to save the time to wait
- // for the current activity to be paused.
- final boolean isTop = this == taskDisplayArea.getFocusedStack();
- mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
- isTop ? "pre-top-activity" : "pre-activity");
- }
- if (lastResumed != null) {
- lastResumed.setWillCloseOrEnterPip(true);
- }
- return true;
- } else if (mResumedActivity == next && next.isState(RESUMED)
- && taskDisplayArea.allResumedActivitiesComplete()) {
- // It is possible for the activity to be resumed when we paused back stacks above if the
- // next activity doesn't have to wait for pause to complete.
- // So, nothing else to-do except:
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- if (DEBUG_STATES) Slog.d(TAG_STATES,
- "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
- return true;
- }
-
- // If the most recent activity was noHistory but was only stopped rather
- // than stopped+finished because the device went to sleep, we need to make
- // sure to finish it as we're making a new activity topmost.
- if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
- !mLastNoHistoryActivity.finishing) {
- if (DEBUG_STATES) Slog.d(TAG_STATES,
- "no-history finish of " + mLastNoHistoryActivity + " on new resume");
- mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
- mLastNoHistoryActivity = null;
- }
-
- if (prev != null && prev != next && next.nowVisible) {
-
- // The next activity is already visible, so hide the previous
- // activity's windows right now so we can show the new one ASAP.
- // We only do this if the previous is finishing, which should mean
- // it is on top of the one being resumed so hiding it quickly
- // is good. Otherwise, we want to do the normal route of allowing
- // the resumed activity to be shown so we can decide if the
- // previous should actually be hidden depending on whether the
- // new one is found to be full-screen or not.
- if (prev.finishing) {
- prev.setVisibility(false);
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Not waiting for visible to hide: " + prev
- + ", nowVisible=" + next.nowVisible);
- } else {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Previous already visible but still waiting to hide: " + prev
- + ", nowVisible=" + next.nowVisible);
- }
-
- }
-
- // Launching this app's activity, make sure the app is no longer
- // considered stopped.
- try {
- mAtmService.getPackageManager().setPackageStoppedState(
- next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
- } catch (RemoteException e1) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + next.packageName + ": " + e);
- }
-
- // We are starting up the next activity, so tell the window manager
- // that the previous one will be hidden soon. This way it can know
- // to ignore it when computing the desired screen orientation.
- boolean anim = true;
- final DisplayContent dc = taskDisplayArea.mDisplayContent;
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare close transition: prev=" + prev);
- if (mStackSupervisor.mNoAnimActivities.contains(prev)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE, false);
- } else {
- dc.prepareAppTransition(
- prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE
- : TRANSIT_TASK_CLOSE, false);
- }
- prev.setVisibility(false);
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare open transition: prev=" + prev);
- if (mStackSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE, false);
- } else {
- dc.prepareAppTransition(
- prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN
- : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND
- : TRANSIT_TASK_OPEN, false);
- }
- }
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
- if (mStackSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE, false);
- } else {
- dc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
- }
- }
-
- if (anim) {
- next.applyOptionsLocked();
- } else {
- next.clearOptionsLocked();
- }
-
- mStackSupervisor.mNoAnimActivities.clear();
-
- if (next.attachedToProcess()) {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
- + " stopped=" + next.stopped
- + " visibleRequested=" + next.mVisibleRequested);
-
- // If the previous activity is translucent, force a visibility update of
- // the next activity, so that it's added to WM's opening app list, and
- // transition animation can be set up properly.
- // For example, pressing Home button with a translucent activity in focus.
- // Launcher is already visible in this case. If we don't add it to opening
- // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
- // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
- final boolean lastActivityTranslucent = lastFocusedStack != null
- && (lastFocusedStack.inMultiWindowMode()
- || (lastFocusedStack.mLastPausedActivity != null
- && !lastFocusedStack.mLastPausedActivity.occludesParent()));
-
- // This activity is now becoming visible.
- if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
- next.setVisibility(true);
- }
-
- // schedule launch ticks to collect information about slow apps.
- next.startLaunchTickingLocked();
-
- ActivityRecord lastResumedActivity =
- lastFocusedStack == null ? null : lastFocusedStack.mResumedActivity;
- final ActivityState lastState = next.getState();
-
- mAtmService.updateCpuStats();
-
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
- + " (in existing)");
-
- next.setState(RESUMED, "resumeTopActivityInnerLocked");
-
- next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
- true /* activityChange */, true /* updateOomAdj */,
- true /* addPendingTopUid */);
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean notUpdated = true;
-
- // Activity should also be visible if set mLaunchTaskBehind to true (see
- // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
- if (shouldBeVisible(next)) {
- // We have special rotation behavior when here is some active activity that
- // requests specific orientation or Keyguard is locked. Make sure all activity
- // visibilities are set correctly as well as the transition is updated if needed
- // to get the correct rotation behavior. Otherwise the following call to update
- // the orientation may cause incorrect configurations delivered to client as a
- // result of invisible window resize.
- // TODO: Remove this once visibilities are set correctly immediately when
- // starting an activity.
- notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
- true /* markFrozenIfConfigChanged */, false /* deferResume */);
- }
-
- if (notUpdated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- ActivityRecord nextNext = topRunningActivity();
- if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
- "Activity config changed during resume: " + next
- + ", new next: " + nextNext);
- if (nextNext != next) {
- // Do over!
- mStackSupervisor.scheduleResumeTopActivities();
- }
- if (!next.mVisibleRequested || next.stopped) {
- next.setVisibility(true);
- }
- next.completeResumeLocked();
- return true;
- }
-
- try {
- final ClientTransaction transaction =
- ClientTransaction.obtain(next.app.getThread(), next.appToken);
- // Deliver all pending results.
- ArrayList<ResultInfo> a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
- "Delivering results to " + next + ": " + a);
- transaction.addCallback(ActivityResultItem.obtain(a));
- }
- }
-
- if (next.newIntents != null) {
- transaction.addCallback(
- NewIntentItem.obtain(next.newIntents, true /* resume */));
- }
-
- // Well the app will no longer be stopped.
- // Clear app token stopped state in window manager if needed.
- next.notifyAppResumed(next.stopped);
-
- EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
- next.getTask().mTaskId, next.shortComponentName);
-
- next.setSleeping(false);
- mAtmService.getAppWarningsLocked().onResumeActivity(next);
- next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
- next.clearOptionsLocked();
- transaction.setLifecycleStateRequest(
- ResumeActivityItem.obtain(next.app.getReportedProcState(),
- dc.isNextTransitionForward()));
- mAtmService.getLifecycleManager().scheduleTransaction(transaction);
-
- if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
- + next);
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
- + lastState + ": " + next);
- next.setState(lastState, "resumeTopActivityInnerLocked");
-
- // lastResumedActivity being non-null implies there is a lastStack present.
- if (lastResumedActivity != null) {
- lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
- }
-
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedStack != null
- && lastFocusedStack.isTopStackInDisplayArea()) {
- next.showStartingWindow(null /* prev */, false /* newTask */,
- false /* taskSwitch */);
- }
- mStackSupervisor.startSpecificActivity(next, true, false);
- return true;
- }
-
- // From this point on, if something goes wrong there is no way
- // to recover the activity.
- try {
- next.completeResumeLocked();
- } catch (Exception e) {
- // If any exception gets thrown, toss away this
- // activity and try the next one.
- Slog.w(TAG, "Exception thrown during resume of " + next, e);
- next.finishIfPossible("resume-exception", true /* oomAdj */);
- return true;
- }
- } else {
- // Whoops, need to restart this activity!
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_PREVIEW) {
- next.showStartingWindow(null /* prev */, false /* newTask */,
- false /* taskSwich */);
- }
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
- }
- if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
- mStackSupervisor.startSpecificActivity(next, true, true);
- }
-
- return true;
- }
-
- /**
- * Resume the next eligible activity in a focusable stack when this one does not have any
- * running activities left. The focus will be adjusted to the next focusable stack and
- * top running activities will be resumed in all focusable stacks. However, if the current stack
- * is a home stack - we have to keep it focused, start and resume a home activity on the current
- * display instead to make sure that the display is not empty.
- */
- private boolean resumeNextFocusableActivityWhenStackIsEmpty(ActivityRecord prev,
- ActivityOptions options) {
- final String reason = "noMoreActivities";
-
- if (!isActivityTypeHome()) {
- final ActivityStack nextFocusedStack = adjustFocusToNextFocusableTask(reason);
- if (nextFocusedStack != null) {
- // Try to move focus to the next visible stack with a running activity if this
- // stack is not covering the entire screen or is on a secondary display with no home
- // stack.
- return mRootWindowContainer.resumeFocusedStacksTopActivities(nextFocusedStack,
- prev, null /* targetOptions */);
- }
- }
-
- // If the current stack is a home stack, or if focus didn't switch to a different stack -
- // just start up the Launcher...
- ActivityOptions.abort(options);
- if (DEBUG_STATES) Slog.d(TAG_STATES,
- "resumeNextFocusableActivityWhenStackIsEmpty: " + reason + ", go home");
- return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea());
- }
-
- void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
- boolean newTask, boolean keepCurTransition, ActivityOptions options) {
- Task rTask = r.getTask();
- final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
- final boolean isOrhasTask = rTask == this || hasChild(rTask);
- // mLaunchTaskBehind tasks get placed at the back of the task stack.
- if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) {
- // Last activity in task had been removed or ActivityManagerService is reusing task.
- // Insert or replace.
- // Might not even be in.
- positionChildAtTop(rTask);
- }
- Task task = null;
- if (!newTask && isOrhasTask) {
- // Starting activity cannot be occluding activity, otherwise starting window could be
- // remove immediately without transferring to starting activity.
- final ActivityRecord occludingActivity = getOccludingActivityAbove(r);
- if (occludingActivity != null) {
- // Here it is! Now, if this is not yet visible (occluded by another task) to the
- // user, then just add it without starting; it will get started when the user
- // navigates back to it.
- if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task,
- new RuntimeException("here").fillInStackTrace());
- rTask.positionChildAtTop(r);
- ActivityOptions.abort(options);
- return;
- }
- }
-
- // Place a new activity at top of stack, so it is next to interact with the user.
-
- // If we are not placing the new activity frontmost, we do not want to deliver the
- // onUserLeaving callback to the actual frontmost activity
- final Task activityTask = r.getTask();
- if (task == activityTask && mChildren.indexOf(task) != (getChildCount() - 1)) {
- mStackSupervisor.mUserLeaving = false;
- if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
- "startActivity() behind front, mUserLeaving=false");
- }
-
- task = activityTask;
-
- // Slot the activity into the history stack and proceed
- if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
- new RuntimeException("here").fillInStackTrace());
- task.positionChildAtTop(r);
-
- // The transition animation and starting window are not needed if {@code allowMoveToFront}
- // is false, because the activity won't be visible.
- if ((!isHomeOrRecentsStack() || hasActivity()) && allowMoveToFront) {
- final DisplayContent dc = getDisplay().mDisplayContent;
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare open transition: starting " + r);
- if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- dc.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
- mStackSupervisor.mNoAnimActivities.add(r);
- } else {
- int transit = TRANSIT_ACTIVITY_OPEN;
- if (newTask) {
- if (r.mLaunchTaskBehind) {
- transit = TRANSIT_TASK_OPEN_BEHIND;
- } else if (getDisplay().isSingleTaskInstance()) {
- transit = TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
- } else {
- // If a new task is being launched, then mark the existing top activity as
- // supporting picture-in-picture while pausing only if the starting activity
- // would not be considered an overlay on top of the current activity
- // (eg. not fullscreen, or the assistant)
- if (canEnterPipOnTaskSwitch(focusedTopActivity,
- null /* toFrontTask */, r, options)) {
- focusedTopActivity.supportsEnterPipOnTaskSwitch = true;
- }
- transit = TRANSIT_TASK_OPEN;
- }
- }
- dc.prepareAppTransition(transit, keepCurTransition);
- mStackSupervisor.mNoAnimActivities.remove(r);
- }
- boolean doShow = true;
- if (newTask) {
- // Even though this activity is starting fresh, we still need
- // to reset it to make sure we apply affinities to move any
- // existing activities from other tasks in to it.
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- resetTaskIfNeeded(r, r);
- doShow = topRunningNonDelayedActivityLocked(null) == r;
- }
- } else if (options != null && options.getAnimationType()
- == ActivityOptions.ANIM_SCENE_TRANSITION) {
- doShow = false;
- }
- if (r.mLaunchTaskBehind) {
- // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
- // tell WindowManager that r is visible even though it is at the back of the stack.
- r.setVisibility(true);
- ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- // Go ahead to execute app transition for this activity since the app transition
- // will not be triggered through the resume channel.
- getDisplay().mDisplayContent.executeAppTransition();
- } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
- // Figure out if we are transitioning from another activity that is
- // "has the same starting icon" as the next one. This allows the
- // window manager to keep the previous window it had previously
- // created, if it still had one.
- Task prevTask = r.getTask();
- ActivityRecord prev = prevTask.topActivityWithStartingWindow();
- if (prev != null) {
- // We don't want to reuse the previous starting preview if:
- // (1) The current activity is in a different task.
- if (prev.getTask() != prevTask) {
- prev = null;
- }
- // (2) The current activity is already displayed.
- else if (prev.nowVisible) {
- prev = null;
- }
- }
- r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
- }
- } else {
- // If this is the first activity, don't do any fancy animations,
- // because there is nothing for it to animate on top of.
- ActivityOptions.abort(options);
- }
- }
-
- /**
- * @return Whether the switch to another task can trigger the currently running activity to
- * enter PiP while it is pausing (if supported). Only one of {@param toFrontTask} or
- * {@param toFrontActivity} should be set.
- */
- private boolean canEnterPipOnTaskSwitch(ActivityRecord pipCandidate,
- Task toFrontTask, ActivityRecord toFrontActivity, ActivityOptions opts) {
- if (opts != null && opts.disallowEnterPictureInPictureWhileLaunching()) {
- // Ensure the caller has requested not to trigger auto-enter PiP
- return false;
- }
- if (pipCandidate == null || pipCandidate.inPinnedWindowingMode()) {
- // Ensure that we do not trigger entering PiP an activity on the pinned stack
- return false;
- }
- final ActivityStack targetStack = toFrontTask != null
- ? toFrontTask.getStack() : toFrontActivity.getRootTask();
- if (targetStack != null && targetStack.isActivityTypeAssistant()) {
- // Ensure the task/activity being brought forward is not the assistant
- return false;
- }
- return true;
- }
-
- private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) {
- return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
- }
-
- /**
- * Reset the task by reparenting the activities that have same affinity to the task or
- * reparenting the activities that have different affinityies out of the task, while these
- * activities allow task reparenting.
- *
- * @param taskTop Top activity of the task might be reset.
- * @param newActivity The activity that going to be started.
- * @return The non-finishing top activity of the task after reset or the original task top
- * activity if all activities within the task are finishing.
- */
- ActivityRecord resetTaskIfNeeded(ActivityRecord taskTop, ActivityRecord newActivity) {
- final boolean forceReset =
- (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
- final Task task = taskTop.getTask();
-
- // If ActivityOptions are moved out and need to be aborted or moved to taskTop.
- final ActivityOptions topOptions = sResetTargetTaskHelper.process(task, forceReset);
-
- if (mChildren.contains(task)) {
- final ActivityRecord newTop = task.getTopNonFinishingActivity();
- if (newTop != null) {
- taskTop = newTop;
- }
- }
-
- if (topOptions != null) {
- // If we got some ActivityOptions from an activity on top that
- // was removed from the task, propagate them to the new real top.
- taskTop.updateOptionsLocked(topOptions);
- }
-
- return taskTop;
- }
-
- /**
- * Finish the topmost activity that belongs to the crashed app. We may also finish the activity
- * that requested launch of the crashed one to prevent launch-crash loop.
- * @param app The app that crashed.
- * @param reason Reason to perform this action.
- * @return The task that was finished in this stack, {@code null} if top running activity does
- * not belong to the crashed app.
- */
- final Task finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
- final ActivityRecord r = topRunningActivity();
- if (r == null || r.app != app) {
- return null;
- }
- if (r.isActivityTypeHome() && mAtmService.mHomeProcess == app) {
- // Home activities should not be force-finished as we have nothing else to go
- // back to. AppErrors will get to it after two crashes in MIN_CRASH_INTERVAL.
- Slog.w(TAG, " Not force finishing home activity "
- + r.intent.getComponent().flattenToShortString());
- return null;
- }
- Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- Task finishedTask = r.getTask();
- getDisplay().mDisplayContent.prepareAppTransition(
- TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
- r.finishIfPossible(reason, false /* oomAdj */);
-
- // Also terminate any activities below it that aren't yet stopped, to avoid a situation
- // where one will get re-start our crashing activity once it gets resumed again.
- final ActivityRecord activityBelow = getActivityBelow(r);
- if (activityBelow != null) {
- if (activityBelow.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
- if (!activityBelow.isActivityTypeHome()
- || mAtmService.mHomeProcess != activityBelow.app) {
- Slog.w(TAG, " Force finishing activity "
- + activityBelow.intent.getComponent().flattenToShortString());
- activityBelow.finishIfPossible(reason, false /* oomAdj */);
- }
- }
- }
-
- return finishedTask;
- }
-
- void finishVoiceTask(IVoiceInteractionSession session) {
- final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::finishIfVoiceTask,
- PooledLambda.__(Task.class), session.asBinder());
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
- private static void finishIfVoiceTask(Task tr, IBinder binder) {
- if (tr.voiceSession != null && tr.voiceSession.asBinder() == binder) {
- tr.forAllActivities((r) -> {
- if (r.finishing) return;
- r.finishIfPossible("finish-voice", false /* oomAdj */);
- tr.mAtmService.updateOomAdj();
- });
- } else {
- // Check if any of the activities are using voice
- final PooledFunction f = PooledLambda.obtainFunction(
- ActivityStack::finishIfVoiceActivity, PooledLambda.__(ActivityRecord.class),
- binder);
- tr.forAllActivities(f);
- f.recycle();
- }
- }
-
- private static boolean finishIfVoiceActivity(ActivityRecord r, IBinder binder) {
- if (r.voiceSession == null || r.voiceSession.asBinder() != binder) return false;
- // Inform of cancellation
- r.clearVoiceSessionLocked();
- try {
- r.app.getThread().scheduleLocalVoiceInteractionStarted(r.appToken, null);
- } catch (RemoteException re) {
- // Ok Boomer...
- }
- r.mAtmService.finishRunningVoiceLocked();
- return true;
- }
-
- /** Finish all activities in the stack without waiting. */
- void finishAllActivitiesImmediately() {
- if (!hasChild()) {
- removeIfPossible();
- return;
- }
- forAllActivities((r) -> {
- Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
- r.destroyIfPossible("finishAllActivitiesImmediately");
- });
- }
-
- /** @return true if the stack behind this one is a standard activity type. */
- private boolean inFrontOfStandardStack() {
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
- if (taskDisplayArea == null) {
- return false;
- }
- final int index = taskDisplayArea.getIndexOf(this);
- if (index == 0) {
- return false;
- }
- final ActivityStack stackBehind = taskDisplayArea.getChildAt(index - 1);
- return stackBehind.isActivityTypeStandard();
- }
-
- boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
- // Basic case: for simple app-centric recents, we need to recreate
- // the task if the affinity has changed.
-
- final String affinity = ActivityRecord.getTaskAffinityWithUid(destAffinity, srec.getUid());
- if (srec == null || srec.getTask().affinity == null
- || !srec.getTask().affinity.equals(affinity)) {
- return true;
- }
- // Document-centric case: an app may be split in to multiple documents;
- // they need to re-create their task if this current activity is the root
- // of a document, unless simply finishing it will return them to the the
- // correct app behind.
- final Task task = srec.getTask();
- if (srec.isRootOfTask() && task.getBaseIntent() != null
- && task.getBaseIntent().isDocument()) {
- // Okay, this activity is at the root of its task. What to do, what to do...
- if (!inFrontOfStandardStack()) {
- // Finishing won't return to an application, so we need to recreate.
- return true;
- }
- // We now need to get the task below it to determine what to do.
- final Task prevTask = getTaskBelow(task);
- if (prevTask == null) {
- Slog.w(TAG, "shouldUpRecreateTask: task not in history for " + srec);
- return false;
- }
- if (!task.affinity.equals(prevTask.affinity)) {
- // These are different apps, so need to recreate.
- return true;
- }
- }
- return false;
- }
-
- boolean navigateUpTo(ActivityRecord srec, Intent destIntent, NeededUriGrants destGrants,
- int resultCode, Intent resultData, NeededUriGrants resultGrants) {
- if (!srec.attachedToProcess()) {
- // Nothing to do if the caller is not attached, because this method should be called
- // from an alive activity.
- return false;
- }
- final Task task = srec.getTask();
- if (!srec.isDescendantOf(this)) {
- return false;
- }
-
- ActivityRecord parent = task.getActivityBelow(srec);
- boolean foundParentInTask = false;
- final ComponentName dest = destIntent.getComponent();
- if (task.getBottomMostActivity() != srec && dest != null) {
- final ActivityRecord candidate = task.getActivity((ar) ->
- ar.info.packageName.equals(dest.getPackageName()) &&
- ar.info.name.equals(dest.getClassName()), srec, false /*includeBoundary*/,
- true /*traverseTopToBottom*/);
- if (candidate != null) {
- parent = candidate;
- foundParentInTask = true;
- }
- }
-
- // TODO: There is a dup. of this block of code in ActivityTaskManagerService.finishActivity
- // We should consolidate.
- IActivityController controller = mAtmService.mController;
- if (controller != null) {
- ActivityRecord next = topRunningActivity(srec.appToken, INVALID_TASK_ID);
- if (next != null) {
- // ask watcher if this is allowed
- boolean resumeOK = true;
- try {
- resumeOK = controller.activityResuming(next.packageName);
- } catch (RemoteException e) {
- mAtmService.mController = null;
- Watchdog.getInstance().setActivityController(null);
- }
-
- if (!resumeOK) {
- return false;
- }
- }
- }
- final long origId = Binder.clearCallingIdentity();
-
- final int[] resultCodeHolder = new int[1];
- resultCodeHolder[0] = resultCode;
- final Intent[] resultDataHolder = new Intent[1];
- resultDataHolder[0] = resultData;
- final NeededUriGrants[] resultGrantsHolder = new NeededUriGrants[1];
- resultGrantsHolder[0] = resultGrants;
- final ActivityRecord finalParent = parent;
- task.forAllActivities((ar) -> {
- if (ar == finalParent) return true;
-
- ar.finishIfPossible(resultCodeHolder[0], resultDataHolder[0], resultGrantsHolder[0],
- "navigate-up", true /* oomAdj */);
- // Only return the supplied result for the first activity finished
- resultCodeHolder[0] = Activity.RESULT_CANCELED;
- resultDataHolder[0] = null;
- return false;
- }, srec, true, true);
- resultCode = resultCodeHolder[0];
- resultData = resultDataHolder[0];
-
- if (parent != null && foundParentInTask) {
- final int callingUid = srec.info.applicationInfo.uid;
- final int parentLaunchMode = parent.info.launchMode;
- final int destIntentFlags = destIntent.getFlags();
- if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
- parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
- parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
- (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, srec.packageName);
- } else {
- try {
- ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
- destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
- srec.mUserId);
- // TODO(b/64750076): Check if calling pid should really be -1.
- final int res = mAtmService.getActivityStartController()
- .obtainStarter(destIntent, "navigateUpTo")
- .setCaller(srec.app.getThread())
- .setActivityInfo(aInfo)
- .setResultTo(parent.appToken)
- .setCallingPid(-1)
- .setCallingUid(callingUid)
- .setCallingPackage(srec.packageName)
- .setCallingFeatureId(parent.launchedFromFeatureId)
- .setRealCallingPid(-1)
- .setRealCallingUid(callingUid)
- .setComponentSpecified(true)
- .execute();
- foundParentInTask = res == ActivityManager.START_SUCCESS;
- } catch (RemoteException e) {
- foundParentInTask = false;
- }
- parent.finishIfPossible(resultCode, resultData, resultGrants,
- "navigate-top", true /* oomAdj */);
- }
- }
- Binder.restoreCallingIdentity(origId);
- return foundParentInTask;
- }
-
- void removeLaunchTickMessages() {
- forAllActivities(ActivityRecord::removeLaunchTickRunnable);
- }
-
- private void updateTransitLocked(int transit, ActivityOptions options) {
- if (options != null) {
- ActivityRecord r = topRunningActivity();
- if (r != null && !r.isState(RESUMED)) {
- r.updateOptionsLocked(options);
- } else {
- ActivityOptions.abort(options);
- }
- }
- getDisplay().mDisplayContent.prepareAppTransition(transit, false);
- }
-
- final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
- AppTimeTracker timeTracker, String reason) {
- moveTaskToFront(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
- }
-
- final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
- AppTimeTracker timeTracker, boolean deferResume, String reason) {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
-
- final ActivityStack topStack = getDisplayArea().getTopStack();
- final ActivityRecord topActivity = topStack != null
- ? topStack.getTopNonFinishingActivity() : null;
-
- if (tr != this && !tr.isDescendantOf(this)) {
- // nothing to do!
- if (noAnimation) {
- ActivityOptions.abort(options);
- } else {
- updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
- }
- return;
- }
-
- if (timeTracker != null) {
- // The caller wants a time tracker associated with this task.
- final PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setAppTimeTracker,
- PooledLambda.__(ActivityRecord.class), timeTracker);
- tr.forAllActivities(c);
- c.recycle();
- }
-
- try {
- // Defer updating the IME target since the new IME target will try to get computed
- // before updating all closing and opening apps, which can cause the ime target to
- // get calculated incorrectly.
- getDisplay().deferUpdateImeTarget();
-
- // Shift all activities with this task up to the top
- // of the stack, keeping them in the same internal order.
- positionChildAtTop(tr);
-
- // Don't refocus if invisible to current user
- final ActivityRecord top = tr.getTopNonFinishingActivity();
- if (top == null || !top.okToShowLocked()) {
- if (top != null) {
- mStackSupervisor.mRecentTasks.add(top.getTask());
- }
- ActivityOptions.abort(options);
- return;
- }
-
- // Set focus to the top running activity of this stack.
- final ActivityRecord r = topRunningActivity();
- if (r != null) {
- r.moveFocusableActivityToTop(reason);
- }
-
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
- if (noAnimation) {
- getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_NONE, false);
- if (r != null) {
- mStackSupervisor.mNoAnimActivities.add(r);
- }
- ActivityOptions.abort(options);
- } else {
- updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
- }
- // If a new task is moved to the front, then mark the existing top activity as
- // supporting
-
- // picture-in-picture while paused only if the task would not be considered an oerlay
- // on top
- // of the current activity (eg. not fullscreen, or the assistant)
- if (canEnterPipOnTaskSwitch(topActivity, tr, null /* toFrontActivity */,
- options)) {
- topActivity.supportsEnterPipOnTaskSwitch = true;
- }
-
- if (!deferResume) {
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
- EventLogTags.writeWmTaskToFront(tr.mUserId, tr.mTaskId);
- mAtmService.getTaskChangeNotificationController()
- .notifyTaskMovedToFront(tr.getTaskInfo());
- } finally {
- getDisplay().continueUpdateImeTarget();
- }
- }
-
- /**
- * Worker method for rearranging history stack. Implements the function of moving all
- * activities for a specific task (gathering them if disjoint) into a single group at the
- * bottom of the stack.
- *
- * If a watcher is installed, the action is preflighted and the watcher has an opportunity
- * to premeptively cancel the move.
- *
- * @param tr The task to collect and move to the bottom.
- * @return Returns true if the move completed, false if not.
- */
- boolean moveTaskToBack(Task tr) {
- Slog.i(TAG, "moveTaskToBack: " + tr);
-
- // In LockTask mode, moving a locked task to the back of the stack may expose unlocked
- // ones. Therefore we need to check if this operation is allowed.
- if (!mAtmService.getLockTaskController().canMoveTaskToBack(tr)) {
- return false;
- }
-
- // If we have a watcher, preflight the move before committing to it. First check
- // for *other* available tasks, but if none are available, then try again allowing the
- // current task to be selected.
- if (isTopStackInDisplayArea() && mAtmService.mController != null) {
- ActivityRecord next = topRunningActivity(null, tr.mTaskId);
- if (next == null) {
- next = topRunningActivity(null, INVALID_TASK_ID);
- }
- if (next != null) {
- // ask watcher if this is allowed
- boolean moveOK = true;
- try {
- moveOK = mAtmService.mController.activityResuming(next.packageName);
- } catch (RemoteException e) {
- mAtmService.mController = null;
- Watchdog.getInstance().setActivityController(null);
- }
- if (!moveOK) {
- return false;
- }
- }
- }
-
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
- + tr.mTaskId);
-
- getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
- moveToBack("moveTaskToBackLocked", tr);
-
- if (inPinnedWindowingMode()) {
- mStackSupervisor.removeStack(this);
- return true;
- }
-
- ActivityRecord topActivity = getDisplayArea().topRunningActivity();
- ActivityStack topStack = topActivity.getRootTask();
- if (topStack != null && topStack != this && topActivity.isState(RESUMED)) {
- // The new top activity is already resumed, so there's a good chance that nothing will
- // get resumed below. So, update visibility now in case the transition is closed
- // prematurely.
- mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
- getDisplay().mDisplayId, false /* markFrozenIfConfigChanged */,
- false /* deferResume */);
- // Usually resuming a top activity triggers the next app transition, but nothing's got
- // resumed in this case, so we need to execute it explicitly.
- getDisplay().mDisplayContent.executeAppTransition();
- } else {
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
- return true;
- }
-
- /**
- * Ensures all visible activities at or below the input activity have the right configuration.
- */
- void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
- mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
- }
-
- // TODO: Can only be called from special methods in ActivityStackSupervisor.
- // Need to consolidate those calls points into this resize method so anyone can call directly.
- void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + getRootTaskId());
- mAtmService.deferWindowLayout();
- try {
- // TODO: Why not just set this on the stack directly vs. on each tasks?
- // Update override configurations of all tasks in the stack.
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityStack::processTaskResizeBounds, PooledLambda.__(Task.class),
- displayedBounds);
- forAllTasks(c, true /* traverseTopToBottom */);
- c.recycle();
-
- if (mBoundsAnimating) {
- // Force to update task surface bounds and relayout windows, since configBounds
- // remains unchanged during bounds animation.
- updateSurfaceBounds();
- getDisplay().setLayoutNeeded();
- mWmService.requestTraversal();
- }
-
- if (!deferResume) {
- ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows);
- }
- } finally {
- mAtmService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- }
-
- private static void processTaskResizeBounds(Task task, Rect displayedBounds) {
- if (!task.isResizeable()) return;
-
- task.setBounds(displayedBounds);
- }
-
- /**
- * Until we can break this "set task bounds to same as stack bounds" behavior, this
- * basically resizes both stack and task bounds to the same bounds.
- */
- private void setTaskBounds(Rect bounds) {
- final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskBounds,
- PooledLambda.__(Task.class), bounds);
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
- private static void setTaskBounds(Task task, Rect bounds) {
- task.setBounds(task.isResizeable() ? bounds : null);
- }
-
- /**
- * Returns the top-most activity that occludes the given one, or @{code null} if none.
- */
- @Nullable
- private ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
- ActivityRecord top = getActivity((ar) -> ar.occludesParent(),
- true /* traverseTopToBottom */, activity);
- return top != activity ? top : null;
- }
-
- boolean willActivityBeVisible(IBinder token) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r == null) {
- return false;
- }
-
- // See if there is an occluding activity on-top of this one.
- final ActivityRecord occludingActivity = getOccludingActivityAbove(r);
- if (occludingActivity != null) return false;
-
- if (r.finishing) Slog.e(TAG, "willActivityBeVisible: Returning false,"
- + " would have returned true for r=" + r);
- return !r.finishing;
- }
-
- void unhandledBackLocked() {
- final ActivityRecord topActivity = getTopMostActivity();
- if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
- "Performing unhandledBack(): top activity: " + topActivity);
- if (topActivity != null) {
- topActivity.finishIfPossible("unhandled-back", true /* oomAdj */);
- }
- }
-
- /**
- * Reset local parameters because an app's activity died.
- * @param app The app of the activity that died.
- * @return result from removeHistoryRecordsForAppLocked.
- */
- boolean handleAppDied(WindowProcessController app) {
- if (mPausingActivity != null && mPausingActivity.app == app) {
- if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
- "App died while pausing: " + mPausingActivity);
- mPausingActivity = null;
- }
- if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
- mLastPausedActivity = null;
- mLastNoHistoryActivity = null;
- }
-
- mStackSupervisor.removeHistoryRecords(app);
- return mRemoveHistoryRecordsForApp.process(app);
- }
-
- boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
- String dumpPackage, final boolean needSep) {
- Runnable headerPrinter = () -> {
- if (needSep) {
- pw.println();
- }
- pw.println(" Stack #" + getRootTaskId()
- + ": type=" + activityTypeToString(getActivityType())
- + " mode=" + windowingModeToString(getWindowingMode()));
- pw.println(" isSleeping=" + shouldSleepActivities());
- pw.println(" mBounds=" + getRequestedOverrideBounds());
- };
-
- boolean printed = false;
-
- if (dumpPackage == null) {
- // If we are not filtering by package, we want to print absolutely everything,
- // so always print the header even if there are no tasks/activities inside.
- headerPrinter.run();
- headerPrinter = null;
- printed = true;
- }
-
- printed |= printThisActivity(pw, mPausingActivity, dumpPackage, false,
- " mPausingActivity: ", null);
- printed |= printThisActivity(pw, getResumedActivity(), dumpPackage, false,
- " mResumedActivity: ", null);
- if (dumpAll) {
- printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
- " mLastPausedActivity: ", null);
- printed |= printThisActivity(pw, mLastNoHistoryActivity, dumpPackage,
- false, " mLastNoHistoryActivity: ", null);
- }
-
- printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter);
-
- return printed;
- }
-
- private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
- boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) {
- if (!hasChild()) {
- return false;
- }
- final AtomicBoolean printedHeader = new AtomicBoolean(false);
- final AtomicBoolean printed = new AtomicBoolean(false);
- forAllLeafTasks((task) -> {
- final String prefix = " ";
- Runnable headerPrinter = () -> {
- printed.set(true);
- if (!printedHeader.get()) {
- if (needSep) {
- pw.println("");
- }
- if (header != null) {
- header.run();
- }
- printedHeader.set(true);
- }
- pw.print(prefix); pw.print("* "); pw.println(task);
- pw.print(prefix); pw.print(" mBounds=");
- pw.println(task.getRequestedOverrideBounds());
- pw.print(prefix); pw.print(" mMinWidth="); pw.print(task.mMinWidth);
- pw.print(" mMinHeight="); pw.println(task.mMinHeight);
- if (mLastNonFullscreenBounds != null) {
- pw.print(prefix);
- pw.print(" mLastNonFullscreenBounds=");
- pw.println(task.mLastNonFullscreenBounds);
- }
- task.dump(pw, prefix + " ");
- };
- if (dumpPackage == null) {
- // If we are not filtering by package, we want to print absolutely everything,
- // so always print the header even if there are no activities inside.
- headerPrinter.run();
- headerPrinter = null;
- }
- final ArrayList<ActivityRecord> activities = new ArrayList<>();
- // Add activities by traversing the hierarchy from bottom to top, since activities
- // are dumped in reverse order in {@link ActivityStackSupervisor#dumpHistoryList()}.
- task.forAllActivities((Consumer<ActivityRecord>) activities::add,
- false /* traverseTopToBottom */);
- dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
- dumpPackage, false, headerPrinter, task);
- }, true /* traverseTopToBottom */);
- return printed.get();
- }
-
- ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
- ArrayList<ActivityRecord> activities = new ArrayList<>();
-
- if ("all".equals(name)) {
- forAllActivities((Consumer<ActivityRecord>) activities::add);
- } else if ("top".equals(name)) {
- final ActivityRecord topActivity = getTopMostActivity();
- if (topActivity != null) {
- activities.add(topActivity);
- }
- } else {
- ItemMatcher matcher = new ItemMatcher();
- matcher.build(name);
-
- forAllActivities((r) -> {
- if (matcher.match(r, r.intent.getComponent())) {
- activities.add(r);
- }
- });
- }
-
- return activities;
- }
-
- ActivityRecord restartPackage(String packageName) {
- ActivityRecord starting = topRunningActivity();
-
- // All activities that came from the package must be
- // restarted as if there was a config change.
- PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::restartPackage,
- PooledLambda.__(ActivityRecord.class), starting, packageName);
- forAllActivities(c);
- c.recycle();
-
- return starting;
- }
-
- private static void restartPackage(
- ActivityRecord r, ActivityRecord starting, String packageName) {
- if (r.info.packageName.equals(packageName)) {
- r.forceNewConfig = true;
- if (starting != null && r == starting && r.mVisibleRequested) {
- r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
- }
- }
- }
-
- Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) {
- return reuseOrCreateTask(info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
- toTop, null /*activity*/, null /*source*/, null /*options*/);
- }
- // TODO: Can be removed once we change callpoints creating stacks to be creating tasks.
- /** Either returns this current task to be re-used or creates a new child task. */
- Task reuseOrCreateTask(ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession,
- IVoiceInteractor voiceInteractor, boolean toTop, ActivityRecord activity,
- ActivityRecord source, ActivityOptions options) {
-
- Task task;
- if (DisplayContent.alwaysCreateStack(getWindowingMode(), getActivityType())) {
- // This stack will only contain one task, so just return itself since all stacks ara now
- // tasks and all tasks are now stacks.
- task = reuseAsLeafTask(voiceSession, voiceInteractor, intent, info, activity);
- } else {
- // Create child task since this stack can contain multiple tasks.
- final int taskId = activity != null
- ? mStackSupervisor.getNextTaskIdForUser(activity.mUserId)
- : mStackSupervisor.getNextTaskIdForUser();
- task = new ActivityStack(mAtmService, taskId, info, intent, voiceSession,
- voiceInteractor, null /* taskDescription */, this);
-
- // add the task to stack first, mTaskPositioner might need the stack association
- addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
- }
-
- int displayId = getDisplayId();
- if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
- final boolean isLockscreenShown = mAtmService.mStackSupervisor.getKeyguardController()
- .isKeyguardOrAodShowing(displayId);
- if (!mStackSupervisor.getLaunchParamsController()
- .layoutTask(task, info.windowLayout, activity, source, options)
- && !getRequestedOverrideBounds().isEmpty()
- && task.isResizeable() && !isLockscreenShown) {
- task.setBounds(getRequestedOverrideBounds());
- }
-
- return task;
- }
-
- void addChild(WindowContainer child, final boolean toTop, boolean showForAllUsers) {
- if (isSingleTaskInstance() && hasChild()) {
- throw new IllegalStateException("Can only have one child on stack=" + this);
- }
-
- Task task = child.asTask();
- try {
-
- if (task != null) {
- task.setForceShowForAllUsers(showForAllUsers);
- }
- // We only want to move the parents to the parents if we are creating this task at the
- // top of its stack.
- addChild(child, toTop ? MAX_VALUE : 0, toTop /*moveParents*/);
- } finally {
- if (task != null) {
- task.setForceShowForAllUsers(false);
- }
- }
- }
-
- void positionChildAt(Task task, int position) {
- if (task.getStack() != this) {
- throw new IllegalArgumentException("AS.positionChildAt: task=" + task
- + " is not a child of stack=" + this + " current parent=" + task.getStack());
- }
-
- task.updateOverrideConfigurationForStack(this);
-
- final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
- final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity;
-
- boolean toTop = position >= getChildCount();
- boolean includingParents = toTop || getDisplayArea().getNextFocusableStack(this,
- true /* ignoreCurrent */) == null;
- if (WindowManagerDebugConfig.DEBUG_STACK) {
- Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position);
- }
- positionChildAt(position, task, includingParents);
- task.updateTaskMovement(toTop);
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-
-
- // TODO: Investigate if this random code is really needed.
- if (task.voiceSession != null) {
- try {
- task.voiceSession.taskStarted(task.intent, task.mTaskId);
- } catch (RemoteException e) {
- }
- }
-
- if (wasResumed) {
- if (mResumedActivity != null) {
- Log.wtf(TAG, "mResumedActivity was already set when moving mResumedActivity from"
- + " other stack to this stack mResumedActivity=" + mResumedActivity
- + " other mResumedActivity=" + topRunningActivity);
- }
- topRunningActivity.setState(RESUMED, "positionChildAt");
- }
-
- // The task might have already been running and its visibility needs to be synchronized with
- // the visibility of the stack / windows.
- ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- mRootWindowContainer.resumeFocusedStacksTopActivities();
- }
-
- public void setAlwaysOnTop(boolean alwaysOnTop) {
- if (isAlwaysOnTop() == alwaysOnTop) {
- return;
- }
- super.setAlwaysOnTop(alwaysOnTop);
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
- // positionChildAtTop() must be called even when always on top gets turned off because we
- // need to make sure that the stack is moved from among always on top windows to below other
- // always on top windows. Since the position the stack should be inserted into is calculated
- // properly in {@link DisplayContent#getTopInsertPosition()} in both cases, we can just
- // request that the stack is put at top here.
- taskDisplayArea.positionStackAtTop(this, false /* includingParents */);
- }
-
- /** NOTE: Should only be called from {@link Task#reparent}. */
- void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
- boolean setPause, String reason) {
- if (!moveToFront) {
- return;
- }
-
- final ActivityState origState = r.getState();
- // If the activity owns the last resumed activity, transfer that together,
- // so that we don't resume the same activity again in the new stack.
- // Apps may depend on onResume()/onPause() being called in pairs.
- if (setResume) {
- r.setState(RESUMED, "moveToFrontAndResumeStateIfNeeded");
- }
- // If the activity was previously pausing, then ensure we transfer that as well
- if (setPause) {
- mPausingActivity = r;
- r.schedulePauseTimeout();
- }
- // Move the stack in which we are placing the activity to the front.
- moveToFront(reason);
- // If the original state is resumed, there is no state change to update focused app.
- // So here makes sure the activity focus is set if it is the top.
- if (origState == RESUMED && r == mRootWindowContainer.getTopResumedActivity()) {
- mAtmService.setResumedActivityUncheckLocked(r, reason);
- }
- }
-
- void dismissPip() {
- if (!isActivityTypeStandardOrUndefined()) {
- throw new IllegalArgumentException(
- "You can't move tasks from non-standard stacks.");
- }
- if (getWindowingMode() != WINDOWING_MODE_PINNED) {
- throw new IllegalArgumentException(
- "Can't exit pinned mode if it's not pinned already.");
- }
-
- mWmService.inSurfaceTransaction(() -> {
- final Task task = getBottomMostTask();
- setWindowingMode(WINDOWING_MODE_UNDEFINED);
-
- getDisplayArea().positionStackAtTop(this, false /* includingParents */);
-
- mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
- MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext,
- task.effectiveUid, task.realActivity.flattenToString());
- });
- }
-
- void prepareFreezingTaskBounds() {
- forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */);
- }
-
- @Override
- public int setBounds(Rect bounds) {
- // Calling Task#setBounds() for leaf task since this is the a specialization of
- // {@link #setBounds(int)} for ActivityStack.
- if (!isRootTask()) {
- return super.setBounds(bounds);
- } else {
- return setBounds(getRequestedOverrideBounds(), bounds);
- }
- }
-
- private int setBounds(Rect existing, Rect bounds) {
- if (equivalentBounds(existing, bounds)) {
- return BOUNDS_CHANGE_NONE;
- }
-
- final int result = super.setBounds(!inMultiWindowMode() ? null : bounds);
-
- updateSurfaceBounds();
- return result;
- }
-
- /** Bounds of the stack without adjusting for other factors in the system like visibility
- * of docked stack.
- * Most callers should be using {@link ConfigurationContainer#getRequestedOverrideBounds} a
- * it takes into consideration other system factors. */
- void getRawBounds(Rect out) {
- out.set(getRawBounds());
- }
-
- private Rect getRawBounds() {
- return super.getBounds();
- }
-
- @Override
- public void getBounds(Rect bounds) {
- bounds.set(getBounds());
- }
-
- /**
- * @return the final bounds for the bounds animation.
- */
- void getFinalAnimationBounds(Rect outBounds) {
- outBounds.set(mBoundsAnimationTarget);
- }
-
- /**
- * @return the final source bounds for the bounds animation.
- */
- void getFinalAnimationSourceHintBounds(Rect outBounds) {
- outBounds.set(mBoundsAnimationSourceHintBounds);
- }
-
- /** Bounds of the stack with other system factors taken into consideration. */
- void getDimBounds(Rect out) {
- getBounds(out);
- }
-
- /**
- * Put a Task in this stack. Used for adding only.
- * When task is added to top of the stack, the entire branch of the hierarchy (including stack
- * and display) will be brought to top.
- * @param child The child to add.
- * @param position Target position to add the task to.
- */
- private void addChild(WindowContainer child, int position, boolean moveParents) {
- // Add child task.
- addChild(child, null);
-
- // Move child to a proper position, as some restriction for position might apply.
- positionChildAt(position, child, moveParents /* includingParents */);
- }
-
- void positionChildAtTop(Task child) {
- if (child == null) {
- // TODO: Fix the call-points that cause this to happen.
- return;
- }
-
- if (child == this) {
- // TODO: Fix call-points
- moveToFront("positionChildAtTop");
- return;
- }
-
- positionChildAt(POSITION_TOP, child, true /* includingParents */);
- child.updateTaskMovement(true);
-
- final DisplayContent displayContent = getDisplayContent();
- displayContent.layoutAndAssignWindowLayersIfNeeded();
- }
-
- void positionChildAtBottom(Task child) {
- // If there are other focusable stacks on the display, the z-order of the display should not
- // be changed just because a task was placed at the bottom. E.g. if it is moving the topmost
- // task to bottom, the next focusable stack on the same display should be focused.
- final ActivityStack nextFocusableStack = getDisplayArea().getNextFocusableStack(
- child.getStack(), true /* ignoreCurrent */);
- positionChildAtBottom(child, nextFocusableStack == null /* includingParents */);
- child.updateTaskMovement(true);
- }
-
- @VisibleForTesting
- void positionChildAtBottom(Task child, boolean includingParents) {
- if (child == null) {
- // TODO: Fix the call-points that cause this to happen.
- return;
- }
-
- positionChildAt(POSITION_BOTTOM, child, includingParents);
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
-
- @Override
- void onChildPositionChanged(WindowContainer child) {
- if (isOrganized()) {
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
- }
-
- if (!mChildren.contains(child)) {
- return;
- }
-
- final boolean isTop = getTopChild() == child;
-
- final Task task = child.asTask();
- if (task != null) {
- task.updateTaskMovement(isTop);
- }
-
- if (isTop) {
- final DisplayContent displayContent = getDisplayContent();
- displayContent.layoutAndAssignWindowLayersIfNeeded();
- }
- }
-
- @Override
- void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- final DisplayContent display = newParent != null
- ? ((WindowContainer) newParent).getDisplayContent() : null;
- final DisplayContent oldDisplay = oldParent != null
- ? ((WindowContainer) oldParent).getDisplayContent() : null;
- super.onParentChanged(newParent, oldParent);
-
- // Resume next focusable stack after reparenting to another display if we aren't removing
- // the prevous display.
- if (oldDisplay != null && oldDisplay.isRemoving()) {
- postReparent();
- }
- }
-
- void reparent(TaskDisplayArea newParent, boolean onTop) {
- reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
- }
-
- private void updateSurfaceBounds() {
- updateSurfaceSize(getSyncTransaction());
- updateSurfacePosition();
- scheduleAnimation();
- }
-
- @Override
- void getRelativePosition(Point outPos) {
- super.getRelativePosition(outPos);
- final int outset = getTaskOutset();
- outPos.x -= outset;
- outPos.y -= outset;
- }
-
- @Override
- void onDisplayChanged(DisplayContent dc) {
- super.onDisplayChanged(dc);
- if (isRootTask()) {
- updateSurfaceBounds();
- }
- }
-
- boolean shouldIgnoreInput() {
- if (inSplitScreenPrimaryWindowingMode() && !isFocusable()) {
- return true;
- }
- if (mAtmService.mHasLeanbackFeature && inPinnedWindowingMode()
- && !isFocusedStackOnDisplay()) {
- // Preventing Picture-in-Picture stack from receiving input on TVs.
- return true;
- }
- return false;
- }
-
- @Override
- void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- if (mDeferRemoval) {
- pw.println(prefix + "mDeferRemoval=true");
- }
- super.dump(pw, prefix, dumpAll);
- if (!mExitingActivities.isEmpty()) {
- pw.println();
- pw.println(prefix + "Exiting application tokens:");
- final String doublePrefix = prefix + " ";
- for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
- WindowToken token = mExitingActivities.get(i);
- pw.print(doublePrefix + "Exiting App #" + i);
- pw.print(' '); pw.print(token);
- pw.println(':');
- token.dump(pw, doublePrefix, dumpAll);
- }
- pw.println();
- }
- mAnimatingActivityRegistry.dump(pw, "AnimatingApps:", prefix);
- }
-
- /**
- * Sets the current picture-in-picture aspect ratio.
- */
- void setPictureInPictureAspectRatio(float aspectRatio) {
- if (!mWmService.mAtmService.mSupportsPictureInPicture) {
- return;
- }
-
- final DisplayContent displayContent = getDisplayContent();
- if (displayContent == null) {
- return;
- }
-
- if (!inPinnedWindowingMode()) {
- return;
- }
-
- final PinnedStackController pinnedStackController =
- getDisplayContent().getPinnedStackController();
-
- if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
- return;
- }
-
- // 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);
- }
-
- /**
- * Sets the current picture-in-picture actions.
- */
- void setPictureInPictureActions(List<RemoteAction> actions) {
- if (!mWmService.mAtmService.mSupportsPictureInPicture) {
- return;
- }
-
- if (!inPinnedWindowingMode()) {
- return;
- }
-
- getDisplayContent().getPinnedStackController().setActions(actions);
- }
-
- public boolean isForceScaled() {
- return mBoundsAnimating;
- }
-
- /** Returns true if a removal action is still being deferred. */
- boolean checkCompleteDeferredRemoval() {
- if (isAnimating(TRANSITION | CHILDREN)) {
- return true;
- }
- if (mDeferRemoval) {
- removeImmediately();
- }
-
- return super.checkCompleteDeferredRemoval();
- }
-
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- AnimatingActivityRegistry getAnimatingActivityRegistry() {
- return mAnimatingActivityRegistry;
- }
-
- void executeAppTransition(ActivityOptions options) {
- getDisplay().mDisplayContent.executeAppTransition();
- ActivityOptions.abort(options);
- }
-
- boolean shouldSleepActivities() {
- final DisplayContent display = getDisplay();
-
- // Do not sleep activities in this stack if we're marked as focused and the keyguard
- // is in the process of going away.
- if (isFocusedStackOnDisplay()
- && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
- return false;
- }
-
- return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
- }
-
- boolean shouldSleepOrShutDownActivities() {
- return shouldSleepActivities() || mAtmService.mShuttingDown;
- }
-
- @Override
- public void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
- return;
- }
-
- final long token = proto.start(fieldId);
- super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
-
- proto.write(TaskProto.ID, mTaskId);
- proto.write(DISPLAY_ID, getDisplayId());
- proto.write(ROOT_TASK_ID, getRootTaskId());
-
- if (mResumedActivity != null) {
- mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
- }
- if (realActivity != null) {
- proto.write(REAL_ACTIVITY, realActivity.flattenToShortString());
- }
- if (origActivity != null) {
- proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString());
- }
- proto.write(ACTIVITY_TYPE, getActivityType());
- proto.write(RESIZE_MODE, mResizeMode);
- proto.write(MIN_WIDTH, mMinWidth);
- proto.write(MIN_HEIGHT, mMinHeight);
-
- proto.write(FILLS_PARENT, matchParentBounds());
- getRawBounds().dumpDebug(proto, BOUNDS);
-
- if (mLastNonFullscreenBounds != null) {
- mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS);
- }
-
- proto.write(DEFER_REMOVAL, mDeferRemoval);
- proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
-
- if (mSurfaceControl != null) {
- proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
- proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
- }
-
- proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
-
- proto.end(token);
- }
-}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 9b2a06f60aa3..04b1edc3eede 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -46,9 +46,6 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.TYPE_VIRTUAL;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.TAG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
@@ -74,11 +71,14 @@ import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_
import static com.android.server.wm.RootWindowContainer.TAG_STATES;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.Task.ActivityState.PAUSING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.wm.Task.TAG_CLEANUP;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -380,12 +380,12 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final ActivityRecord r;
final ActivityRecord sourceRecord;
final int startFlags;
- final ActivityStack stack;
+ final Task stack;
final WindowProcessController callerApp;
final NeededUriGrants intentGrants;
PendingActivityLaunch(ActivityRecord r, ActivityRecord sourceRecord,
- int startFlags, ActivityStack stack, WindowProcessController callerApp,
+ int startFlags, Task stack, WindowProcessController callerApp,
NeededUriGrants intentGrants) {
this.r = r;
this.sourceRecord = sourceRecord;
@@ -493,7 +493,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
void moveRecentsStackToFront(String reason) {
- final ActivityStack recentsStack = mRootWindowContainer.getDefaultTaskDisplayArea()
+ final Task recentsStack = mRootWindowContainer.getDefaultTaskDisplayArea()
.getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (recentsStack != null) {
recentsStack.moveToFront(reason);
@@ -729,7 +729,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
final Task task = r.getTask();
- final ActivityStack stack = task.getStack();
+ final Task rootTask = task.getRootTask();
beginDeferResume();
@@ -905,7 +905,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
if (andResume && readyToResume()) {
// As part of the process of launching, ActivityThread also performs
// a resume.
- stack.minimalResumeActivityLocked(r);
+ rootTask.minimalResumeActivityLocked(r);
} else {
// This activity is not starting in the resumed state... which should look like we asked
// it to pause+stop (but remain visible), and it has done so and reported back the
@@ -923,7 +923,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
// switch back to it faster and look better.
- if (mRootWindowContainer.isTopDisplayFocusedStack(stack)) {
+ if (mRootWindowContainer.isTopDisplayFocusedStack(rootTask)) {
mService.getActivityStartController().startSetupActivity();
}
@@ -990,7 +990,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
int requestCode, int callingPid, int callingUid, String callingPackage,
@Nullable String callingFeatureId, boolean ignoreTargetSecurity,
boolean launchingInTask, WindowProcessController callerApp, ActivityRecord resultRecord,
- ActivityStack resultStack) {
+ Task resultStack) {
final boolean isCallerRecents = mService.getRecentTasks() != null
&& mService.getRecentTasks().isCallerRecents(callingUid);
final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
@@ -1332,7 +1332,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
/** This doesn't just find a task, it also moves the task to front. */
void findTaskToMoveToFront(Task task, int flags, ActivityOptions options, String reason,
boolean forceNonResizeable) {
- ActivityStack currentStack = task.getStack();
+ Task currentStack = task.getRootTask();
if (currentStack == null) {
Slog.e(TAG, "findTaskToMoveToFront: can't move task="
+ task + " to front. Stack is null");
@@ -1349,7 +1349,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final Rect bounds = options.getLaunchBounds();
task.setBounds(bounds);
- ActivityStack stack =
+ Task stack =
mRootWindowContainer.getLaunchStack(null, options, task, ON_TOP);
if (stack != currentStack) {
@@ -1388,7 +1388,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
private void moveHomeStackToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
String reason) {
- final ActivityStack focusedStack = taskDisplayArea.getFocusedStack();
+ final Task focusedStack = taskDisplayArea.getFocusedStack();
if ((taskDisplayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
&& (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
@@ -1423,7 +1423,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mWindowManager.setDockedStackResizing(resizing);
}
- private void removePinnedStackInSurfaceTransaction(ActivityStack stack) {
+ private void removePinnedStackInSurfaceTransaction(Task stack) {
/**
* Workaround: Force-stop all the activities in the pinned stack before we reparent them
* to the fullscreen stack. This is to guarantee that when we are removing a stack,
@@ -1446,7 +1446,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mService.deferWindowLayout();
try {
stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- stack.setBounds(null);
+ if (stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ stack.setBounds(null);
+ }
toDisplay.getDefaultTaskDisplayArea().positionTaskBehindHome(stack);
// Follow on the workaround: activities are kept force hidden till the new windowing
@@ -1459,7 +1461,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
}
- private void removeStackInSurfaceTransaction(ActivityStack stack) {
+ private void removeStackInSurfaceTransaction(Task stack) {
if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
removePinnedStackInSurfaceTransaction(stack);
} else {
@@ -1479,7 +1481,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
* pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
* instead moved back onto the fullscreen stack.
*/
- void removeStack(ActivityStack stack) {
+ void removeStack(Task stack) {
mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stack));
}
@@ -1504,12 +1506,21 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
- task.removeTaskActivitiesLocked(reason);
- cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
- mService.getLockTaskController().clearLockedTask(task);
- mService.getTaskChangeNotificationController().notifyTaskStackChanged();
- if (task.isPersistable) {
- mService.notifyTaskPersisterLocked(null, true);
+ if (task.mInRemoveTask) {
+ // Prevent recursion.
+ return;
+ }
+ task.mInRemoveTask = true;
+ try {
+ task.performClearTask(reason);
+ cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
+ mService.getLockTaskController().clearLockedTask(task);
+ mService.getTaskChangeNotificationController().notifyTaskStackChanged();
+ if (task.isPersistable) {
+ mService.notifyTaskPersisterLocked(null, true);
+ }
+ } finally {
+ task.mInRemoveTask = false;
}
}
@@ -1588,7 +1599,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
* @return true if the task has been restored successfully.
*/
boolean restoreRecentTaskLocked(Task task, ActivityOptions aOptions, boolean onTop) {
- final ActivityStack stack =
+ final Task stack =
mRootWindowContainer.getLaunchStack(null, aOptions, task, onTop);
final WindowContainer parent = task.getParent();
@@ -1630,8 +1641,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
* the various checks on tasks that are going to be reparented from one stack to another.
*/
// TODO: Look into changing users to this method to DisplayContent.resolveWindowingMode()
- ActivityStack getReparentTargetStack(Task task, ActivityStack stack, boolean toTop) {
- final ActivityStack prevStack = task.getStack();
+ Task getReparentTargetStack(Task task, Task stack, boolean toTop) {
+ final Task prevStack = task.getRootTask();
final int rootTaskId = stack.mTaskId;
final boolean inMultiWindowMode = stack.inMultiWindowMode();
@@ -1760,7 +1771,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// A resumed activity cannot be stopping. remove from list
mStoppingActivities.remove(r);
- final ActivityStack stack = r.getRootTask();
+ final Task stack = r.getRootTask();
if (stack.getDisplayArea().allResumedActivitiesComplete()) {
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// Make sure activity & window visibility should be identical
@@ -1774,15 +1785,15 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// Called when WindowManager has finished animating the launchingBehind activity to the back.
private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
final Task task = r.getTask();
- final ActivityStack stack = task.getStack();
+ final Task rootTask = task.getRootTask();
mRecentTasks.add(task);
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
- stack.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ rootTask.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// When launching tasks behind, update the last active time of the top task after the new
// task has been shown briefly
- final ActivityRecord top = stack.getTopNonFinishingActivity();
+ final ActivityRecord top = rootTask.getTopNonFinishingActivity();
if (top != null) {
top.getTask().touchActiveTime();
}
@@ -2019,7 +2030,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
*/
void updateTopResumedActivityIfNeeded() {
final ActivityRecord prevTopActivity = mTopResumedActivity;
- final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
+ final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack();
if (topStack == null || topStack.mResumedActivity == prevTopActivity) {
return;
}
@@ -2115,13 +2126,13 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
void handleNonResizableTaskIfNeeded(Task task, int preferredWindowingMode,
- TaskDisplayArea preferredTaskDisplayArea, ActivityStack actualStack) {
+ TaskDisplayArea preferredTaskDisplayArea, Task actualStack) {
handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredTaskDisplayArea,
actualStack, false /* forceNonResizable */);
}
void handleNonResizableTaskIfNeeded(Task task, int preferredWindowingMode,
- TaskDisplayArea preferredTaskDisplayArea, ActivityStack actualStack,
+ TaskDisplayArea preferredTaskDisplayArea, Task actualStack,
boolean forceNonResizable) {
final boolean isSecondaryDisplayPreferred = preferredTaskDisplayArea != null
&& preferredTaskDisplayArea.getDisplayId() != DEFAULT_DISPLAY;
@@ -2177,7 +2188,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// split-screen in split-screen.
mService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- taskDisplayArea.onSplitScreenModeDismissed(task.getStack());
+ taskDisplayArea.onSplitScreenModeDismissed(task);
taskDisplayArea.mDisplayContent.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
true /* notifyClients */);
}
@@ -2233,14 +2244,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
}
- void scheduleUpdatePictureInPictureModeIfNeeded(Task task, ActivityStack prevStack) {
- final ActivityStack stack = task.getStack();
- if ((prevStack == null || (prevStack != stack
- && !prevStack.inPinnedWindowingMode() && !stack.inPinnedWindowingMode()))) {
+ void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Task prevStack) {
+ final Task rootTask = task.getRootTask();
+ if ((prevStack == null || (prevStack != rootTask
+ && !prevStack.inPinnedWindowingMode() && !rootTask.inPinnedWindowingMode()))) {
return;
}
- scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getRequestedOverrideBounds());
+ scheduleUpdatePictureInPictureModeIfNeeded(task, rootTask.getRequestedOverrideBounds());
}
private void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetStackBounds) {
@@ -2272,7 +2283,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final PooledConsumer c = PooledLambda.obtainConsumer(
ActivityRecord::updatePictureInPictureMode,
PooledLambda.__(ActivityRecord.class), targetStackBounds, forceUpdate);
- task.getStack().setBounds(targetStackBounds);
+ task.getRootTask().setBounds(targetStackBounds);
task.forAllActivities(c);
c.recycle();
}
@@ -2340,7 +2351,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
int uid = 0;
synchronized (mService.mGlobalLock) {
if (r.attachedToProcess()
- && r.isState(ActivityStack.ActivityState.RESTARTING_PROCESS)) {
+ && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
processName = r.app.mName;
uid = r.app.mUid;
}
@@ -2507,7 +2518,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mService.getActivityStartController().postStartActivityProcessingForLastStarter(
task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT,
- task.getStack());
+ task.getRootTask());
return ActivityManager.START_TASK_TO_FRONT;
}
callingPackage = task.mCallingPackage;
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 6fbfa68d9309..e944faed6494 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -52,6 +52,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
import com.android.server.wm.ActivityStarter.DefaultFactory;
import com.android.server.wm.ActivityStarter.Factory;
@@ -163,7 +164,7 @@ public class ActivityStartController {
* last starter for an arbitrary task record. Re-evaluate whether we can remove.
*/
void postStartActivityProcessingForLastStarter(ActivityRecord r, int result,
- ActivityStack targetStack) {
+ Task targetStack) {
if (mLastStarter == null) {
return;
}
@@ -189,7 +190,7 @@ public class ActivityStartController {
// The home activity will be started later, defer resuming to avoid unneccerary operations
// (e.g. start home recursively) when creating home stack.
mSupervisor.beginDeferResume();
- final ActivityStack homeStack;
+ final Task homeStack;
try {
// Make sure home stack exists on display area.
homeStack = taskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
@@ -402,6 +403,7 @@ public class ActivityStartController {
// potentially acquire activity manager lock that leads to deadlock.
for (int i = 0; i < intents.length; i++) {
Intent intent = intents[i];
+ NeededUriGrants intentGrants = null;
// Refuse possible leaked file descriptors.
if (intent.hasFileDescriptors()) {
@@ -418,6 +420,14 @@ public class ActivityStartController {
0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid);
aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
+ // Carefully collect grants without holding lock
+ if (aInfo != null) {
+ intentGrants = mSupervisor.mService.mUgmInternal
+ .checkGrantUriPermissionFromIntent(intent, filterCallingUid,
+ aInfo.applicationInfo.packageName,
+ UserHandle.getUserId(aInfo.applicationInfo.uid));
+ }
+
if (aInfo != null) {
if ((aInfo.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
@@ -433,6 +443,7 @@ public class ActivityStartController {
? options
: null;
starters[i] = obtainStarter(intent, reason)
+ .setIntentGrants(intentGrants)
.setCaller(caller)
.setResolvedType(resolvedTypes[i])
.setActivityInfo(aInfo)
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5acedcdad783..e84f040931bb 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -56,7 +56,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.INVALID_UID;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -76,6 +75,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -180,8 +180,8 @@ class ActivityStarter {
private ActivityInfo mNewTaskInfo;
private Intent mNewTaskIntent;
- private ActivityStack mSourceStack;
- private ActivityStack mTargetStack;
+ private Task mSourceStack;
+ private Task mTargetStack;
// The task that the last activity was started into. We currently reset the actual start
// activity's task and as a result may not have a reference to the task in all cases
private Task mTargetTask;
@@ -640,13 +640,10 @@ class ActivityStarter {
}
// If the caller hasn't already resolved the activity, we're willing
- // to do so here, but because that may require acquiring the AM lock
- // as part of calculating the NeededUriGrants, we must never hold
- // the WM lock here to avoid deadlocking.
+ // to do so here. If the caller is already holding the WM lock here,
+ // and we need to check dynamic Uri permissions, then we're forced
+ // to assume those permissions are denied to avoid deadlocking.
if (mRequest.activityInfo == null) {
- if (Thread.holdsLock(mService.mGlobalLock)) {
- Slog.wtf(TAG, new IllegalStateException("Caller must not hold WM lock"));
- }
mRequest.resolveActivity(mSupervisor);
}
@@ -654,7 +651,7 @@ class ActivityStarter {
synchronized (mService.mGlobalLock) {
final boolean globalConfigWillChange = mRequest.globalConfig != null
&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
- final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
+ final Task stack = mRootWindowContainer.getTopDisplayFocusedStack();
if (stack != null) {
stack.mConfigWillChange = globalConfigWillChange;
}
@@ -987,7 +984,7 @@ class ActivityStarter {
}
}
- final ActivityStack resultStack = resultRecord == null
+ final Task resultStack = resultRecord == null
? null : resultRecord.getRootTask();
if (err != START_SUCCESS) {
@@ -1125,7 +1122,7 @@ class ActivityStarter {
null /*profilerInfo*/);
if (DEBUG_PERMISSIONS_REVIEW) {
- final ActivityStack focusedStack =
+ final Task focusedStack =
mRootWindowContainer.getTopDisplayFocusedStack();
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
true, false) + "} from uid " + callingUid + " on display "
@@ -1166,7 +1163,7 @@ class ActivityStarter {
r.appTimeTracker = sourceRecord.appTimeTracker;
}
- final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
+ final Task stack = mRootWindowContainer.getTopDisplayFocusedStack();
// If we are starting an activity that is not from the same uid as the currently resumed
// one, check whether app switches are allowed.
@@ -1291,11 +1288,11 @@ class ActivityStarter {
return false;
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
- // wasn't whitelisted to start an activity
+ // wasn't allowed to start an activity
if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
- + ") is persistent system process AND intent sender whitelisted "
+ + ") is persistent system process AND intent sender allowed "
+ "(allowBackgroundActivityStart = true)");
}
return false;
@@ -1349,23 +1346,23 @@ class ActivityStarter {
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive. If that's the case, we retrieve the WindowProcessController for the send()
- // caller, so that we can make the decision based on its foreground/whitelisted state.
+ // caller, so that we can make the decision based on its state.
int callerAppUid = callingUid;
if (callerApp == null) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid;
}
- // don't abort if the callerApp or other processes of that uid are whitelisted in any way
+ // don't abort if the callerApp or other processes of that uid are allowed in any way
if (callerApp != null) {
// first check the original calling process
if (callerApp.areBackgroundActivityStartsAllowed()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
- + callerApp.getPid() + ", uid = " + callerAppUid + ") is whitelisted");
+ + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
}
return false;
}
- // only if that one wasn't whitelisted, check the other ones
+ // only if that one wasn't allowed, check the other ones
final ArraySet<WindowProcessController> uidProcesses =
mService.mProcessMap.getProcesses(callerAppUid);
if (uidProcesses != null) {
@@ -1375,7 +1372,7 @@ class ActivityStarter {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG,
"Background activity start allowed: process " + proc.getPid()
- + " from uid " + callerAppUid + " is whitelisted");
+ + " from uid " + callerAppUid + " is allowed");
}
return false;
}
@@ -1404,7 +1401,7 @@ class ActivityStarter {
+ "; isRealCallingUidPersistentSystemProcess: "
+ isRealCallingUidPersistentSystemProcess
+ "; originatingPendingIntent: " + originatingPendingIntent
- + "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+ + "; allowBackgroundActivityStart: " + allowBackgroundActivityStart
+ "; intent: " + intent
+ "; callerApp: " + callerApp
+ "]");
@@ -1448,7 +1445,7 @@ class ActivityStarter {
}
void postStartActivityProcessing(ActivityRecord r, int result,
- ActivityStack startedActivityStack) {
+ Task startedActivityStack) {
if (!ActivityManager.isStartResultSuccessful(result)) {
if (mFrozeTaskList) {
// If we specifically froze the task list as part of starting an activity, then
@@ -1482,7 +1479,7 @@ class ActivityStarter {
// The activity was already running so it wasn't started, but either brought to the
// front or the new intent was delivered to it since it was already in front. Notify
// anyone interested in this piece of information.
- final ActivityStack homeStack = targetTask.getDisplayArea().getRootHomeTask();
+ final Task homeStack = targetTask.getDisplayArea().getRootHomeTask();
final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null);
mService.getTaskChangeNotificationController().notifyActivityRestartAttempt(
targetTask.getTaskInfo(), homeTaskVisible, clearedTask,
@@ -1517,7 +1514,7 @@ class ActivityStarter {
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants) {
int result = START_CANCELED;
- final ActivityStack startedActivityStack;
+ final Task startedActivityStack;
try {
mService.deferWindowLayout();
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
@@ -1540,9 +1537,9 @@ class ActivityStarter {
*
* @return the stack where the successful started activity resides.
*/
- private @Nullable ActivityStack handleStartResult(@NonNull ActivityRecord started, int result) {
- final ActivityStack currentStack = started.getRootTask();
- ActivityStack startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+ private @Nullable Task handleStartResult(@NonNull ActivityRecord started, int result) {
+ final Task currentStack = started.getRootTask();
+ Task startedActivityStack = currentStack != null ? currentStack : mTargetStack;
if (ActivityManager.isStartResultSuccessful(result)) {
if (startedActivityStack != null) {
@@ -1562,7 +1559,7 @@ class ActivityStarter {
// If we are not able to proceed, disassociate the activity from the task. Leaving an
// activity in an incomplete state can lead to issues, such as performing operations
// without a window container.
- final ActivityStack stack = mStartActivity.getRootTask();
+ final Task stack = mStartActivity.getRootTask();
if (stack != null) {
mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
}
@@ -1637,7 +1634,7 @@ class ActivityStarter {
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
- final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
+ final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack();
if (topStack != null) {
startResult = deliverToCurrentTopIfNeeded(topStack, intentGrants);
if (startResult != START_SUCCESS) {
@@ -1662,12 +1659,18 @@ class ActivityStarter {
}
if (!mAvoidMoveToFront && mDoResume) {
- mTargetStack.getStack().moveToFront("reuseOrNewTask", targetTask);
+ mTargetStack.getRootTask().moveToFront("reuseOrNewTask", targetTask);
if (mOptions != null) {
if (mOptions.getTaskAlwaysOnTop()) {
mTargetStack.setAlwaysOnTop(true);
}
}
+ if (!mTargetStack.isTopStackInDisplayArea() && mService.mInternal.isDreaming()) {
+ // Launching underneath dream activity (fullscreen, always-on-top). Run the launch-
+ // -behind transition so the Activity gets created and starts in visible state.
+ mLaunchTaskBehind = true;
+ r.mLaunchTaskBehind = true;
+ }
}
mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants,
@@ -1749,7 +1752,7 @@ class ActivityStarter {
} else if (mInTask != null) {
return mInTask;
} else {
- final ActivityStack stack = getLaunchStack(mStartActivity, mLaunchFlags,
+ final Task stack = getLaunchStack(mStartActivity, mLaunchFlags,
null /* task */, mOptions);
final ActivityRecord top = stack.getTopNonFinishingActivity();
if (top != null) {
@@ -1764,7 +1767,7 @@ class ActivityStarter {
private void computeLaunchParams(ActivityRecord r, ActivityRecord sourceRecord,
Task targetTask) {
- final ActivityStack sourceStack = mSourceStack != null ? mSourceStack
+ final Task sourceStack = mSourceStack != null ? mSourceStack
: mRootWindowContainer.getTopDisplayFocusedStack();
if (sourceStack != null && sourceStack.inSplitScreenWindowingMode()
&& (mOptions == null
@@ -1848,7 +1851,7 @@ class ActivityStarter {
// Should not recycle task which is from a different user, just adding the starting
// activity to the task.
if (targetTask.mUserId != mStartActivity.mUserId) {
- mTargetStack = targetTask.getStack();
+ mTargetStack = targetTask.getRootTask();
mAddingToTask = true;
return START_SUCCESS;
}
@@ -1917,6 +1920,12 @@ class ActivityStarter {
return START_SUCCESS;
}
+ // At this point we are certain we want the task moved to the front. If we need to dismiss
+ // any other always-on-top stacks, now is the time to do it.
+ if (targetTaskTop.canTurnScreenOn() && mService.mInternal.isDreaming()) {
+ targetTaskTop.mStackSupervisor.wakeUp("recycleTask#turnScreenOnFlag");
+ }
+
if (mMovedToFront) {
// We moved the task to front, use starting window to hide initial drawn delay.
targetTaskTop.showStartingWindow(null /* prev */, false /* newTask */,
@@ -1940,7 +1949,7 @@ class ActivityStarter {
* Check if the activity being launched is the same as the one currently at the top and it
* should only be launched once.
*/
- private int deliverToCurrentTopIfNeeded(ActivityStack topStack, NeededUriGrants intentGrants) {
+ private int deliverToCurrentTopIfNeeded(Task topStack, NeededUriGrants intentGrants) {
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.mActivityComponent.equals(mStartActivity.mActivityComponent)
@@ -2033,7 +2042,7 @@ class ActivityStarter {
// running, and the caller has asked to clear the current task to have this
// activity at the top.
mAddingToTask = true;
- if (targetTask.getStack() == null) {
+ if (targetTask.getRootTask() == null) {
// Target stack got cleared when we all activities were removed above.
// Go ahead and reset it.
mTargetStack =
@@ -2253,7 +2262,7 @@ class ActivityStarter {
if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
ActivityRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
- ActivityStack topFocusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
+ Task topFocusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
if (topFocusedStack != null) {
checkedCaller = topFocusedStack.topRunningNonDelayedActivityLocked(mNotTop);
}
@@ -2290,7 +2299,7 @@ class ActivityStarter {
private void computeLaunchingTaskFlags() {
// If the caller is not coming from another activity, but has given us an explicit task into
// which they would like us to launch the new activity, then let's see about doing that.
- if (mSourceRecord == null && mInTask != null && mInTask.getStack() != null) {
+ if (mSourceRecord == null && mInTask != null && mInTask.getRootTask() != null) {
final Intent baseIntent = mInTask.getBaseIntent();
final ActivityRecord root = mInTask.getRootActivity();
if (baseIntent == null) {
@@ -2473,7 +2482,7 @@ class ActivityStarter {
// to the front if the caller is not itself in the front.
final boolean differentTopTask;
if (mTargetStack.getDisplayArea() == mPreferredTaskDisplayArea) {
- final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
+ final Task focusStack = mTargetStack.getDisplay().getFocusedStack();
final ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
final Task topTask = curTop != null ? curTop.getTask() : null;
@@ -2494,7 +2503,7 @@ class ActivityStarter {
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
}
- final ActivityStack launchStack =
+ final Task launchStack =
getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
if (launchStack == null || launchStack == mTargetStack) {
// Do not set mMovedToFront to true below for split-screen-top stack, or
@@ -2607,11 +2616,11 @@ class ActivityStarter {
return launchFlags;
}
- private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, Task task,
+ private Task getLaunchStack(ActivityRecord r, int launchFlags, Task task,
ActivityOptions aOptions) {
// We are reusing a task, keep the stack!
if (mReuseTask != null) {
- return mReuseTask.getStack();
+ return mReuseTask.getRootTask();
}
final boolean onTop =
@@ -2638,6 +2647,11 @@ class ActivityStarter {
return mRequest.intent;
}
+ ActivityStarter setIntentGrants(NeededUriGrants intentGrants) {
+ mRequest.intentGrants = intentGrants;
+ return this;
+ }
+
ActivityStarter setReason(String reason) {
mRequest.reason = reason;
return this;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index d5df9068e81d..a903bcd3d728 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -125,20 +125,30 @@ public abstract class ActivityTaskManagerInternal {
* Sleep tokens cause the activity manager to put the top activity to sleep.
* They are used by components such as dreams that may hide and block interaction
* with underlying activities.
+ * The Acquirer provides an interface that encapsulates the underlying work, so the user does
+ * not need to handle the token by him/herself.
*/
- public static abstract class SleepToken {
+ public interface SleepTokenAcquirer {
- /** Releases the sleep token. */
- public abstract void release();
+ /**
+ * Acquires a sleep token.
+ * @param displayId The display to apply to.
+ */
+ void acquire(int displayId);
+
+ /**
+ * Releases the sleep token.
+ * @param displayId The display to apply to.
+ */
+ void release(int displayId);
}
/**
- * Acquires a sleep token for the specified display with the specified tag.
+ * Creates a sleep token acquirer for the specified display with the specified tag.
*
- * @param tag A string identifying the purpose of the token (eg. "Dream").
- * @param displayId The display to apply the sleep token to.
+ * @param tag A string identifying the purpose (eg. "Dream").
*/
- public abstract SleepToken acquireSleepToken(@NonNull String tag, int displayId);
+ public abstract SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag);
/**
* Returns home activity for the specified user.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bf85db2e9700..07c11a1c9332 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -86,8 +86,6 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Scr
import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
import static com.android.server.am.EventLogTags.writeConfigurationChanged;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -120,6 +118,8 @@ import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_P
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_ONLY;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.Task.ActivityState.DESTROYED;
+import static com.android.server.wm.Task.ActivityState.DESTROYING;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
@@ -145,7 +145,6 @@ import android.app.IActivityTaskManager;
import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
import android.app.INotificationManager;
-import android.app.IRequestFinishCallback;
import android.app.ITaskStackListener;
import android.app.Notification;
import android.app.NotificationManager;
@@ -218,7 +217,6 @@ import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.sysprop.DisplayProperties;
import android.telecom.TelecomManager;
-import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1120,7 +1118,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
- final ActivityStack stack = getTopDisplayFocusedStack();
+ final Task stack = getTopDisplayFocusedStack();
if (stack != null && stack.mResumedActivity != null
&& stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
@@ -1869,7 +1867,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
r = ActivityRecord.isInStackLocked(token);
if (r != null) {
if (r.attachedToProcess()
- && r.isState(ActivityStack.ActivityState.RESTARTING_PROCESS)) {
+ && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
// The activity was requested to restart from
// {@link #restartActivityProcessIfVisible}.
restartingName = r.app.mName;
@@ -1956,7 +1954,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
r.immersive = immersive;
// update associated state if we're frontmost
- if (r.isResumedActivityOnDisplay()) {
+ if (r.isFocusedActivityOnDisplay()) {
if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
applyUpdateLockStateLocked(r);
}
@@ -1997,7 +1995,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("isTopActivityImmersive");
synchronized (mGlobalLock) {
- final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ final Task topFocusedStack = getTopDisplayFocusedStack();
if (topFocusedStack == null) {
return false;
}
@@ -2019,7 +2017,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final long origId = Binder.clearCallingIdentity();
if (self.isState(
- ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
+ Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
self.getDisplay().mDisplayContent.mAppTransition.overridePendingAppTransition(
packageName, enterAnim, exitAnim, null, null);
}
@@ -2032,7 +2030,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
synchronized (mGlobalLock) {
- final ActivityStack stack = getTopDisplayFocusedStack();
+ final Task stack = getTopDisplayFocusedStack();
final ActivityRecord r = stack != null ? stack.topRunningActivity() : null;
if (r == null) {
return ActivityManager.COMPAT_MODE_UNKNOWN;
@@ -2047,7 +2045,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
"setFrontActivityScreenCompatMode");
ApplicationInfo ai;
synchronized (mGlobalLock) {
- final ActivityStack stack = getTopDisplayFocusedStack();
+ final Task stack = getTopDisplayFocusedStack();
final ActivityRecord r = stack != null ? stack.topRunningActivity() : null;
if (r == null) {
Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
@@ -2144,7 +2142,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public int getDisplayId(IBinder activityToken) throws RemoteException {
synchronized (mGlobalLock) {
- final ActivityStack stack = ActivityRecord.getStackLocked(activityToken);
+ final Task stack = ActivityRecord.getStackLocked(activityToken);
if (stack != null) {
final int displayId = stack.getDisplayId();
return displayId != INVALID_DISPLAY ? displayId : DEFAULT_DISPLAY;
@@ -2159,7 +2157,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- ActivityStack focusedStack = getTopDisplayFocusedStack();
+ Task focusedStack = getTopDisplayFocusedStack();
if (focusedStack != null) {
return mRootWindowContainer.getStackInfo(focusedStack.mTaskId);
}
@@ -2177,7 +2175,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final ActivityStack stack = mRootWindowContainer.getStack(stackId);
+ final Task stack = mRootWindowContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
return;
@@ -2394,7 +2392,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
+ windowingMode);
}
- final ActivityStack stack = task.getStack();
+ final Task stack = task.getRootTask();
if (toTop) {
stack.moveToFront("setTaskWindowingMode", task);
}
@@ -2454,7 +2452,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ final Task topFocusedStack = getTopDisplayFocusedStack();
if (topFocusedStack != null) {
topFocusedStack.unhandledBackLocked();
}
@@ -2465,13 +2463,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
+ public void onBackPressedOnTaskRoot(IBinder token) {
synchronized (mGlobalLock) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return;
}
- ActivityStack stack = r.getRootTask();
+ Task stack = r.getRootTask();
final TaskOrganizerController taskOrgController =
mWindowOrganizerController.mTaskOrganizerController;
if (taskOrgController.handleInterceptBackPressedOnTaskRoot(stack)) {
@@ -2479,18 +2477,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// callback
} else if (stack != null && (stack.isSingleTaskInstance())) {
// Single-task stacks are used for activities which are presented in floating
- // windows above full screen activities. Instead of directly finishing the
- // task, a task change listener is used to notify SystemUI so the action can be
- // handled specially.
+ // windows above full screen activities. A task change listener is used to notify
+ // SystemUI so the back action can be handled specially.
final Task task = r.getTask();
mTaskChangeNotificationController
.notifyBackPressedOnTaskRoot(task.getTaskInfo());
} else {
- try {
- callback.requestFinish();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke request finish callback", e);
- }
+ moveActivityTaskToBack(token, false /* nonRoot */);
}
}
}
@@ -2728,7 +2721,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public boolean willActivityBeVisible(IBinder token) {
synchronized (mGlobalLock) {
- ActivityStack stack = ActivityRecord.getStackLocked(token);
+ Task stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
return stack.willActivityBeVisible(token);
}
@@ -2751,7 +2744,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+ " to stackId=" + stackId + " toTop=" + toTop);
- final ActivityStack stack = mRootWindowContainer.getStack(stackId);
+ final Task stack = mRootWindowContainer.getStack(stackId);
if (stack == null) {
throw new IllegalStateException(
"moveTaskToStack: No stack for stackId=" + stackId);
@@ -2830,20 +2823,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
void moveTaskToSplitScreenPrimaryTask(Task task, boolean toTop) {
final TaskDisplayArea taskDisplayArea = task.getDisplayArea();
- final ActivityStack primarySplitTask = taskDisplayArea.getRootSplitScreenPrimaryTask();
+ final Task primarySplitTask = taskDisplayArea.getRootSplitScreenPrimaryTask();
if (primarySplitTask == null) {
throw new IllegalStateException("Can't enter split without associated organized task");
}
if (toTop) {
- taskDisplayArea.positionStackAt(POSITION_TOP, primarySplitTask,
+ taskDisplayArea.positionChildAt(POSITION_TOP, primarySplitTask,
false /* includingParents */);
}
WindowContainerTransaction wct = new WindowContainerTransaction();
// Clear out current windowing mode before reparenting to split taks.
wct.setWindowingMode(
- task.getStack().mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_UNDEFINED);
- wct.reparent(task.getStack().mRemoteToken.toWindowContainerToken(),
+ task.getRootTask().mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_UNDEFINED);
+ wct.reparent(task.getRootTask().mRemoteToken.toWindowContainerToken(),
primarySplitTask.mRemoteToken.toWindowContainerToken(), toTop);
mWindowOrganizerController.applyTransaction(wct);
}
@@ -2991,7 +2984,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
// When starting lock task mode the stack must be in front and focused
- task.getStack().moveToFront("startSystemLockTaskMode");
+ task.getRootTask().moveToFront("startSystemLockTaskMode");
startLockTaskModeLocked(task, true /* isSystemCaller */);
}
} finally {
@@ -3026,7 +3019,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return;
}
- final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
+ final Task stack = mRootWindowContainer.getTopDisplayFocusedStack();
if (stack == null || task != stack.getTopMostTask()) {
throw new IllegalArgumentException("Invalid task, not in foreground");
}
@@ -3300,7 +3293,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
+ ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
}
- final ActivityStack stack = r.getRootTask();
+ final Task stack = r.getRootTask();
final Task task = stack.getDisplayArea().createStack(stack.getWindowingMode(),
stack.getActivityType(), !ON_TOP, ainfo, intent,
false /* createdByOrganizer */);
@@ -3461,7 +3454,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final ActivityStack stack = mRootWindowContainer.getStack(stackId);
+ final Task stack = mRootWindowContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "removeStack: No stack with id=" + stackId);
return;
@@ -3505,7 +3498,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
+ token);
}
- final ActivityStack stack = r.getRootTask();
+ final Task stack = r.getRootTask();
if (stack == null) {
throw new IllegalStateException("toggleFreeformWindowingMode: the activity "
+ "doesn't have a stack");
@@ -3669,7 +3662,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
"enqueueAssistContext()");
synchronized (mGlobalLock) {
- final ActivityStack stack = getTopDisplayFocusedStack();
+ final Task stack = getTopDisplayFocusedStack();
ActivityRecord activity = stack != null ? stack.getTopNonFinishingActivity() : null;
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras failed: no top activity");
@@ -3798,7 +3791,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public boolean isAssistDataAllowedOnCurrentActivity() {
int userId;
synchronized (mGlobalLock) {
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ final Task focusedStack = getTopDisplayFocusedStack();
if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
return false;
}
@@ -3971,7 +3964,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
+ taskId);
}
- final ActivityStack stack = mRootWindowContainer.getStack(stackId);
+ final Task stack = mRootWindowContainer.getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException("positionTaskInStack: no stack for id="
@@ -3984,7 +3977,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// TODO: Have the callers of this API call a separate reparent method if that is
// what they intended to do vs. having this method also do reparenting.
- if (task.getStack() == stack) {
+ if (task.getRootTask() == stack) {
// Change position in current stack.
stack.positionChildAt(task, position);
} else {
@@ -4007,8 +4000,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
ActivityRecord record = ActivityRecord.isInStackLocked(token);
if (record == null) {
- throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not "
- + "found for: " + token);
+ return;
}
record.setSizeConfigurations(horizontalSizeConfiguration,
verticalSizeConfigurations, smallestSizeConfigurations);
@@ -4091,7 +4083,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
mRootWindowContainer.moveActivityToPinnedStack(
r, "enterPictureInPictureMode");
- final ActivityStack stack = r.getRootTask();
+ final Task stack = r.getRootTask();
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
MetricsLoggerWrapper.logPictureInPictureEnter(mContext,
@@ -4136,7 +4128,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// If the activity is already in picture-in-picture, update the pinned stack now
// if it is not already expanding to fullscreen. Otherwise, the arguments will
// be used the next time the activity enters PiP
- final ActivityStack stack = r.getRootTask();
+ final Task stack = r.getRootTask();
stack.setPictureInPictureAspectRatio(
r.pictureInPictureArgs.getAspectRatio());
stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
@@ -4322,7 +4314,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
r.requestedVrComponent = (enabled) ? packageName : null;
// Update associated state if this activity is currently focused
- if (r.isResumedActivityOnDisplay()) {
+ if (r.isFocusedActivityOnDisplay()) {
applyUpdateVrModeLocked(r);
}
return 0;
@@ -4800,7 +4792,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- ActivityStack getTopDisplayFocusedStack() {
+ Task getTopDisplayFocusedStack() {
return mRootWindowContainer.getTopDisplayFocusedStack();
}
@@ -5072,7 +5064,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
PowerManagerInternal.wakefulnessToProtoEnum(wakeFullness));
- for (ActivityTaskManagerInternal.SleepToken st : mRootWindowContainer.mSleepTokens) {
+ final int tokenSize = mRootWindowContainer.mSleepTokens.size();
+ for (int i = 0; i < tokenSize; i++) {
+ final RootWindowContainer.SleepToken st =
+ mRootWindowContainer.mSleepTokens.valueAt(i);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
st.toString());
}
@@ -5506,12 +5501,35 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
reason);
}
- ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
- synchronized (mGlobalLock) {
- final ActivityTaskManagerInternal.SleepToken token =
- mRootWindowContainer.createSleepToken(tag, displayId);
- updateSleepIfNeededLocked();
- return token;
+ final class SleepTokenAcquirerImpl implements ActivityTaskManagerInternal.SleepTokenAcquirer {
+ private final String mTag;
+ private final SparseArray<RootWindowContainer.SleepToken> mSleepTokens =
+ new SparseArray<>();
+
+ SleepTokenAcquirerImpl(@NonNull String tag) {
+ mTag = tag;
+ }
+
+ @Override
+ public void acquire(int displayId) {
+ synchronized (mGlobalLock) {
+ if (!mSleepTokens.contains(displayId)) {
+ mSleepTokens.append(displayId,
+ mRootWindowContainer.createSleepToken(mTag, displayId));
+ updateSleepIfNeededLocked();
+ }
+ }
+ }
+
+ @Override
+ public void release(int displayId) {
+ synchronized (mGlobalLock) {
+ final RootWindowContainer.SleepToken token = mSleepTokens.get(displayId);
+ if (token != null) {
+ mRootWindowContainer.removeSleepToken(token);
+ mSleepTokens.remove(displayId);
+ }
+ }
}
}
@@ -5771,7 +5789,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/** Applies latest configuration and/or visibility updates if needed. */
boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
boolean kept = true;
- final ActivityStack mainStack = mRootWindowContainer.getTopDisplayFocusedStack();
+ final Task mainStack = mRootWindowContainer.getTopDisplayFocusedStack();
// mainStack is null during startup.
if (mainStack != null) {
if (changes != 0 && starting == null) {
@@ -6070,9 +6088,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final class LocalService extends ActivityTaskManagerInternal {
@Override
- public SleepToken acquireSleepToken(String tag, int displayId) {
+ public SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag) {
Objects.requireNonNull(tag);
- return ActivityTaskManagerService.this.acquireSleepToken(tag, displayId);
+ return new SleepTokenAcquirerImpl(tag);
}
@Override
@@ -6822,7 +6840,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
// Clean-up disabled activities.
if (mRootWindowContainer.finishDisabledPackageActivities(
- packageName, disabledClasses, true, false, userId) && booted) {
+ packageName, disabledClasses, true /* doit */, false /* evenPersistent */,
+ userId, false /* onlyRemoveNoProcess */) && booted) {
mRootWindowContainer.resumeFocusedStacksTopActivities();
mStackSupervisor.scheduleIdle();
}
@@ -6841,7 +6860,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean didSomething =
getActivityStartController().clearPendingActivityLaunches(packageName);
didSomething |= mRootWindowContainer.finishDisabledPackageActivities(packageName,
- null, doit, evenPersistent, userId);
+ null /* filterByClasses */, doit, evenPersistent, userId,
+ // Only remove the activities without process because the activities with
+ // attached process will be removed when handling process died with
+ // WindowProcessController#isRemoved == true.
+ true /* onlyRemoveNoProcess */);
return didSomething;
}
}
@@ -7026,7 +7049,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mRootWindowContainer.dumpDisplayConfigs(pw, " ");
}
if (dumpAll) {
- final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ final Task topFocusedStack = getTopDisplayFocusedStack();
if (dumpPackage == null && topFocusedStack != null) {
pw.println(" mConfigWillChange: " + topFocusedStack.mConfigWillChange);
}
@@ -7109,7 +7132,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
if (dumpPackage == null) {
getGlobalConfiguration().dumpDebug(proto, GLOBAL_CONFIGURATION);
- final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ final Task topFocusedStack = getTopDisplayFocusedStack();
if (topFocusedStack != null) {
proto.write(CONFIG_WILL_CHANGE, topFocusedStack.mConfigWillChange);
}
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 26e0790a7604..c1447553ba31 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static com.android.server.wm.BarControllerProto.STATE;
import static com.android.server.wm.BarControllerProto.TRANSIENT_STATE;
+import android.annotation.NonNull;
import android.app.StatusBarManager;
import android.graphics.Rect;
import android.os.Handler;
@@ -169,13 +170,23 @@ public class BarController {
return vis;
}
+ private Rect getContentFrame(@NonNull WindowState win) {
+ final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType);
+ return rotatedContentFrame != null ? rotatedContentFrame : mContentFrame;
+ }
+
+ boolean isLightAppearanceAllowed(WindowState win) {
+ if (win == null) {
+ return true;
+ }
+ return !win.isLetterboxedOverlappingWith(getContentFrame(win));
+ }
+
boolean isTransparentAllowed(WindowState win) {
if (win == null) {
return true;
}
- final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType);
- final Rect contentFrame = rotatedContentFrame != null ? rotatedContentFrame : mContentFrame;
- return win.letterboxNotIntersectsOrFullyContains(contentFrame);
+ return win.letterboxNotIntersectsOrFullyContains(getContentFrame(win));
}
boolean setBarShowingLw(final boolean show) {
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 8ab17950f4fc..f563e57b363e 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -50,6 +50,7 @@ public class BlackFrame {
.setName("BlackSurface")
.setColorLayer()
.setParent(surfaceControl)
+ .setCallsite("BlackSurface")
.build();
transaction.setWindowCrop(surface, w, h);
transaction.setAlpha(surface, 1);
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 320ca65d215b..167afab9db0e 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -22,20 +22,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFI
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import com.android.internal.util.FastXmlSerializer;
-
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.pm.ApplicationInfo;
@@ -51,6 +37,20 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
public final class CompatModePackages {
private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
@@ -321,7 +321,7 @@ public final class CompatModePackages {
scheduleWrite();
- final ActivityStack stack = mService.getTopDisplayFocusedStack();
+ final Task stack = mService.getTopDisplayFocusedStack();
ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 98f57c5c31da..0d8e882ad58f 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -368,7 +368,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
/** Sets the always on top flag for this configuration container.
* When you call this function, make sure that the following functions are called as well to
* keep proper z-order.
- * - {@Link DisplayContent#positionStackAt(POSITION_TOP, TaskStack)};
+ * - {@link TaskDisplayArea#positionChildAt(int POSITION_TOP, Task, boolean)};
* */
public void setAlwaysOnTop(boolean alwaysOnTop) {
mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index d43a7b87ee35..07729d17d7b2 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -175,6 +175,7 @@ class Dimmer {
.setParent(mHost.getSurfaceControl())
.setColorLayer()
.setName("Dim Layer for - " + mHost.getName())
+ .setCallsite("Dimmer.makeDimLayer")
.build();
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 6f3ddc95994c..546c5d4c29de 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -21,7 +21,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
-import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS;
@@ -31,6 +30,7 @@ import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA;
+import android.annotation.Nullable;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
@@ -41,7 +41,9 @@ import com.android.server.policy.WindowManagerPolicy;
import com.android.server.protolog.common.ProtoLog;
import java.util.Comparator;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -91,7 +93,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
// Verify that we have proper ordering
Type.checkChild(mType, Type.typeOf(child));
- if (child instanceof ActivityStack) {
+ if (child instanceof Task) {
// TODO(display-area): ActivityStacks are type ANY, but are allowed to have siblings.
// They might need a separate type.
return;
@@ -107,6 +109,66 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
@Override
+ void positionChildAt(int position, T child, boolean includingParents) {
+ if (child.asDisplayArea() == null) {
+ // Reposition other window containers as normal.
+ super.positionChildAt(position, child, includingParents);
+ return;
+ }
+
+ final int targetPosition = findPositionForChildDisplayArea(position, child.asDisplayArea());
+ super.positionChildAt(targetPosition, child, false /* includingParents */);
+
+ final WindowContainer parent = getParent();
+ if (includingParents && parent != null
+ && (position == POSITION_TOP || position == POSITION_BOTTOM)) {
+ parent.positionChildAt(position, this /* child */, true /* includingParents */);
+ }
+ }
+
+ /**
+ * When a {@link DisplayArea} is repositioned, it should only be moved among its siblings of the
+ * same {@link Type}.
+ * For example, when a {@link DisplayArea} of {@link Type#ANY} is repositioned, it shouldn't be
+ * moved above any {@link Type#ABOVE_TASKS} siblings, or below any {@link Type#BELOW_TASKS}
+ * siblings.
+ */
+ private int findPositionForChildDisplayArea(int requestPosition, DisplayArea child) {
+ if (child.getParent() != this) {
+ throw new IllegalArgumentException("positionChildAt: container=" + child.getName()
+ + " is not a child of container=" + getName()
+ + " current parent=" + child.getParent());
+ }
+
+ // The max possible position we can insert the child at.
+ int maxPosition = findMaxPositionForChildDisplayArea(child);
+ // The min possible position we can insert the child at.
+ int minPosition = findMinPositionForChildDisplayArea(child);
+
+ return Math.max(Math.min(requestPosition, maxPosition), minPosition);
+ }
+
+ private int findMaxPositionForChildDisplayArea(DisplayArea child) {
+ final Type childType = Type.typeOf(child);
+ for (int i = mChildren.size() - 1; i > 0; i--) {
+ if (Type.typeOf(getChildAt(i)) == childType) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ private int findMinPositionForChildDisplayArea(DisplayArea child) {
+ final Type childType = Type.typeOf(child);
+ for (int i = 0; i < mChildren.size(); i++) {
+ if (Type.typeOf(getChildAt(i)) == childType) {
+ return i;
+ }
+ }
+ return mChildren.size() - 1;
+ }
+
+ @Override
boolean needsZBoost() {
// Z Boost should only happen at or below the ActivityStack level.
return false;
@@ -140,11 +202,108 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
return DISPLAY_AREA;
}
+ @Override
+ final DisplayArea asDisplayArea() {
+ return this;
+ }
+
+ @Override
void forAllDisplayAreas(Consumer<DisplayArea> callback) {
super.forAllDisplayAreas(callback);
callback.accept(this);
}
+ @Override
+ boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
+ boolean traverseTopToBottom) {
+ // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
+ if (mType != DisplayArea.Type.ANY) {
+ return false;
+ }
+
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ T child = mChildren.get(i);
+ // Only traverse if the child is a DisplayArea.
+ if (child.asDisplayArea() != null && child.asDisplayArea()
+ .forAllTaskDisplayAreas(callback, traverseTopToBottom)) {
+ return true;
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return false;
+ }
+
+ @Override
+ void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) {
+ // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
+ if (mType != DisplayArea.Type.ANY) {
+ return;
+ }
+
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ T child = mChildren.get(i);
+ // Only traverse if the child is a DisplayArea.
+ if (child.asDisplayArea() != null) {
+ child.asDisplayArea().forAllTaskDisplayAreas(callback, traverseTopToBottom);
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ }
+
+ @Nullable
+ @Override
+ <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator,
+ @Nullable R initValue, boolean traverseTopToBottom) {
+ // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
+ if (mType != DisplayArea.Type.ANY) {
+ return initValue;
+ }
+
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ R result = initValue;
+ while (i >= 0 && i < childCount) {
+ T child = mChildren.get(i);
+ // Only traverse if the child is a DisplayArea.
+ if (child.asDisplayArea() != null) {
+ result = (R) child.asDisplayArea()
+ .reduceOnAllTaskDisplayAreas(accumulator, result, traverseTopToBottom);
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return result;
+ }
+
+ @Nullable
+ @Override
+ <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
+ boolean traverseTopToBottom) {
+ // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
+ if (mType != DisplayArea.Type.ANY) {
+ return null;
+ }
+
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ T child = mChildren.get(i);
+ // Only traverse if the child is a DisplayArea.
+ if (child.asDisplayArea() != null) {
+ R result = (R) child.asDisplayArea()
+ .getItemFromTaskDisplayAreas(callback, traverseTopToBottom);
+ if (result != null) {
+ return result;
+ }
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return null;
+ }
+
void setOrganizer(IDisplayAreaOrganizer organizer) {
if (mOrganizer == organizer) return;
IDisplayAreaOrganizer lastOrganizer = mOrganizer;
@@ -253,6 +412,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
req = mLastKeyguardForcedOrientation;
}
}
+ mLastOrientationSource = win;
return req;
}
}
@@ -263,15 +423,6 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
/**
- * Root of the display area hierarchy.
- */
- public static class Root extends DisplayArea<DisplayArea> {
- Root(WindowManagerService wms) {
- super(wms, Type.ANY, "DisplayArea.Root", FEATURE_ROOT);
- }
- }
-
- /**
* DisplayArea that can be dimmed.
*/
static class Dimmable extends DisplayArea<DisplayArea> {
@@ -332,11 +483,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
static Type typeOf(WindowContainer c) {
- if (c instanceof DisplayArea) {
+ if (c.asDisplayArea() != null) {
return ((DisplayArea) c).mType;
} else if (c instanceof WindowToken && !(c instanceof ActivityRecord)) {
return typeOf((WindowToken) c);
- } else if (c instanceof ActivityStack) {
+ } else if (c instanceof Task) {
return ANY;
} else {
throw new IllegalArgumentException("Unknown container: " + c);
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index b6c71bb17631..9a397fe07f4e 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -114,7 +114,8 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) {
try {
- SurfaceControl outSurfaceControl = new SurfaceControl(da.getSurfaceControl());
+ SurfaceControl outSurfaceControl = new SurfaceControl(da.getSurfaceControl(),
+ "DisplayAreaOrganizerController.onDisplayAreaAppeared");
organizer.onDisplayAreaAppeared(da.getDisplayAreaInfo(), outSurfaceControl);
} catch (RemoteException e) {
// Oh well...
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index e4e9eec6d5b4..33eb3ce57373 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -17,12 +17,19 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
+import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
+import static com.android.server.wm.DisplayAreaPolicyBuilder.HierarchyBuilder;
+
import android.content.res.Resources;
import android.text.TextUtils;
@@ -34,56 +41,24 @@ import java.util.List;
*/
public abstract class DisplayAreaPolicy {
protected final WindowManagerService mWmService;
- protected final DisplayContent mContent;
/**
* The root DisplayArea. Attach all DisplayAreas to this area (directly or indirectly).
*/
- protected final DisplayArea.Root mRoot;
-
- /**
- * The IME container. The IME's windows are automatically added to this container.
- */
- protected final DisplayArea<? extends WindowContainer> mImeContainer;
-
- /**
- * The task display areas. Tasks etc. are automatically added to these containers.
- */
- protected final List<TaskDisplayArea> mTaskDisplayAreas;
+ protected final RootDisplayArea mRoot;
/**
* Construct a new {@link DisplayAreaPolicy}
*
* @param wmService the window manager service instance
- * @param content the display content for which the policy applies
* @param root the root display area under which the policy operates
- * @param imeContainer the ime container that the policy must attach
- * @param taskDisplayAreas the task display areas that the policy must attach
- *
- * @see #attachDisplayAreas()
*/
- protected DisplayAreaPolicy(WindowManagerService wmService,
- DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer,
- List<TaskDisplayArea> taskDisplayAreas) {
+ protected DisplayAreaPolicy(WindowManagerService wmService, RootDisplayArea root) {
mWmService = wmService;
- mContent = content;
mRoot = root;
- mImeContainer = imeContainer;
- mTaskDisplayAreas = taskDisplayAreas;
}
/**
- * Called to ask the policy to set up the DisplayArea hierarchy. At a minimum this must:
- *
- * - attach mImeContainer to mRoot (or one of its descendants)
- * - attach mTaskStacks to mRoot (or one of its descendants)
- *
- * Additionally, this is the right place to set up any other DisplayAreas as desired.
- */
- public abstract void attachDisplayAreas();
-
- /**
* Called to ask the policy to attach the given WindowToken to the DisplayArea hierarchy.
*
* This must attach the token to mRoot (or one of its descendants).
@@ -96,44 +71,50 @@ public abstract class DisplayAreaPolicy {
public abstract List<DisplayArea<? extends WindowContainer>> getDisplayAreas(int featureId);
/**
- * @return the number of task display areas on the display.
+ * @return the default/fallback {@link TaskDisplayArea} on the display.
*/
- public int getTaskDisplayAreaCount() {
- return mTaskDisplayAreas.size();
- }
-
- /**
- * @return the task display area at index.
- */
- public TaskDisplayArea getTaskDisplayAreaAt(int index) {
- return mTaskDisplayAreas.get(index);
- }
+ public abstract TaskDisplayArea getDefaultTaskDisplayArea();
/** Provider for platform-default display area policy. */
static final class DefaultProvider implements DisplayAreaPolicy.Provider {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
- DisplayContent content, DisplayArea.Root root,
+ DisplayContent content, RootDisplayArea root,
DisplayArea<? extends WindowContainer> imeContainer) {
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
final List<TaskDisplayArea> tdaList = new ArrayList<>();
tdaList.add(defaultTaskDisplayArea);
- return new DisplayAreaPolicyBuilder()
- .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy,
- "WindowedMagnification", FEATURE_WINDOWED_MAGNIFICATION)
+
+ // Define the features that will be supported under the root of the whole logical
+ // display. The policy will build the DisplayArea hierarchy based on this.
+ HierarchyBuilder rootHierarchy = new HierarchyBuilder(root)
+ .addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
+ FEATURE_WINDOWED_MAGNIFICATION)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
// Make the DA dimmable so that the magnify window also mirrors the dim
// layer
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
.build())
- .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy,
- "OneHanded", FEATURE_ONE_HANDED)
+ .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
+ FEATURE_ONE_HANDED)
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
.build())
- .build(wmService, content, root, imeContainer, tdaList);
+ .addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",
+ FEATURE_FULLSCREEN_MAGNIFICATION)
+ .all()
+ .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD,
+ TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY,
+ TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
+ .build())
+ .setImeContainer(imeContainer)
+ .setTaskDisplayAreas(tdaList);
+
+ // Instantiate the policy with the hierarchy defined above. This will create and attach
+ // all the necessary DisplayAreas to the root.
+ return new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
}
}
@@ -146,16 +127,15 @@ public abstract class DisplayAreaPolicy {
*/
public interface Provider {
/**
- * Instantiate a new DisplayAreaPolicy.
+ * Instantiates a new DisplayAreaPolicy. It should set up the {@link DisplayArea} hierarchy.
*
* @see DisplayAreaPolicy#DisplayAreaPolicy
*/
- DisplayAreaPolicy instantiate(WindowManagerService wmService,
- DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer);
+ DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
+ RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer);
/**
- * Instantiate the device-specific {@link Provider}.
+ * Instantiates the device-specific {@link Provider}.
*/
static Provider fromResources(Resources res) {
String name = res.getString(
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 8a831d32fb08..681b21cfc829 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -23,16 +23,24 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.policy.WindowManagerPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
/**
* A builder for instantiating a complex {@link DisplayAreaPolicy}
@@ -51,7 +59,7 @@ import java.util.Map;
* .build(...)
*
* // Builds a policy with the following hierarchy:
- * - DisplayArea.Root
+ * - RootDisplayArea
* - Magnification
* - DisplayArea.Tokens (Wallpapers are attached here)
* - TaskDisplayArea
@@ -62,11 +70,305 @@ import java.util.Map;
*
* </pre>
*
- * // TODO(display-area): document more complex scenarios where we need multiple areas per feature.
+ * // TODO(b/158713595): document more complex scenarios where we need multiple areas per feature.
*/
class DisplayAreaPolicyBuilder {
+ @Nullable private HierarchyBuilder mRootHierarchyBuilder;
+ private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>();
+
+ /**
+ * When a window is created, the policy will use this function to select the
+ * {@link RootDisplayArea} to place that window in. The selected root can be either the one of
+ * the {@link #mRootHierarchyBuilder} or the one of any of the
+ * {@link #mDisplayAreaGroupHierarchyBuilders}.
+ **/
+ @Nullable private BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
+
+ /** Defines the root hierarchy for the whole logical display. */
+ DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) {
+ mRootHierarchyBuilder = rootHierarchyBuilder;
+ return this;
+ }
+
+ /**
+ * Defines a DisplayAreaGroup hierarchy. Its root will be added as a child of the root
+ * hierarchy.
+ */
+ DisplayAreaPolicyBuilder addDisplayAreaGroupHierarchy(
+ HierarchyBuilder displayAreaGroupHierarchy) {
+ mDisplayAreaGroupHierarchyBuilders.add(displayAreaGroupHierarchy);
+ return this;
+ }
+
+ /** The policy will use this function to find the root to place windows in. */
+ DisplayAreaPolicyBuilder setSelectRootForWindowFunc(
+ BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc) {
+ mSelectRootForWindowFunc = selectRootForWindowFunc;
+ return this;
+ }
+
+ /** Makes sure the setting meets the requirement. */
+ private void validate() {
+ if (mRootHierarchyBuilder == null) {
+ throw new IllegalStateException("Root must be set for the display area policy.");
+ }
+
+ boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null;
+ boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder);
+ for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
+ HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
+ if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) {
+ throw new IllegalStateException(
+ "DisplayAreaGroup must contain at least one TaskDisplayArea.");
+ }
+
+ containsImeContainer = containsImeContainer || hierarchyBuilder.mImeContainer != null;
+ if (containsDefaultTda) {
+ if (containsDefaultTaskDisplayArea(hierarchyBuilder)) {
+ throw new IllegalStateException("Only one TaskDisplayArea can have the feature "
+ + "of FEATURE_DEFAULT_TASK_CONTAINER");
+ }
+ } else {
+ containsDefaultTda = containsDefaultTaskDisplayArea(hierarchyBuilder);
+ }
+ }
+
+ if (!containsImeContainer) {
+ throw new IllegalStateException("IME container must be set.");
+ }
+
+ if (!containsDefaultTda) {
+ throw new IllegalStateException("There must be a default TaskDisplayArea.");
+ }
+ }
+
+ /** Checks if the given hierarchy contains the default {@link TaskDisplayArea}. */
+ private static boolean containsDefaultTaskDisplayArea(HierarchyBuilder displayAreaHierarchy) {
+ for (int i = 0; i < displayAreaHierarchy.mTaskDisplayAreas.size(); i++) {
+ if (displayAreaHierarchy.mTaskDisplayAreas.get(i).mFeatureId
+ == FEATURE_DEFAULT_TASK_CONTAINER) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Result build(WindowManagerService wmService) {
+ validate();
+
+ // Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
+ mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
+ List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
+ mDisplayAreaGroupHierarchyBuilders.size());
+ for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
+ HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
+ hierarchyBuilder.build();
+ displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
+ }
+ return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
+ mSelectRootForWindowFunc);
+ }
+
+ /**
+ * Builder to define {@link Feature} and {@link DisplayArea} hierarchy under a
+ * {@link RootDisplayArea}
+ */
+ static class HierarchyBuilder {
+ private static final int LEAF_TYPE_TASK_CONTAINERS = 1;
+ private static final int LEAF_TYPE_IME_CONTAINERS = 2;
+ private static final int LEAF_TYPE_TOKENS = 0;
+
+ private final RootDisplayArea mRoot;
+ private final ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures = new ArrayList<>();
+ private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>();
+ @Nullable
+ private DisplayArea<? extends WindowContainer> mImeContainer;
+
+ HierarchyBuilder(RootDisplayArea root) {
+ mRoot = root;
+ }
+
+ /** Adds {@link Feature} that applies to layers under this container. */
+ HierarchyBuilder addFeature(DisplayAreaPolicyBuilder.Feature feature) {
+ mFeatures.add(feature);
+ return this;
+ }
+
+ /**
+ * Sets {@link TaskDisplayArea} that are children of this hierarchy root.
+ * {@link DisplayArea} group must have at least one {@link TaskDisplayArea}.
+ */
+ HierarchyBuilder setTaskDisplayAreas(List<TaskDisplayArea> taskDisplayAreas) {
+ mTaskDisplayAreas.clear();
+ mTaskDisplayAreas.addAll(taskDisplayAreas);
+ return this;
+ }
+
+ /** Sets IME container as a child of this hierarchy root. */
+ HierarchyBuilder setImeContainer(DisplayArea<? extends WindowContainer> imeContainer) {
+ mImeContainer = imeContainer;
+ return this;
+ }
+
+ /** Builds the {@link DisplayArea} hierarchy below root. */
+ private void build() {
+ build(null /* displayAreaGroupHierarchyBuilders */);
+ }
+
+ /**
+ * Builds the {@link DisplayArea} hierarchy below root. And adds the roots of those
+ * {@link HierarchyBuilder} as children.
+ */
+ private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
+ final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
+ final int maxWindowLayerCount = policy.getMaxWindowLayer();
+ final DisplayArea.Tokens[] displayAreaForLayer =
+ new DisplayArea.Tokens[maxWindowLayerCount];
+ final Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureAreas =
+ new ArrayMap<>(mFeatures.size());
+ for (int i = 0; i < mFeatures.size(); i++) {
+ featureAreas.put(mFeatures.get(i), new ArrayList<>());
+ }
+
+ // This method constructs the layer hierarchy with the following properties:
+ // (1) Every feature maps to a set of DisplayAreas
+ // (2) After adding a window, for every feature the window's type belongs to,
+ // it is a descendant of one of the corresponding DisplayAreas of the feature.
+ // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows
+ // within a DisplayArea:
+ // for every pair of DisplayArea siblings (a,b), where a is below b, it holds that
+ // max(z-range(a)) <= min(z-range(b))
+ //
+ // The algorithm below iteratively creates such a hierarchy:
+ // - Initially, all windows are attached to the root.
+ // - For each feature we create a set of DisplayAreas, by looping over the layers
+ // - if the feature does apply to the current layer, we need to find a DisplayArea
+ // for it to satisfy (2)
+ // - we can re-use the previous layer's area if:
+ // the current feature also applies to the previous layer, (to satisfy (3))
+ // and the last feature that applied to the previous layer is the same as
+ // the last feature that applied to the current layer (to satisfy (2))
+ // - otherwise we create a new DisplayArea below the last feature that applied
+ // to the current layer
+
+ PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
+ final PendingArea root = new PendingArea(null, 0, null);
+ Arrays.fill(areaForLayer, root);
+
+ // Create DisplayAreas to cover all defined features.
+ final int size = mFeatures.size();
+ for (int i = 0; i < size; i++) {
+ // Traverse the features with the order they are defined, so that the early defined
+ // feature will be on the top in the hierarchy.
+ final Feature feature = mFeatures.get(i);
+ PendingArea featureArea = null;
+ for (int layer = 0; layer < maxWindowLayerCount; layer++) {
+ if (feature.mWindowLayers[layer]) {
+ // This feature will be applied to this window layer.
+ //
+ // We need to find a DisplayArea for it:
+ // We can reuse the existing one if it was created for this feature for the
+ // previous layer AND the last feature that applied to the previous layer is
+ // the same as the feature that applied to the current layer (so they are ok
+ // to share the same parent DisplayArea).
+ if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
+ // No suitable DisplayArea:
+ // Create a new one under the previous area (as parent) for this layer.
+ featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
+ areaForLayer[layer].mChildren.add(featureArea);
+ }
+ areaForLayer[layer] = featureArea;
+ } else {
+ // This feature won't be applied to this window layer. If it needs to be
+ // applied to the next layer, we will need to create a new DisplayArea for
+ // that.
+ featureArea = null;
+ }
+ }
+ }
+
+ // Create Tokens as leaf for every layer.
+ PendingArea leafArea = null;
+ int leafType = LEAF_TYPE_TOKENS;
+ for (int layer = 0; layer < maxWindowLayerCount; layer++) {
+ int type = typeOfLayer(policy, layer);
+ // Check whether we can reuse the same Tokens with the previous layer. This happens
+ // if the previous layer is the same type as the current layer AND there is no
+ // feature that applies to only one of them.
+ if (leafArea == null || leafArea.mParent != areaForLayer[layer]
+ || type != leafType) {
+ // Create a new Tokens for this layer.
+ leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
+ areaForLayer[layer].mChildren.add(leafArea);
+ leafType = type;
+ if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
+ // We use the passed in TaskDisplayAreas for task container type of layer.
+ // Skip creating Tokens even if there is no TDA.
+ addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
+ addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
+ displayAreaGroupHierarchyBuilders);
+ leafArea.mSkipTokens = true;
+ } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
+ // We use the passed in ImeContainer for ime container type of layer.
+ // Skip creating Tokens even if there is no ime container.
+ leafArea.mExisting = mImeContainer;
+ leafArea.mSkipTokens = true;
+ }
+ }
+ leafArea.mMaxLayer = layer;
+ }
+ root.computeMaxLayer();
+
+ // We built a tree of PendingAreas above with all the necessary info to represent the
+ // hierarchy, now create and attach real DisplayAreas to the root.
+ root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);
+
+ // Notify the root that we have finished attaching all the DisplayAreas. Cache all the
+ // feature related collections there for fast access.
+ mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
+ }
+
+ /** Adds all {@link TaskDisplayArea} to the application layer. */
+ private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) {
+ final int count = mTaskDisplayAreas.size();
+ for (int i = 0; i < count; i++) {
+ PendingArea leafArea =
+ new PendingArea(null /* feature */, APPLICATION_LAYER, parentPendingArea);
+ leafArea.mExisting = mTaskDisplayAreas.get(i);
+ leafArea.mMaxLayer = APPLICATION_LAYER;
+ parentPendingArea.mChildren.add(leafArea);
+ }
+ }
- private final ArrayList<Feature> mFeatures = new ArrayList<>();
+ /** Adds roots of the DisplayAreaGroups to the application layer. */
+ private void addDisplayAreaGroupsToApplicationLayer(
+ DisplayAreaPolicyBuilder.PendingArea parentPendingArea,
+ @Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
+ if (displayAreaGroupHierarchyBuilders == null) {
+ return;
+ }
+ final int count = displayAreaGroupHierarchyBuilders.size();
+ for (int i = 0; i < count; i++) {
+ DisplayAreaPolicyBuilder.PendingArea
+ leafArea = new DisplayAreaPolicyBuilder.PendingArea(
+ null /* feature */, APPLICATION_LAYER, parentPendingArea);
+ leafArea.mExisting = displayAreaGroupHierarchyBuilders.get(i).mRoot;
+ leafArea.mMaxLayer = APPLICATION_LAYER;
+ parentPendingArea.mChildren.add(leafArea);
+ }
+ }
+
+ private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
+ if (layer == APPLICATION_LAYER) {
+ return LEAF_TYPE_TASK_CONTAINERS;
+ } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
+ || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
+ return LEAF_TYPE_IME_CONTAINERS;
+ } else {
+ return LEAF_TYPE_TOKENS;
+ }
+ }
+ }
/** Supplier interface to provide a new created {@link DisplayArea}. */
interface NewDisplayAreaSupplier {
@@ -116,8 +418,8 @@ class DisplayAreaPolicyBuilder {
private NewDisplayAreaSupplier mNewDisplayAreaSupplier = DisplayArea::new;
/**
- * Build a new feature that applies to a set of window types as specified by the builder
- * methods.
+ * Builds a new feature that applies to a set of window types as specified by the
+ * builder methods.
*
* <p>The set of types is updated iteratively in the order of the method invocations.
* For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should
@@ -208,103 +510,30 @@ class DisplayAreaPolicyBuilder {
}
static class Result extends DisplayAreaPolicy {
- private static final int LEAF_TYPE_TASK_CONTAINERS = 1;
- private static final int LEAF_TYPE_IME_CONTAINERS = 2;
- private static final int LEAF_TYPE_TOKENS = 0;
-
- private final int mMaxWindowLayer = mWmService.mPolicy.getMaxWindowLayer();
-
- private final ArrayList<Feature> mFeatures;
- private final Map<Feature, List<DisplayArea<? extends WindowContainer>>> mAreas;
- private final DisplayArea.Tokens[] mAreaForLayer = new DisplayArea.Tokens[mMaxWindowLayer];
-
- Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer,
- List<TaskDisplayArea> taskDisplayAreas, ArrayList<Feature> features) {
- super(wmService, content, root, imeContainer, taskDisplayAreas);
- mFeatures = features;
- mAreas = new HashMap<>(features.size());
- for (int i = 0; i < mFeatures.size(); i++) {
- mAreas.put(mFeatures.get(i), new ArrayList<>());
- }
- }
-
- @Override
- public void attachDisplayAreas() {
- // This method constructs the layer hierarchy with the following properties:
- // (1) Every feature maps to a set of DisplayAreas
- // (2) After adding a window, for every feature the window's type belongs to,
- // it is a descendant of one of the corresponding DisplayAreas of the feature.
- // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows
- // within a DisplayArea:
- // for every pair of DisplayArea siblings (a,b), where a is below b, it holds that
- // max(z-range(a)) <= min(z-range(b))
- //
- // The algorithm below iteratively creates such a hierarchy:
- // - Initially, all windows are attached to the root.
- // - For each feature we create a set of DisplayAreas, by looping over the layers
- // - if the feature does apply to the current layer, we need to find a DisplayArea
- // for it to satisfy (2)
- // - we can re-use the previous layer's area if:
- // the current feature also applies to the previous layer, (to satisfy (3))
- // and the last feature that applied to the previous layer is the same as
- // the last feature that applied to the current layer (to satisfy (2))
- // - otherwise we create a new DisplayArea below the last feature that applied
- // to the current layer
-
-
- PendingArea[] areaForLayer = new PendingArea[mMaxWindowLayer];
- final PendingArea root = new PendingArea(null, 0, null);
- Arrays.fill(areaForLayer, root);
-
- final int size = mFeatures.size();
- for (int i = 0; i < size; i++) {
- PendingArea featureArea = null;
- for (int layer = 0; layer < mMaxWindowLayer; layer++) {
- final Feature feature = mFeatures.get(i);
- if (feature.mWindowLayers[layer]) {
- if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
- // No suitable DisplayArea - create a new one under the previous area
- // for this layer.
- featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
- areaForLayer[layer].mChildren.add(featureArea);
- }
- areaForLayer[layer] = featureArea;
- } else {
- featureArea = null;
- }
- }
- }
-
- PendingArea leafArea = null;
- int leafType = LEAF_TYPE_TOKENS;
- for (int layer = 0; layer < mMaxWindowLayer; layer++) {
- int type = typeOfLayer(mWmService.mPolicy, layer);
- if (leafArea == null || leafArea.mParent != areaForLayer[layer]
- || type != leafType) {
- leafArea = new PendingArea(null, layer, areaForLayer[layer]);
- areaForLayer[layer].mChildren.add(leafArea);
- leafType = type;
- if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
- addTaskDisplayAreasToLayer(areaForLayer[layer], layer);
- } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
- leafArea.mExisting = mImeContainer;
- }
- }
- leafArea.mMaxLayer = layer;
- }
- root.computeMaxLayer();
- root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas);
- }
-
- /** Adds all task display areas to the specified layer */
- private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea, int layer) {
- final int count = mTaskDisplayAreas.size();
- for (int i = 0; i < count; i++) {
- PendingArea leafArea = new PendingArea(null, layer, parentPendingArea);
- leafArea.mExisting = mTaskDisplayAreas.get(i);
- leafArea.mMaxLayer = layer;
- parentPendingArea.mChildren.add(leafArea);
+ final List<RootDisplayArea> mDisplayAreaGroupRoots;
+ final BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
+ private final TaskDisplayArea mDefaultTaskDisplayArea;
+
+ Result(WindowManagerService wmService, RootDisplayArea root,
+ List<RootDisplayArea> displayAreaGroupRoots,
+ @Nullable BiFunction<WindowToken, Bundle, RootDisplayArea>
+ selectRootForWindowFunc) {
+ super(wmService, root);
+ mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
+ mSelectRootForWindowFunc = selectRootForWindowFunc == null
+ // Always return the highest level root of the logical display when the func is
+ // not specified.
+ ? (window, options) -> mRoot
+ : selectRootForWindowFunc;
+
+ // Cache the default TaskDisplayArea for quick access.
+ mDefaultTaskDisplayArea = mRoot.getItemFromTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.mFeatureId == FEATURE_DEFAULT_TASK_CONTAINER
+ ? taskDisplayArea
+ : null);
+ if (mDefaultTaskDisplayArea == null) {
+ throw new IllegalStateException(
+ "No display area with FEATURE_DEFAULT_TASK_CONTAINER");
}
}
@@ -316,64 +545,44 @@ class DisplayAreaPolicyBuilder {
@VisibleForTesting
DisplayArea.Tokens findAreaForToken(WindowToken token) {
- int windowLayerFromType = token.getWindowLayerFromType();
- if (windowLayerFromType == APPLICATION_LAYER) {
- throw new IllegalArgumentException(
- "There shouldn't be WindowToken on APPLICATION_LAYER");
- } else if (token.mRoundedCornerOverlay) {
- windowLayerFromType = mMaxWindowLayer - 1;
- }
- return mAreaForLayer[windowLayerFromType];
+ return mSelectRootForWindowFunc.apply(token, token.mOptions).findAreaForToken(token);
}
@VisibleForTesting
- ArrayList<Feature> getFeatures() {
- return mFeatures;
+ List<Feature> getFeatures() {
+ Set<Feature> features = new ArraySet<>();
+ features.addAll(mRoot.mFeatures);
+ for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) {
+ features.addAll(mDisplayAreaGroupRoots.get(i).mFeatures);
+ }
+ return new ArrayList<>(features);
}
@Override
public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(int featureId) {
- for (int i = 0; i < mFeatures.size(); i++) {
- Feature feature = mFeatures.get(i);
- if (feature.getId() == featureId) {
- return getDisplayAreas(feature);
- }
+ List<DisplayArea<? extends WindowContainer>> displayAreas = new ArrayList<>();
+ getDisplayAreas(mRoot, featureId, displayAreas);
+ for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) {
+ getDisplayAreas(mDisplayAreaGroupRoots.get(i), featureId, displayAreas);
}
- return new ArrayList<>();
- }
-
- public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(Feature feature) {
- return mAreas.get(feature);
+ return displayAreas;
}
- private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
- if (layer == APPLICATION_LAYER) {
- return LEAF_TYPE_TASK_CONTAINERS;
- } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
- || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
- return LEAF_TYPE_IME_CONTAINERS;
- } else {
- return LEAF_TYPE_TOKENS;
+ private static void getDisplayAreas(RootDisplayArea root, int featureId,
+ List<DisplayArea<? extends WindowContainer>> displayAreas) {
+ List<Feature> features = root.mFeatures;
+ for (int i = 0; i < features.size(); i++) {
+ Feature feature = features.get(i);
+ if (feature.mId == featureId) {
+ displayAreas.addAll(root.mFeatureToDisplayAreas.get(feature));
+ }
}
}
- }
- DisplayAreaPolicyBuilder addFeature(Feature feature) {
- mFeatures.add(feature);
- return this;
- }
-
- protected List<Feature> getFeatures() {
- return mFeatures;
- }
-
- Result build(WindowManagerService wmService,
- DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer,
- List<TaskDisplayArea> taskDisplayAreas) {
-
- return new Result(wmService, content, root, imeContainer, taskDisplayAreas, new ArrayList<>(
- mFeatures));
+ @Override
+ public TaskDisplayArea getDefaultTaskDisplayArea() {
+ return mDefaultTaskDisplayArea;
+ }
}
static class PendingArea {
@@ -382,11 +591,21 @@ class DisplayAreaPolicyBuilder {
final Feature mFeature;
final PendingArea mParent;
int mMaxLayer;
- DisplayArea mExisting;
- PendingArea(Feature feature,
- int minLayer,
- PendingArea parent) {
+ /** If not {@code null}, use this instead of creating a {@link DisplayArea.Tokens}. */
+ @Nullable DisplayArea mExisting;
+
+ /**
+ * Whether to skip creating a {@link DisplayArea.Tokens} if {@link #mExisting} is
+ * {@code null}.
+ *
+ * This will be set for {@link HierarchyBuilder#LEAF_TYPE_IME_CONTAINERS} and
+ * {@link HierarchyBuilder#LEAF_TYPE_TASK_CONTAINERS}, because we don't want to create
+ * {@link DisplayArea.Tokens} for them even if they are not set.
+ */
+ boolean mSkipTokens = false;
+
+ PendingArea(Feature feature, int minLayer, PendingArea parent) {
mMinLayer = minLayer;
mFeature = feature;
mParent = parent;
@@ -399,13 +618,17 @@ class DisplayAreaPolicyBuilder {
return mMaxLayer;
}
- void instantiateChildren(DisplayArea<DisplayArea> parent,
- DisplayArea.Tokens[] areaForLayer, int level, Map<Feature, List<DisplayArea<?
- extends WindowContainer>>> areas) {
+ void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,
+ int level, Map<Feature, List<DisplayArea<? extends WindowContainer>>> areas) {
mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
for (int i = 0; i < mChildren.size(); i++) {
final PendingArea child = mChildren.get(i);
final DisplayArea area = child.createArea(parent, areaForLayer);
+ if (area == null) {
+ // TaskDisplayArea and ImeContainer can be set at different hierarchy, so it can
+ // be null.
+ continue;
+ }
parent.addChild(area, WindowContainer.POSITION_TOP);
if (child.mFeature != null) {
areas.get(child.mFeature).add(area);
@@ -414,11 +637,15 @@ class DisplayAreaPolicyBuilder {
}
}
+ @Nullable
private DisplayArea createArea(DisplayArea<DisplayArea> parent,
DisplayArea.Tokens[] areaForLayer) {
if (mExisting != null) {
return mExisting;
}
+ if (mSkipTokens) {
+ return null;
+ }
DisplayArea.Type type;
if (mMinLayer > APPLICATION_LAYER) {
type = DisplayArea.Type.ABOVE_TASKS;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ebd82caeb945..0b1f4d9e1613 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -71,13 +71,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
@@ -102,6 +102,7 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT;
@@ -231,7 +232,7 @@ import java.util.function.Predicate;
* Utility class for keeping track of the WindowStates and other pertinent contents of a
* particular Display.
*/
-class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.DisplayContentInfo {
+class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
@@ -489,6 +490,8 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
*/
private ActivityRecord mFixedRotationLaunchingApp;
+ /** The delay to avoid toggling the animation quickly. */
+ private static final long FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS = 250;
private FixedRotationAnimationController mFixedRotationAnimationController;
final FixedRotationTransitionListener mFixedRotationTransitionListener =
@@ -584,9 +587,9 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
private IntArray mDisplayAccessUIDs = new IntArray();
/** All tokens used to put activities on this stack to sleep (including mOffToken) */
- final ArrayList<ActivityTaskManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
- /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
- ActivityTaskManagerInternal.SleepToken mOffToken;
+ final ArrayList<RootWindowContainer.SleepToken> mAllSleepTokens = new ArrayList<>();
+ /** The token acquirer to put stacks on the display to sleep */
+ private final ActivityTaskManagerInternal.SleepTokenAcquirer mOffTokenAcquirer;
private boolean mSleeping;
@@ -910,7 +913,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
* @param root {@link RootWindowContainer}
*/
DisplayContent(Display display, RootWindowContainer root) {
- super(root.mWindowManager);
+ super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
if (mWmService.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists="
@@ -922,6 +925,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
mAtmService = mWmService.mAtmService;
mDisplay = display;
mDisplayId = display.getDisplayId();
+ mOffTokenAcquirer = mRootWindowContainer.mDisplayOffTokenAcquirer;
mWallpaperController = new WallpaperController(mWmService, this);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
@@ -974,7 +978,8 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
.setOpaque(true)
- .setContainerLayer();
+ .setContainerLayer()
+ .setCallsite("DisplayContent");
mSurfaceControl = b.setName("Root").setContainerLayer().build();
mOverlayLayer = b.setName("Display Overlays").setParent(mSurfaceControl).build();
@@ -989,7 +994,6 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
// Setup the policy and build the display area hierarchy.
mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate(
mWmService, this /* content */, this /* root */, mImeWindowsContainers);
- mDisplayAreaPolicy.attachDisplayAreas();
// Sets the display content for the children.
onDisplayChanged(this);
@@ -1094,7 +1098,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
return null;
}
mShellRoots.put(windowType, root);
- SurfaceControl out = new SurfaceControl(rootLeash);
+ SurfaceControl out = new SurfaceControl(rootLeash, "DisplayContent.addShellRoot");
return out;
}
@@ -1170,6 +1174,8 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
activity.onRemovedFromDisplay();
if (activity == mFixedRotationLaunchingApp) {
+ // Make sure the states of associated tokens are also cleared.
+ activity.finishFixedRotationTransform();
setFixedRotationLaunchingAppUnchecked(null);
}
}
@@ -1468,6 +1474,12 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
// window was transferred ({@link #mSkipAppTransitionAnimation}).
return false;
}
+ if ((mAppTransition.getTransitFlags()
+ & WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) != 0) {
+ // The transition may be finished before keyguard hidden. In order to avoid the
+ // intermediate orientation change, it is more stable to freeze the display.
+ return false;
+ }
} else if (r != topRunningActivity()) {
// If the transition has not started yet, the activity must be the top.
return false;
@@ -1514,10 +1526,10 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
if (mFixedRotationLaunchingApp == null && r != null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
- if (mFixedRotationAnimationController == null) {
- mFixedRotationAnimationController = new FixedRotationAnimationController(this);
- mFixedRotationAnimationController.hide();
- }
+ startFixedRotationAnimation(
+ // Delay the hide animation to avoid blinking by clicking navigation bar that
+ // may toggle fixed rotation in a short time.
+ r == mFixedRotationTransitionListener.mAnimatingRecents /* shouldDebounce */);
} else if (mFixedRotationLaunchingApp != null && r == null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
finishFixedRotationAnimationIfPossible();
@@ -1615,6 +1627,32 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
}
+ /**
+ * Starts the hide animation for the windows which will be rotated seamlessly.
+ *
+ * @return {@code true} if the animation is executed right now.
+ */
+ private boolean startFixedRotationAnimation(boolean shouldDebounce) {
+ if (shouldDebounce) {
+ mWmService.mH.postDelayed(() -> {
+ synchronized (mWmService.mGlobalLock) {
+ if (mFixedRotationLaunchingApp != null
+ && startFixedRotationAnimation(false /* shouldDebounce */)) {
+ // Apply the transaction so the animation leash can take effect immediately.
+ getPendingTransaction().apply();
+ }
+ }
+ }, FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS);
+ return false;
+ }
+ if (mFixedRotationAnimationController == null) {
+ mFixedRotationAnimationController = new FixedRotationAnimationController(this);
+ mFixedRotationAnimationController.hide();
+ return true;
+ }
+ return false;
+ }
+
/** Re-show the previously hidden windows if all seamless rotated windows are done. */
void finishFixedRotationAnimationIfPossible() {
final FixedRotationAnimationController controller = mFixedRotationAnimationController;
@@ -2183,52 +2221,27 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
* Returns the topmost stack on the display that is compatible with the input windowing mode and
* activity type. Null is no compatible stack on the display.
*/
- ActivityStack getStack(int windowingMode, int activityType) {
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final ActivityStack stack = getTaskDisplayAreaAt(tdaNdx)
- .getStack(windowingMode, activityType);
- if (stack != null) {
- return stack;
- }
- }
- return null;
- }
-
- protected int getTaskDisplayAreaCount() {
- return mDisplayAreaPolicy.getTaskDisplayAreaCount();
- }
-
- protected TaskDisplayArea getTaskDisplayAreaAt(int index) {
- return mDisplayAreaPolicy.getTaskDisplayAreaAt(index);
+ @Nullable
+ Task getStack(int windowingMode, int activityType) {
+ return getItemFromTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.getStack(windowingMode, activityType));
}
- ActivityStack getStack(int rootTaskId) {
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final ActivityStack stack = getTaskDisplayAreaAt(tdaNdx).getStack(rootTaskId);
- if (stack != null) {
- return stack;
- }
- }
- return null;
+ @Nullable
+ Task getStack(int rootTaskId) {
+ return getItemFromTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.getStack(rootTaskId));
}
protected int getStackCount() {
- int totalStackCount = 0;
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- totalStackCount += getTaskDisplayAreaAt(i).getStackCount();
- }
- return totalStackCount;
+ return reduceOnAllTaskDisplayAreas((taskDisplayArea, count) ->
+ count + taskDisplayArea.getStackCount(), 0 /* initValue */);
}
@VisibleForTesting
- ActivityStack getTopStack() {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getTaskDisplayAreaAt(i).getTopStack();
- if (stack != null) {
- return stack;
- }
- }
- return null;
+ @Nullable
+ Task getTopStack() {
+ return getItemFromTaskDisplayAreas(TaskDisplayArea::getTopStack);
}
/**
@@ -2291,12 +2304,23 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
void onAppTransitionDone() {
super.onAppTransitionDone();
mWmService.mWindowsChanged = true;
+ // If the transition finished callback cannot match the token for some reason, make sure the
+ // rotated state is cleared if it is already invisible.
+ if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.mVisibleRequested
+ && !mFixedRotationLaunchingApp.isVisible()
+ && !mDisplayRotation.isRotatingSeamlessly()) {
+ clearFixedRotationLaunchingApp();
+ }
}
@Override
public void setWindowingMode(int windowingMode) {
- super.setWindowingMode(windowingMode);
- super.setDisplayWindowingMode(windowingMode);
+ // Intentionally call onRequestedOverrideConfigurationChanged() directly to change windowing
+ // mode and display windowing mode atomically.
+ mTmpConfiguration.setTo(getRequestedOverrideConfiguration());
+ mTmpConfiguration.windowConfiguration.setWindowingMode(windowingMode);
+ mTmpConfiguration.windowConfiguration.setDisplayWindowingMode(windowingMode);
+ onRequestedOverrideConfigurationChanged(mTmpConfiguration);
}
@Override
@@ -2517,7 +2541,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
* or for cases when multi-instance is not supported yet (like Split-screen, PiP or Recents).
*/
TaskDisplayArea getDefaultTaskDisplayArea() {
- return mDisplayAreaPolicy.getTaskDisplayAreaAt(0);
+ return mDisplayAreaPolicy.getDefaultTaskDisplayArea();
}
void positionDisplayAt(int position, boolean includingParents) {
@@ -2549,9 +2573,11 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
* Find the task whose outside touch area (for resizing) (x, y) falls within.
* Returns null if the touch doesn't fall into a resizing area.
*/
+ @Nullable
Task findTaskForResizePoint(int x, int y) {
final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
- return mTmpTaskForResizePointSearchResult.process(getDefaultTaskDisplayArea(), x, y, delta);
+ return getItemFromTaskDisplayAreas(taskDisplayArea ->
+ mTmpTaskForResizePointSearchResult.process(taskDisplayArea, x, y, delta));
}
void updateTouchExcludeRegion() {
@@ -2619,7 +2645,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
// to exclude the docked stack from touch so we need the entire screen area and not just a
// small portion which the home stack currently is resized to.
if (task.isActivityTypeHome() && task.isVisible() && task.isResizeable()) {
- mDisplayContent.getBounds(mTmpRect);
+ task.getDisplayArea().getBounds(mTmpRect);
} else {
task.getDimBounds(mTmpRect);
}
@@ -2683,6 +2709,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
@Override
void removeImmediately() {
mRemovingDisplay = true;
+ mDeferredRemoval = false;
try {
if (mParentWindow != null) {
mParentWindow.removeEmbeddedDisplayContent(this);
@@ -2716,31 +2743,14 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
/** Returns true if a removal action is still being deferred. */
@Override
- boolean checkCompleteDeferredRemoval() {
- boolean stillDeferringRemoval = false;
-
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final DisplayArea child = getChildAt(i);
- stillDeferringRemoval |= child.checkCompleteDeferredRemoval();
- if (getChildCount() == 0) {
- // If this display is pending to be removed because it contains an activity with
- // {@link ActivityRecord#mIsExiting} is true, this display may be removed when
- // completing the removal of the last activity from
- // {@link ActivityRecord#checkCompleteDeferredRemoval}.
- return false;
- }
- }
+ boolean handleCompleteDeferredRemoval() {
+ final boolean stillDeferringRemoval = super.handleCompleteDeferredRemoval();
if (!stillDeferringRemoval && mDeferredRemoval) {
removeImmediately();
return false;
}
- return true;
- }
-
- /** @return 'true' if removal of this display content is deferred due to active animation. */
- boolean isRemovalDeferred() {
- return mDeferredRemoval;
+ return stillDeferringRemoval;
}
void adjustForImeIfNeeded() {
@@ -2752,9 +2762,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
void prepareFreezingTaskBounds() {
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- getTaskDisplayAreaAt(tdaNdx).prepareFreezingTaskBounds();
- }
+ forAllTaskDisplayAreas(TaskDisplayArea::prepareFreezingTaskBounds);
}
void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
@@ -2852,7 +2860,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
- final ActivityStack focusedStack = getFocusedStack();
+ final Task focusedStack = getFocusedStack();
if (focusedStack != null) {
proto.write(FOCUSED_ROOT_TASK_ID, focusedStack.getRootTaskId());
final ActivityRecord focusedActivity = focusedStack.getDisplayArea()
@@ -2949,9 +2957,9 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
pw.println();
pw.println(prefix + "Task display areas in top down Z order:");
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- getTaskDisplayAreaAt(tdaNdx).dump(pw, prefix + " ", dumpAll);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.dump(pw, prefix + " ", dumpAll);
+ });
pw.println();
if (!mExitingTokens.isEmpty()) {
@@ -2968,37 +2976,35 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
final ScreenRotationAnimation rotationAnimation = getRotationAnimation();
if (rotationAnimation != null) {
- pw.print(subPrefix);
pw.println(" mScreenRotationAnimation:");
- rotationAnimation.printTo(" ", pw);
+ rotationAnimation.printTo(subPrefix, pw);
} else if (dumpAll) {
- pw.print(subPrefix);
pw.println(" no ScreenRotationAnimation ");
}
pw.println();
// Dump stack references
- final ActivityStack homeStack = getDefaultTaskDisplayArea().getRootHomeTask();
+ final Task homeStack = getDefaultTaskDisplayArea().getRootHomeTask();
if (homeStack != null) {
pw.println(prefix + "homeStack=" + homeStack.getName());
}
- final ActivityStack pinnedStack = getDefaultTaskDisplayArea().getRootPinnedTask();
+ final Task pinnedStack = getDefaultTaskDisplayArea().getRootPinnedTask();
if (pinnedStack != null) {
pw.println(prefix + "pinnedStack=" + pinnedStack.getName());
}
- final ActivityStack splitScreenPrimaryStack = getDefaultTaskDisplayArea()
+ final Task splitScreenPrimaryStack = getDefaultTaskDisplayArea()
.getRootSplitScreenPrimaryTask();
if (splitScreenPrimaryStack != null) {
pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName());
}
// TODO: Support recents on non-default task containers
- final ActivityStack recentsStack = getDefaultTaskDisplayArea().getStack(
+ final Task recentsStack = getDefaultTaskDisplayArea().getStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (recentsStack != null) {
pw.println(prefix + "recentsStack=" + recentsStack.getName());
}
- final ActivityStack dreamStack =
+ final Task dreamStack =
getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_DREAM);
if (dreamStack != null) {
pw.println(prefix + "dreamStack=" + dreamStack.getName());
@@ -3381,9 +3387,10 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
"Proposed new IME target: " + target + " for display: " + getDisplayId());
// Now, a special case -- if the last target's window is in the process of exiting, but
- // not removed, keep on the last target to avoid IME flicker.
+ // not removed, keep on the last target to avoid IME flicker. The exception is if the
+ // current target is home since we want opening apps to become the IME target right away.
if (curTarget != null && !curTarget.mRemoved && curTarget.isDisplayedLw()
- && curTarget.isClosing()) {
+ && curTarget.isClosing() && !curTarget.isActivityTypeHome()) {
if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Not changing target till current window is"
+ " closing and not removed");
return curTarget;
@@ -3449,12 +3456,13 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
private boolean isImeControlledByApp() {
- return mInputMethodTarget != null && !WindowConfiguration.isSplitScreenWindowingMode(
- mInputMethodTarget.getWindowingMode());
+ return mInputMethodInputTarget != null && !WindowConfiguration.isSplitScreenWindowingMode(
+ mInputMethodInputTarget.getWindowingMode());
}
boolean isImeAttachedToApp() {
return isImeControlledByApp()
+ && mInputMethodTarget != null
&& mInputMethodTarget.mActivityRecord != null
&& mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
// An activity with override bounds should be letterboxed inside its parent bounds,
@@ -3480,9 +3488,9 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
InsetsControlTarget getImeFallback() {
// host is in non-default display that doesn't support system decor, default to
// default display's StatusBar to control IME (when available), else let system control it.
- WindowState statusBar =
- mWmService.getDefaultDisplayContentLocked().getDisplayPolicy().getStatusBar();
- return statusBar != null ? statusBar : mRemoteInsetsControlTarget;
+ final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();
+ WindowState statusBar = defaultDc.getDisplayPolicy().getStatusBar();
+ return statusBar != null ? statusBar : defaultDc.mRemoteInsetsControlTarget;
}
boolean canShowIme() {
@@ -3517,12 +3525,11 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
}
- private void updateImeControlTarget() {
+ void updateImeControlTarget() {
mInputMethodControlTarget = computeImeControlTarget();
mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
- final WindowState win = mInputMethodControlTarget != null
- ? mInputMethodControlTarget.getWindow() : null;
+ final WindowState win = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget);
final IBinder token = win != null ? win.mClient.asBinder() : null;
// Note: not allowed to call into IMMS with the WM lock held, hence the post.
mWmService.mH.post(() ->
@@ -3543,10 +3550,12 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
*/
@VisibleForTesting
InsetsControlTarget computeImeControlTarget() {
- if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) {
+ if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null
+ || (mInputMethodInputTarget != null
+ && getImeHostOrFallback(mInputMethodInputTarget.getWindow())
+ == mRemoteInsetsControlTarget)) {
return mRemoteInsetsControlTarget;
} else {
- // Otherwise, we just use the ime target as received from IME.
return mInputMethodInputTarget;
}
}
@@ -3662,15 +3671,15 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
drawnWindowTypes.put(TYPE_NOTIFICATION_SHADE, true);
final WindowState visibleNotDrawnWindow = getWindow(w -> {
- boolean isVisible = w.mViewVisibility == View.VISIBLE && !w.mObscured;
- boolean hasDrawn = w.isDrawnLw() && w.hasDrawnLw();
- if (isVisible && !hasDrawn) {
+ final boolean isVisible = w.isVisible() && !w.mObscured;
+ final boolean isDrawn = w.isDrawnLw();
+ if (isVisible && !isDrawn) {
ProtoLog.d(WM_DEBUG_BOOT,
"DisplayContent: boot is waiting for window of type %d to be drawn",
w.mAttrs.type);
return true;
}
- if (hasDrawn) {
+ if (isDrawn) {
switch (w.mAttrs.type) {
case TYPE_BOOT_PROGRESS:
case TYPE_BASE_APPLICATION:
@@ -4050,36 +4059,16 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
return null;
}
- int dw = mDisplayInfo.logicalWidth;
- int dh = mDisplayInfo.logicalHeight;
-
- if (dw <= 0 || dh <= 0) {
- return null;
- }
-
- final Rect frame = new Rect(0, 0, dw, dh);
-
- // The screenshot API does not apply the current screen rotation.
- int rot = mDisplay.getRotation();
-
- if (rot == ROTATION_90 || rot == ROTATION_270) {
- rot = (rot == ROTATION_90) ? ROTATION_270 : ROTATION_90;
- }
-
- // SurfaceFlinger is not aware of orientation, so convert our logical
- // crop to SurfaceFlinger's portrait orientation.
- convertCropForSurfaceFlinger(frame, rot, dw, dh);
-
final ScreenRotationAnimation screenRotationAnimation =
mWmService.mRoot.getDisplayContent(DEFAULT_DISPLAY).getRotationAnimation();
final boolean inRotation = screenRotationAnimation != null &&
screenRotationAnimation.isAnimating();
if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating");
- // TODO(b/68392460): We should screenshot Task controls directly
- // but it's difficult at the moment as the Task doesn't have the
- // correct size set.
- final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, inRotation, rot);
+ // Send invalid rect and no width and height since it will screenshot the entire display.
+ Rect frame = new Rect(0, 0, -1, -1);
+ final Bitmap bitmap = SurfaceControl.screenshot(frame, 0, 0, inRotation,
+ mDisplay.getRotation());
if (bitmap == null) {
Slog.w(TAG_WM, "Failed to take screenshot");
return null;
@@ -4094,39 +4083,15 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
return ret;
}
- // TODO: Can this use createRotationMatrix()?
- private static void convertCropForSurfaceFlinger(Rect crop, int rot, int dw, int dh) {
- if (rot == Surface.ROTATION_90) {
- final int tmp = crop.top;
- crop.top = dw - crop.right;
- crop.right = crop.bottom;
- crop.bottom = dw - crop.left;
- crop.left = tmp;
- } else if (rot == Surface.ROTATION_180) {
- int tmp = crop.top;
- crop.top = dh - crop.bottom;
- crop.bottom = dh - tmp;
- tmp = crop.right;
- crop.right = dw - crop.left;
- crop.left = dw - tmp;
- } else if (rot == Surface.ROTATION_270) {
- final int tmp = crop.top;
- crop.top = crop.left;
- crop.left = dh - crop.bottom;
- crop.bottom = crop.right;
- crop.right = dh - tmp;
- }
- }
-
void setExitingTokensHasVisible(boolean hasVisible) {
for (int i = mExitingTokens.size() - 1; i >= 0; i--) {
mExitingTokens.get(i).hasVisible = hasVisible;
}
// Initialize state of exiting applications.
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).setExitingTokensHasVisible(hasVisible);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.setExitingTokensHasVisible(hasVisible);
+ });
}
void removeExistingTokensIfPossible() {
@@ -4138,9 +4103,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
// Time to remove any exiting applications?
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).removeExistingAppTokensIfPossible();
- }
+ forAllTaskDisplayAreas(TaskDisplayArea::removeExistingAppTokensIfPossible);
}
@Override
@@ -4193,7 +4156,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
private boolean processTask(Task task) {
- if (!task.getStack().getWindowConfiguration().canResizeTask()) {
+ if (!task.getRootTask().getWindowConfiguration().canResizeTask()) {
return true;
}
@@ -4464,9 +4427,9 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
void assignStackOrdering() {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).assignStackOrdering(getPendingTransaction());
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.assignStackOrdering(getPendingTransaction());
+ });
}
/**
@@ -4985,11 +4948,10 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
final int displayId = mDisplay.getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
final int displayState = mDisplay.getState();
- if (displayState == Display.STATE_OFF && mOffToken == null) {
- mOffToken = mAtmService.acquireSleepToken("Display-off", displayId);
- } else if (displayState == Display.STATE_ON && mOffToken != null) {
- mOffToken.release();
- mOffToken = null;
+ if (displayState == Display.STATE_OFF) {
+ mOffTokenAcquirer.acquire(mDisplayId);
+ } else if (displayState == Display.STATE_ON) {
+ mOffTokenAcquirer.release(mDisplayId);
}
}
mWmService.requestTraversal();
@@ -5007,14 +4969,8 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
@Nullable
- ActivityStack getFocusedStack() {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getTaskDisplayAreaAt(i).getFocusedStack();
- if (stack != null) {
- return stack;
- }
- }
- return null;
+ Task getFocusedStack() {
+ return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedStack);
}
/**
@@ -5022,15 +4978,15 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
void removeStacksInWindowingModes(int... windowingModes) {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).removeStacksInWindowingModes(windowingModes);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.removeStacksInWindowingModes(windowingModes);
+ });
}
void removeStacksWithActivityTypes(int... activityTypes) {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).removeStacksWithActivityTypes(activityTypes);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.removeStacksWithActivityTypes(activityTypes);
+ });
}
ActivityRecord topRunningActivity() {
@@ -5046,15 +5002,10 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
* can be shown on top of the keyguard will be considered.
* @return The top running activity. {@code null} if none is available.
*/
+ @Nullable
ActivityRecord topRunningActivity(boolean considerKeyguardState) {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- final ActivityRecord activity = getTaskDisplayAreaAt(i)
- .topRunningActivity(considerKeyguardState);
- if (activity != null) {
- return activity;
- }
- }
- return null;
+ return getItemFromTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.topRunningActivity(considerKeyguardState));
}
boolean updateDisplayOverrideConfigurationLocked() {
@@ -5215,18 +5166,17 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
void remove() {
mRemoving = true;
- ActivityStack lastReparentedStack = null;
+ Task lastReparentedStack;
mRootWindowContainer.mStackSupervisor.beginDeferResume();
try {
- int numTaskContainers = getTaskDisplayAreaCount();
- for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) {
- final ActivityStack lastReparentedStackFromArea = getTaskDisplayAreaAt(tdaNdx)
- .remove();
+ lastReparentedStack = reduceOnAllTaskDisplayAreas((taskDisplayArea, stack) -> {
+ final Task lastReparentedStackFromArea = taskDisplayArea.remove();
if (lastReparentedStackFromArea != null) {
- lastReparentedStack = lastReparentedStackFromArea;
+ return lastReparentedStackFromArea;
}
- }
+ return stack;
+ }, null /* initValue */, false /* traverseTopToBottom */);
} finally {
mRootWindowContainer.mStackSupervisor.endDeferResume();
}
@@ -5241,7 +5191,8 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
mDisplayPolicy.release();
if (!mAllSleepTokens.isEmpty()) {
- mRootWindowContainer.mSleepTokens.removeAll(mAllSleepTokens);
+ mAllSleepTokens.forEach(token ->
+ mRootWindowContainer.mSleepTokens.remove(token.mHashKey));
mAllSleepTokens.clear();
mAtmService.updateSleepIfNeededLocked();
}
@@ -5253,26 +5204,19 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
// Check if all task display areas have only the empty home stacks left.
- boolean onlyEmptyHomeStacksLeft = true;
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = getTaskDisplayAreaAt(tdaNdx);
+ boolean hasNonEmptyHomeStack = forAllTaskDisplayAreas(taskDisplayArea -> {
if (taskDisplayArea.getStackCount() != 1) {
- onlyEmptyHomeStacksLeft = false;
- break;
- }
- final ActivityStack stack = taskDisplayArea.getStackAt(0);
- if (!stack.isActivityTypeHome() || stack.hasChild()) {
- onlyEmptyHomeStacksLeft = false;
- break;
+ return true;
}
- }
- if (onlyEmptyHomeStacksLeft) {
+ final Task stack = taskDisplayArea.getStackAt(0);
+ return !stack.isActivityTypeHome() || stack.hasChild();
+ });
+ if (!hasNonEmptyHomeStack) {
// Release this display if only empty home stack(s) are left. This display will be
// released along with the stack(s) removal.
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final ActivityStack s = getTaskDisplayAreaAt(tdaNdx).getStackAt(0);
- s.removeIfPossible();
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.getStackAt(0).removeIfPossible();
+ });
} else if (getTopStack() == null) {
removeIfPossible();
mRootWindowContainer.mStackSupervisor
@@ -5337,10 +5281,10 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
mInEnsureActivitiesVisible = true;
try {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).ensureActivitiesVisible(starting, configChanges,
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.ensureActivitiesVisible(starting, configChanges,
preserveWindows, notifyClients);
- }
+ });
} finally {
mInEnsureActivitiesVisible = false;
}
@@ -5355,8 +5299,9 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
}
void setDisplayToSingleTaskInstance() {
- final int taskDisplayAreaCount = getTaskDisplayAreaCount();
- if (taskDisplayAreaCount > 1) {
+ int tdaCount = reduceOnAllTaskDisplayAreas((taskDisplayArea, count) -> ++count,
+ 0 /* initValue */);
+ if (tdaCount > 1) {
throw new IllegalArgumentException(
"Display already has multiple task display areas. display=" + this);
}
@@ -5366,7 +5311,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
+ this);
}
if (stackCount > 0) {
- final ActivityStack stack = getDefaultTaskDisplayArea().getStackAt(0);
+ final Task stack = getDefaultTaskDisplayArea().getStackAt(0);
if (stack.getChildCount() > 1) {
throw new IllegalArgumentException("Display stack already has multiple tasks."
+ " display=" + this + " stack=" + stack);
@@ -5383,7 +5328,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
@VisibleForTesting
void removeAllTasks() {
- forAllTasks((t) -> { t.getStack().removeChild(t, "removeAllTasks"); });
+ forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); });
}
/**
@@ -5579,6 +5524,19 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
mRemoteInsetsController = controller;
}
+ /**
+ * Notifies the remote insets controller that the top focused window has changed.
+ *
+ * @param packageName The name of the package that is open in the top focused window.
+ */
+ void topFocusedWindowChanged(String packageName) {
+ try {
+ mRemoteInsetsController.topFocusedWindowChanged(packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver package in top focused window change", e);
+ }
+ }
+
void notifyInsetsChanged() {
try {
mRemoteInsetsController.insetsChanged(
@@ -5616,11 +5574,6 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis
Slog.w(TAG, "Failed to deliver showInsets", e);
}
}
-
- @Override
- public boolean isClientControlled() {
- return false;
- }
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 844867b2ea9f..0e24fc8bd307 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -102,6 +102,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_UNKNOWN;
import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
@@ -314,6 +319,17 @@ public class DisplayPolicy {
private int[] mNavigationBarHeightForRotationInCarMode = new int[4];
private int[] mNavigationBarWidthForRotationInCarMode = new int[4];
+ // Alternative status bar for when flexible insets mapping is used to place the status bar on
+ // another side of the screen.
+ private WindowState mStatusBarAlt = null;
+ @WindowManagerPolicy.AltBarPosition
+ private int mStatusBarAltPosition = ALT_BAR_UNKNOWN;
+ // Alternative navigation bar for when flexible insets mapping is used to place the navigation
+ // bar elsewhere on the screen.
+ private WindowState mNavigationBarAlt = null;
+ @WindowManagerPolicy.AltBarPosition
+ private int mNavigationBarAltPosition = ALT_BAR_UNKNOWN;
+
/** See {@link #getNavigationBarFrameHeight} */
private int[] mNavigationBarFrameHeightForRotationDefault = new int[4];
@@ -373,7 +389,10 @@ public class DisplayPolicy {
private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
private static final Rect sTmpRect = new Rect();
private static final Rect sTmpNavFrame = new Rect();
+ private static final Rect sTmpStatusFrame = new Rect();
+ private static final Rect sTmpScreenDecorFrame = new Rect();
private static final Rect sTmpLastParentFrame = new Rect();
+ private static final Rect sTmpDisplayFrameBounds = new Rect();
private WindowState mTopFullscreenOpaqueWindowState;
private WindowState mTopFullscreenOpaqueOrDimmingWindowState;
@@ -386,11 +405,6 @@ public class DisplayPolicy {
private int mForcingShowNavBarLayer;
private boolean mForceShowSystemBars;
- /**
- * Force the display of system bars regardless of other settings.
- */
- private boolean mForceShowSystemBarsFromExternal;
-
private boolean mShowingDream;
private boolean mLastShowingDream;
private boolean mDreamingLockscreen;
@@ -436,7 +450,7 @@ public class DisplayPolicy {
case MSG_REQUEST_TRANSIENT_BARS:
synchronized (mLock) {
WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
- ? mStatusBar : mNavigationBar;
+ ? getStatusBar() : getNavigationBar();
if (targetBar != null) {
requestTransientBars(targetBar);
}
@@ -480,7 +494,6 @@ public class DisplayPolicy {
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
- mForceShowSystemBarsFromExternal = r.getBoolean(R.bool.config_forceShowSystemBars);
mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
@@ -500,6 +513,7 @@ public class DisplayPolicy {
if (mStatusBar != null) {
requestTransientBars(mStatusBar);
}
+ checkAltBarSwipeForTransientBars(ALT_BAR_TOP);
}
}
@@ -510,6 +524,7 @@ public class DisplayPolicy {
&& mNavigationBarPosition == NAV_BAR_BOTTOM) {
requestTransientBars(mNavigationBar);
}
+ checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM);
}
}
@@ -526,6 +541,7 @@ public class DisplayPolicy {
excludedRegion)) {
requestTransientBars(mNavigationBar);
}
+ checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT);
}
excludedRegion.recycle();
}
@@ -543,6 +559,7 @@ public class DisplayPolicy {
excludedRegion)) {
requestTransientBars(mNavigationBar);
}
+ checkAltBarSwipeForTransientBars(ALT_BAR_LEFT);
}
excludedRegion.recycle();
}
@@ -644,6 +661,15 @@ public class DisplayPolicy {
mHandler.post(mGestureNavigationSettingsObserver::register);
}
+ private void checkAltBarSwipeForTransientBars(@WindowManagerPolicy.AltBarPosition int pos) {
+ if (mStatusBarAlt != null && mStatusBarAltPosition == pos) {
+ requestTransientBars(mStatusBarAlt);
+ }
+ if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) {
+ requestTransientBars(mNavigationBarAlt);
+ }
+ }
+
void systemReady() {
mSystemGestures.systemReady();
if (mService.mPointerLocationEnabled) {
@@ -698,17 +724,6 @@ public class DisplayPolicy {
return mDockMode;
}
- /**
- * @see WindowManagerService.setForceShowSystemBars
- */
- void setForceShowSystemBars(boolean forceShowSystemBars) {
- mForceShowSystemBarsFromExternal = forceShowSystemBars;
- }
-
- boolean getForceShowSystemBars() {
- return mForceShowSystemBarsFromExternal;
- }
-
public boolean hasNavigationBar() {
return mHasNavigationBar;
}
@@ -916,6 +931,14 @@ public class DisplayPolicy {
}
break;
}
+
+ // Check if alternate bars positions were updated.
+ if (mStatusBarAlt == win) {
+ mStatusBarAltPosition = getAltBarPosition(attrs);
+ }
+ if (mNavigationBarAlt == win) {
+ mNavigationBarAltPosition = getAltBarPosition(attrs);
+ }
}
/**
@@ -960,10 +983,9 @@ public class DisplayPolicy {
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
- if (mStatusBar != null) {
- if (mStatusBar.isAlive()) {
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
+ if ((mStatusBar != null && mStatusBar.isAlive())
+ || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NOTIFICATION_SHADE:
@@ -980,10 +1002,9 @@ public class DisplayPolicy {
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
- if (mNavigationBar != null) {
- if (mNavigationBar.isAlive()) {
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
+ if ((mNavigationBar != null && mNavigationBar.isAlive())
+ || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NAVIGATION_BAR_PANEL:
@@ -1010,10 +1031,43 @@ public class DisplayPolicy {
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
+
+ for (@InternalInsetsType int insetType : attrs.providesInsetsTypes) {
+ switch (insetType) {
+ case ITYPE_STATUS_BAR:
+ if ((mStatusBar != null && mStatusBar.isAlive())
+ || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ break;
+ case ITYPE_NAVIGATION_BAR:
+ if ((mNavigationBar != null && mNavigationBar.isAlive())
+ || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ break;
+ }
+ }
}
return ADD_OKAY;
}
+ private void getRotatedWindowBounds(DisplayFrames displayFrames, WindowState windowState,
+ Rect outBounds) {
+ outBounds.set(windowState.getBounds());
+
+ int windowRotation = windowState.getWindowConfiguration().getRotation();
+ if (windowRotation == displayFrames.mRotation) {
+ return;
+ }
+
+ // Get displayFrames bounds
+ sTmpDisplayFrameBounds.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight);
+ // Rotate the WindowState's bounds based on the displayFrames rotation
+ mDisplayContent.rotateBounds(sTmpDisplayFrameBounds, windowRotation,
+ displayFrames.mRotation, outBounds);
+ }
+
/**
* Called when a window is being added to the system. Must not throw an exception.
*
@@ -1037,8 +1091,7 @@ public class DisplayPolicy {
mStatusBarController.setWindow(win);
final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider =
(displayFrames, windowState, rect) -> {
- rect.top = 0;
- rect.bottom = getStatusBarHeight(displayFrames);
+ rect.bottom = rect.top + getStatusBarHeight(displayFrames);
};
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
mDisplayContent.setInsetProvider(ITYPE_TOP_GESTURES, win, frameProvider);
@@ -1058,8 +1111,7 @@ public class DisplayPolicy {
displayFrames.mDisplayHeight,
displayFrames.mRotation) == NAV_BAR_BOTTOM
&& !mNavButtonForcedVisible) {
-
- sTmpRect.set(displayFrames.mUnrestricted);
+ sTmpRect.set(inOutFrame);
sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
inOutFrame.top = sTmpRect.bottom
- getNavigationBarHeight(displayFrames.mRotation,
@@ -1101,7 +1153,19 @@ public class DisplayPolicy {
break;
default:
if (attrs.providesInsetsTypes != null) {
- for (int insetsType : attrs.providesInsetsTypes) {
+ for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
+ switch (insetsType) {
+ case ITYPE_STATUS_BAR:
+ mStatusBarAlt = win;
+ mStatusBarController.setWindow(mStatusBarAlt);
+ mStatusBarAltPosition = getAltBarPosition(attrs);
+ break;
+ case ITYPE_NAVIGATION_BAR:
+ mNavigationBarAlt = win;
+ mNavigationBarController.setWindow(mNavigationBarAlt);
+ mNavigationBarAltPosition = getAltBarPosition(attrs);
+ break;
+ }
mDisplayContent.setInsetProvider(insetsType, win, null);
}
}
@@ -1109,6 +1173,22 @@ public class DisplayPolicy {
}
}
+ @WindowManagerPolicy.AltBarPosition
+ private int getAltBarPosition(WindowManager.LayoutParams params) {
+ switch (params.gravity) {
+ case Gravity.LEFT:
+ return ALT_BAR_LEFT;
+ case Gravity.RIGHT:
+ return ALT_BAR_RIGHT;
+ case Gravity.BOTTOM:
+ return ALT_BAR_BOTTOM;
+ case Gravity.TOP:
+ return ALT_BAR_TOP;
+ default:
+ return ALT_BAR_UNKNOWN;
+ }
+ }
+
TriConsumer<DisplayFrames, WindowState, Rect> getImeSourceFrameProvider() {
return (displayFrames, windowState, inOutFrame) -> {
if (mNavigationBar != null && navigationBarPosition(displayFrames.mDisplayWidth,
@@ -1149,12 +1229,14 @@ public class DisplayPolicy {
* @param win The window being removed.
*/
void removeWindowLw(WindowState win) {
- if (mStatusBar == win) {
+ if (mStatusBar == win || mStatusBarAlt == win) {
mStatusBar = null;
+ mStatusBarAlt = null;
mStatusBarController.setWindow(null);
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null);
- } else if (mNavigationBar == win) {
+ } else if (mNavigationBar == win || mNavigationBarAlt == win) {
mNavigationBar = null;
+ mNavigationBarAlt = null;
mNavigationBarController.setWindow(null);
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null);
} else if (mNotificationShade == win) {
@@ -1180,7 +1262,7 @@ public class DisplayPolicy {
}
WindowState getStatusBar() {
- return mStatusBar;
+ return mStatusBar != null ? mStatusBar : mStatusBarAlt;
}
WindowState getNotificationShade() {
@@ -1188,7 +1270,7 @@ public class DisplayPolicy {
}
WindowState getNavigationBar() {
- return mNavigationBar;
+ return mNavigationBar != null ? mNavigationBar : mNavigationBarAlt;
}
/**
@@ -1250,6 +1332,46 @@ public class DisplayPolicy {
return R.anim.dock_left_enter;
}
}
+ } else if (win == mStatusBarAlt || win == mNavigationBarAlt) {
+ if (win.getAttrs().windowAnimations != 0) {
+ return ANIMATION_STYLEABLE;
+ }
+
+ int pos = (win == mStatusBarAlt) ? mStatusBarAltPosition : mNavigationBarAltPosition;
+
+ boolean isExitOrHide = transit == TRANSIT_EXIT || transit == TRANSIT_HIDE;
+ boolean isEnterOrShow = transit == TRANSIT_ENTER || transit == TRANSIT_SHOW;
+
+ switch (pos) {
+ case ALT_BAR_LEFT:
+ if (isExitOrHide) {
+ return R.anim.dock_left_exit;
+ } else if (isEnterOrShow) {
+ return R.anim.dock_left_enter;
+ }
+ break;
+ case ALT_BAR_RIGHT:
+ if (isExitOrHide) {
+ return R.anim.dock_right_exit;
+ } else if (isEnterOrShow) {
+ return R.anim.dock_right_enter;
+ }
+ break;
+ case ALT_BAR_BOTTOM:
+ if (isExitOrHide) {
+ return R.anim.dock_bottom_exit;
+ } else if (isEnterOrShow) {
+ return R.anim.dock_bottom_enter;
+ }
+ break;
+ case ALT_BAR_TOP:
+ if (isExitOrHide) {
+ return R.anim.dock_top_exit;
+ } else if (isEnterOrShow) {
+ return R.anim.dock_top_enter;
+ }
+ break;
+ }
}
if (transit == TRANSIT_PREVIEW_DONE) {
@@ -1608,7 +1730,7 @@ public class DisplayPolicy {
mInputConsumer = null;
Slog.v(TAG, INPUT_CONSUMER_NAVIGATION + " dismissed.");
}
- } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
+ } else if (mInputConsumer == null && getStatusBar() != null && canHideNavigationBar()) {
mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
mHandler.getLooper(),
INPUT_CONSUMER_NAVIGATION,
@@ -1687,12 +1809,13 @@ public class DisplayPolicy {
if (isSimulatedLayout) {
w.setSimulatedWindowFrames(simulatedFrames);
}
+ getRotatedWindowBounds(displayFrames, w, sTmpScreenDecorFrame);
final WindowFrames windowFrames = w.getLayoutingWindowFrames();
- windowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
- displayFrames.mUnrestricted /* displayFrame */,
- displayFrames.mUnrestricted /* contentFrame */,
- displayFrames.mUnrestricted /* visibleFrame */, sTmpRect /* decorFrame */,
- displayFrames.mUnrestricted /* stableFrame */);
+ windowFrames.setFrames(sTmpScreenDecorFrame /* parentFrame */,
+ sTmpScreenDecorFrame /* displayFrame */,
+ sTmpScreenDecorFrame /* contentFrame */,
+ sTmpScreenDecorFrame /* visibleFrame */, sTmpRect /* decorFrame */,
+ sTmpScreenDecorFrame /* stableFrame */);
try {
w.computeFrame(displayFrames);
} finally {
@@ -1749,14 +1872,14 @@ public class DisplayPolicy {
if (mStatusBar == null) {
return false;
}
- // apply any navigation bar insets
+ // apply any status bar insets
+ getRotatedWindowBounds(displayFrames, mStatusBar, sTmpStatusFrame);
sTmpRect.setEmpty();
final WindowFrames windowFrames = mStatusBar.getLayoutingWindowFrames();
- windowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
- displayFrames.mUnrestricted /* displayFrame */,
- displayFrames.mStable /* contentFrame */,
- displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
- displayFrames.mStable /* stableFrame */);
+ windowFrames.setFrames(sTmpStatusFrame /* parentFrame */,
+ sTmpStatusFrame /* displayFrame */, sTmpStatusFrame /* contentFrame */,
+ sTmpStatusFrame /* visibleFrame */, sTmpRect /* decorFrame */,
+ sTmpStatusFrame /* stableFrame */);
// Let the status bar determine its size.
mStatusBar.computeFrame(displayFrames);
@@ -1827,18 +1950,20 @@ public class DisplayPolicy {
final Rect dockFrame = displayFrames.mDock;
final int navBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
+ getRotatedWindowBounds(displayFrames, mNavigationBar, navigationFrame);
+
final Rect cutoutSafeUnrestricted = sTmpRect;
cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
if (navBarPosition == NAV_BAR_BOTTOM) {
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
- final int topNavBar = cutoutSafeUnrestricted.bottom
+ final int topNavBar = Math.min(cutoutSafeUnrestricted.bottom, navigationFrame.bottom)
- getNavigationBarFrameHeight(rotation, uiMode);
- final int top = mNavButtonForcedVisible
- ? topNavBar
- : cutoutSafeUnrestricted.bottom - getNavigationBarHeight(rotation, uiMode);
- navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom);
+ final int top = mNavButtonForcedVisible ? topNavBar :
+ Math.min(cutoutSafeUnrestricted.bottom, navigationFrame.bottom)
+ - getNavigationBarHeight(rotation, uiMode);
+ navigationFrame.top = topNavBar;
displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -1858,9 +1983,9 @@ public class DisplayPolicy {
}
} else if (navBarPosition == NAV_BAR_RIGHT) {
// Landscape screen; nav bar goes to the right.
- final int left = cutoutSafeUnrestricted.right
+ final int left = Math.min(cutoutSafeUnrestricted.right, navigationFrame.right)
- getNavigationBarWidth(rotation, uiMode);
- navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
+ navigationFrame.left = left;
displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -1880,9 +2005,9 @@ public class DisplayPolicy {
}
} else if (navBarPosition == NAV_BAR_LEFT) {
// Seascape screen; nav bar goes to the left.
- final int right = cutoutSafeUnrestricted.left
+ final int right = Math.max(cutoutSafeUnrestricted.left, navigationFrame.left)
+ getNavigationBarWidth(rotation, uiMode);
- navigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
+ navigationFrame.right = right;
displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -2065,7 +2190,8 @@ public class DisplayPolicy {
final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
- final Rect dfu = displayFrames.mUnrestricted;
+ getRotatedWindowBounds(displayFrames, win, sTmpRect);
+ final Rect dfu = sTmpRect;
Insets insets = Insets.of(0, 0, 0, 0);
for (int i = types.size() - 1; i >= 0; i--) {
final InsetsSource source = mDisplayContent.getInsetsPolicy()
@@ -2080,7 +2206,7 @@ public class DisplayPolicy {
final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
- df.set(left, top, dfu.right - right, dfu.bottom - bottom);
+ df.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom);
if (attached == null) {
pf.set(df);
vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING
@@ -2694,10 +2820,10 @@ public class DisplayPolicy {
mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded();
}
- if (mStatusBar != null) {
+ if (getStatusBar() != null) {
if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
+ " top=" + mTopFullscreenOpaqueWindowState);
- final boolean forceShowStatusBar = (mStatusBar.getAttrs().privateFlags
+ final boolean forceShowStatusBar = (getStatusBar().getAttrs().privateFlags
& PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
final boolean notificationShadeForcesShowingNavigation =
mNotificationShade != null
@@ -3183,6 +3309,16 @@ public class DisplayPolicy {
return mNavigationBarPosition;
}
+ @WindowManagerPolicy.AltBarPosition
+ int getAlternateStatusBarPosition() {
+ return mStatusBarAltPosition;
+ }
+
+ @WindowManagerPolicy.AltBarPosition
+ int getAlternateNavBarPosition() {
+ return mNavigationBarAltPosition;
+ }
+
/**
* A new window has been focused.
*/
@@ -3292,7 +3428,7 @@ public class DisplayPolicy {
// keys, we let it keep controlling the visibility.
final boolean lastFocusCanReceiveKeys =
(mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
- winCandidate = isKeyguardShowing() ? mNotificationShade
+ winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade
: lastFocusCanReceiveKeys ? mLastFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
@@ -3300,16 +3436,6 @@ public class DisplayPolicy {
}
}
final WindowState win = winCandidate;
- if (win.getAttrs().type == TYPE_NOTIFICATION_SHADE && isKeyguardShowing()
- && isKeyguardOccluded()) {
- // We are updating at a point where the keyguard has gotten
- // focus, but we were last in a state where the top window is
- // hiding it. This is probably because the keyguard as been
- // shown while the top window was displayed, so we want to ignore
- // it here because this is just a very transient change and it
- // will quickly lose focus once it correctly gets hidden.
- return 0;
- }
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
@@ -3354,8 +3480,8 @@ public class DisplayPolicy {
final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) != 0
|| (PolicyControl.getWindowFlags(win, win.mAttrs) & FLAG_FULLSCREEN) != 0
- || (mStatusBar != null && insetsPolicy.isHidden(ITYPE_STATUS_BAR))
- || (mNavigationBar != null && insetsPolicy.isHidden(
+ || (getStatusBar() != null && insetsPolicy.isHidden(ITYPE_STATUS_BAR))
+ || (getNavigationBar() != null && insetsPolicy.isHidden(
ITYPE_NAVIGATION_BAR));
final int behavior = win.mAttrs.insetsFlags.behavior;
final boolean isImmersive = (visibility & (View.SYSTEM_UI_FLAG_IMMERSIVE
@@ -3447,17 +3573,22 @@ public class DisplayPolicy {
WindowState opaqueOrDimming) {
final boolean onKeyguard = isKeyguardShowing() && !isKeyguardOccluded();
final WindowState statusColorWin = onKeyguard ? mNotificationShade : opaqueOrDimming;
- if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) {
- // If the top fullscreen-or-dimming window is also the top fullscreen, respect
- // its light flag.
- appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
- final int legacyAppearance = InsetsFlags.getAppearance(
- PolicyControl.getSystemUiVisibility(statusColorWin, null));
- appearance |= (statusColorWin.mAttrs.insetsFlags.appearance | legacyAppearance)
- & APPEARANCE_LIGHT_STATUS_BARS;
- } else if (statusColorWin != null && statusColorWin.isDimming()) {
- // Otherwise if it's dimming, clear the light flag.
- appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
+ if (statusColorWin != null) {
+ if (statusColorWin == opaque || onKeyguard) {
+ // If the top fullscreen-or-dimming window is also the top fullscreen, respect
+ // its light flag.
+ appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
+ final int legacyAppearance = InsetsFlags.getAppearance(
+ PolicyControl.getSystemUiVisibility(statusColorWin, null));
+ appearance |= (statusColorWin.mAttrs.insetsFlags.appearance | legacyAppearance)
+ & APPEARANCE_LIGHT_STATUS_BARS;
+ } else if (statusColorWin.isDimming()) {
+ // Otherwise if it's dimming, clear the light flag.
+ appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
+ }
+ if (!mStatusBarController.isLightAppearanceAllowed(statusColorWin)) {
+ appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
+ }
}
return appearance;
}
@@ -3522,8 +3653,7 @@ public class DisplayPolicy {
return vis;
}
- @VisibleForTesting
- static int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque,
+ private int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque,
WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) {
if (navColorWin != null) {
@@ -3536,6 +3666,9 @@ public class DisplayPolicy {
// Clear the light flag for dimming window.
appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
}
+ if (!mNavigationBarController.isLightAppearanceAllowed(navColorWin)) {
+ appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
+ }
}
return appearance;
}
@@ -3550,8 +3683,7 @@ public class DisplayPolicy {
// We need to force system bars when the docked stack is visible, when the freeform stack
// is focused but also when we are resizing for the transitions when docked stack
// visibility changes.
- mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing
- || mForceShowSystemBarsFromExternal;
+ mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing;
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing();
// apply translucent bar vis flags
@@ -3611,7 +3743,7 @@ public class DisplayPolicy {
final boolean hideNavBarSysui =
(vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
- final boolean transientStatusBarAllowed = mStatusBar != null
+ final boolean transientStatusBarAllowed = getStatusBar() != null
&& (notificationShadeHasFocus || (!mForceShowSystemBars
&& (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
@@ -3769,7 +3901,7 @@ public class DisplayPolicy {
// TODO(b/118118435): Remove this after migration
private boolean isImmersiveMode(int vis) {
final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
- return mNavigationBar != null
+ return getNavigationBar() != null
&& (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
&& (vis & flags) != 0
&& canHideNavigationBar();
@@ -3777,7 +3909,7 @@ public class DisplayPolicy {
private boolean isImmersiveMode(WindowState win) {
final int behavior = win.mAttrs.insetsFlags.behavior;
- return mNavigationBar != null
+ return getNavigationBar() != null
&& canHideNavigationBar()
&& (behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
|| behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
@@ -3858,8 +3990,8 @@ public class DisplayPolicy {
public void takeScreenshot(int screenshotType, int source) {
if (mScreenshotHelper != null) {
mScreenshotHelper.takeScreenshot(screenshotType,
- mStatusBar != null && mStatusBar.isVisibleLw(),
- mNavigationBar != null && mNavigationBar.isVisibleLw(),
+ getStatusBar() != null && getStatusBar().isVisibleLw(),
+ getNavigationBar() != null && getNavigationBar().isVisibleLw(),
source, mHandler, null /* completionConsumer */);
}
}
@@ -3897,6 +4029,11 @@ public class DisplayPolicy {
if (mStatusBar != null) {
pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar);
}
+ if (mStatusBarAlt != null) {
+ pw.print(prefix); pw.print("mStatusBarAlt="); pw.print(mStatusBarAlt);
+ pw.print(prefix); pw.print("mStatusBarAltPosition=");
+ pw.println(mStatusBarAltPosition);
+ }
if (mNotificationShade != null) {
pw.print(prefix); pw.print("mExpandedPanel="); pw.print(mNotificationShade);
}
@@ -3908,6 +4045,11 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mNavigationBarPosition=");
pw.println(mNavigationBarPosition);
}
+ if (mNavigationBarAlt != null) {
+ pw.print(prefix); pw.print("mNavigationBarAlt="); pw.println(mNavigationBarAlt);
+ pw.print(prefix); pw.print("mNavigationBarAltPosition=");
+ pw.println(mNavigationBarAltPosition);
+ }
if (mFocusedWindow != null) {
pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow);
}
@@ -3926,8 +4068,8 @@ public class DisplayPolicy {
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
- pw.print(prefix); pw.print("mForceShowSystemBarsFromExternal=");
- pw.print(mForceShowSystemBarsFromExternal);
+ pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars");
+ pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
mStatusBarController.dump(pw, prefix);
mNavigationBarController.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/DragResizeMode.java b/services/core/java/com/android/server/wm/DragResizeMode.java
index 71beb5032914..eb27b046b9ab 100644
--- a/services/core/java/com/android/server/wm/DragResizeMode.java
+++ b/services/core/java/com/android/server/wm/DragResizeMode.java
@@ -35,7 +35,7 @@ class DragResizeMode {
*/
static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
- static boolean isModeAllowedForStack(ActivityStack stack, int mode) {
+ static boolean isModeAllowedForStack(Task stack, int mode) {
switch (mode) {
case DRAG_RESIZE_MODE_FREEFORM:
return stack.getWindowingMode() == WINDOWING_MODE_FREEFORM;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index a26dfdb1bcd4..f840d9273f60 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -152,7 +152,9 @@ class DragState {
mInputSurface = mService.makeSurfaceBuilder(
mService.mRoot.getDisplayContent(mDisplayContent.getDisplayId()).getSession())
.setContainerLayer()
- .setName("Drag and Drop Input Consumer").build();
+ .setName("Drag and Drop Input Consumer")
+ .setCallsite("DragState.showInputSurface")
+ .build();
}
final InputWindowHandle h = getInputWindowHandle();
if (h == null) {
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index c9cc94423fe2..724747daaa5b 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -62,6 +62,7 @@ class EmulatorDisplayOverlay {
.setName("EmulatorDisplayOverlay")
.setBufferSize(mScreenSize.x, mScreenSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
+ .setCallsite("EmulatorDisplayOverlay")
.build();
t.setLayer(ctrl, zOrder);
t.setPosition(ctrl, 0, 0);
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index c4e03f5c65f5..e2c07491db01 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -16,10 +16,8 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-import static com.android.server.wm.ActivityStack.TAG_VISIBILITY;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.Task.TAG_VISIBILITY;
import android.annotation.Nullable;
import android.util.Slog;
@@ -29,7 +27,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
/** Helper class to ensure activities are in the right visible state for a container. */
class EnsureActivitiesVisibleHelper {
- private final ActivityStack mContiner;
+ private final Task mContiner;
private ActivityRecord mTop;
private ActivityRecord mStarting;
private boolean mAboveTop;
@@ -39,7 +37,7 @@ class EnsureActivitiesVisibleHelper {
private boolean mPreserveWindows;
private boolean mNotifyClients;
- EnsureActivitiesVisibleHelper(ActivityStack container) {
+ EnsureActivitiesVisibleHelper(Task container) {
mContiner = container;
}
@@ -69,7 +67,7 @@ class EnsureActivitiesVisibleHelper {
/**
* Ensure visibility with an option to also update the configuration of visible activities.
- * @see ActivityStack#ensureActivitiesVisible(ActivityRecord, int, boolean)
+ * @see Task#ensureActivitiesVisible(ActivityRecord, int, boolean)
* @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
* @param starting The top most activity in the task.
* The activity is either starting or resuming.
@@ -174,12 +172,7 @@ class EnsureActivitiesVisibleHelper {
}
final int windowingMode = mContiner.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_FREEFORM) {
- // The visibility of tasks and the activities they contain in freeform stack are
- // determined individually unlike other stacks where the visibility or fullscreen
- // status of an activity in a previous task affects other.
- mBehindFullscreenActivity = !mContainerShouldBeVisible;
- } else if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
+ if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
&& r.isRootOfTask()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
+ " stackShouldBeVisible=" + mContainerShouldBeVisible
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 8298763c1392..e7fbc334306e 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -18,20 +18,21 @@ package com.android.server.wm;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
-import android.graphics.PixelFormat;
import android.view.InsetsSource;
import android.view.WindowInsets;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.protolog.common.ProtoLog;
+import java.io.PrintWriter;
+
/**
* Controller for IME inset source on the server. It's called provider as it provides the
* {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
*/
class ImeInsetsSourceProvider extends InsetsSourceProvider {
- private WindowState mImeTargetFromIme;
+ private InsetsControlTarget mImeTargetFromIme;
private Runnable mShowImeRunner;
private boolean mIsImeLayoutDrawn;
@@ -46,10 +47,12 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
*
* @param imeTarget imeTarget on which IME request is coming from.
*/
- void scheduleShowImePostLayout(WindowState imeTarget) {
+ void scheduleShowImePostLayout(InsetsControlTarget imeTarget) {
boolean targetChanged = mImeTargetFromIme != imeTarget
&& mImeTargetFromIme != null && imeTarget != null && mShowImeRunner != null
- && mImeTargetFromIme.mActivityRecord == imeTarget.mActivityRecord;
+ && imeTarget.getWindow() != null && mImeTargetFromIme.getWindow() != null
+ && mImeTargetFromIme.getWindow().mActivityRecord
+ == imeTarget.getWindow().mActivityRecord;
mImeTargetFromIme = imeTarget;
if (targetChanged) {
// target changed, check if new target can show IME.
@@ -61,7 +64,8 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
return;
}
- ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeTargetFromIme.getName());
+ ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeTargetFromIme.getWindow() == null
+ ? mImeTargetFromIme : mImeTargetFromIme.getWindow().getName());
mShowImeRunner = () -> {
ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
// Target should still be the same.
@@ -126,14 +130,27 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
return false;
}
ProtoLog.d(WM_DEBUG_IME, "dcTarget: %s mImeTargetFromIme: %s",
- dcTarget.getName(), mImeTargetFromIme.getName());
+ dcTarget.getName(), mImeTargetFromIme.getWindow() == null
+ ? mImeTargetFromIme : mImeTargetFromIme.getWindow().getName());
return (!dcTarget.isClosing() && mImeTargetFromIme == dcTarget)
- || (mImeTargetFromIme != null && dcTarget.getParentWindow() == mImeTargetFromIme
- && dcTarget.mSubLayer > mImeTargetFromIme.mSubLayer)
+ || (mImeTargetFromIme != null && mImeTargetFromIme.getWindow() != null
+ && dcTarget.getParentWindow() == mImeTargetFromIme
+ && dcTarget.mSubLayer > mImeTargetFromIme.getWindow().mSubLayer)
|| mImeTargetFromIme == mDisplayContent.getImeFallback()
- // If IME target is transparent but control target matches requesting window.
- || (controlTarget == mImeTargetFromIme
- && PixelFormat.formatHasAlpha(dcTarget.mAttrs.format));
+ || controlTarget == mImeTargetFromIme
+ && (mImeTargetFromIme.getWindow() == null
+ || !mImeTargetFromIme.getWindow().isClosing());
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ super.dump(pw, prefix);
+ if (mImeTargetFromIme != null) {
+ pw.print(prefix);
+ pw.print("showImePostLayout pending for mImeTargetFromIme=");
+ pw.print(mImeTargetFromIme);
+ pw.println();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index a6066684d850..852b367259c1 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -88,9 +88,12 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
mWindowHandle.ownerUid = Process.myUid();
mWindowHandle.inputFeatures = 0;
mWindowHandle.scaleFactor = 1.0f;
+ mWindowHandle.trustedOverlay = true;
- mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
- .getSession()).setContainerLayer().setName("Input Consumer " + name)
+ mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId).getSession())
+ .setContainerLayer()
+ .setName("Input Consumer " + name)
+ .setCallsite("InputConsumerImpl")
.build();
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0e2611d1ddb9..20f1b9f53013 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,17 +16,29 @@
package com.android.server.wm;
-import static android.os.Process.myPid;
-import static android.os.Process.myUid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -66,9 +78,6 @@ final class InputMonitor {
private boolean mUpdateInputWindowsPending;
private boolean mUpdateInputWindowsImmediately;
- // Currently focused input window handle.
- private InputWindowHandle mFocusedInputWindowHandle;
-
private boolean mDisableWallpaperTouchEvents;
private final Rect mTmpRect = new Rect();
private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer;
@@ -308,10 +317,6 @@ final class InputMonitor {
Slog.d(TAG_WM, "addInputWindowHandle: "
+ child + ", " + inputWindowHandle);
}
-
- if (hasFocus) {
- mFocusedInputWindowHandle = inputWindowHandle;
- }
}
void setUpdateInputWindowsNeededLw() {
@@ -477,11 +482,18 @@ final class InputMonitor {
mService.getRecentsAnimationController();
final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
&& recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
- if (inputWindowHandle == null || w.mRemoved) {
+ final int type = w.mAttrs.type;
+ final boolean isVisible = w.isVisibleLw();
+ if (inputChannel == null || inputWindowHandle == null || w.mRemoved
+ || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
if (w.mWinAnimator.hasSurface()) {
+ // Assign an InputInfo with type to the overlay window which can't receive input
+ // event. This is used to omit Surfaces from occlusion detection.
+ populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible);
mInputTransaction.setInputWindowInfo(
w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
mInvalidInputWindow);
+ return;
}
// Skip this window because it cannot possibly receive input.
return;
@@ -489,24 +501,8 @@ final class InputMonitor {
final int flags = w.mAttrs.flags;
final int privateFlags = w.mAttrs.privateFlags;
- final int type = w.mAttrs.type;
- final boolean isVisible = w.isVisibleLw();
-
- // Assign an InputInfo with type to the overlay window which can't receive input event.
- // This is used to omit Surfaces from occlusion detection.
- if (inputChannel == null
- || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
- if (!w.mWinAnimator.hasSurface()) {
- return;
- }
- populateOverlayInputInfo(inputWindowHandle, w.getName(), type, isVisible);
- mInputTransaction.setInputWindowInfo(
- w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
- inputWindowHandle);
- return;
- }
-
final boolean hasFocus = w.isFocused();
+
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp(
mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
@@ -568,6 +564,8 @@ final class InputMonitor {
}
}
+ // This would reset InputWindowHandle fields to prevent it could be found by input event.
+ // We need to check if any new field of InputWindowHandle could impact the result.
private static void populateOverlayInputInfo(final InputWindowHandle inputWindowHandle,
final String name, final int type, final boolean isVisible) {
inputWindowHandle.name = name;
@@ -577,11 +575,14 @@ final class InputMonitor {
inputWindowHandle.visible = isVisible;
inputWindowHandle.canReceiveKeys = false;
inputWindowHandle.hasFocus = false;
- inputWindowHandle.ownerPid = myPid();
- inputWindowHandle.ownerUid = myUid();
inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
inputWindowHandle.scaleFactor = 1;
- inputWindowHandle.layoutParamsFlags = FLAG_NOT_TOUCH_MODAL;
+ inputWindowHandle.layoutParamsFlags =
+ FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE;
+ inputWindowHandle.portalToDisplayId = INVALID_DISPLAY;
+ inputWindowHandle.touchableRegion.setEmpty();
+ inputWindowHandle.setTouchableRegionCrop(null);
+ inputWindowHandle.trustedOverlay = isTrustedOverlay(type);
}
/**
@@ -596,4 +597,17 @@ final class InputMonitor {
populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true);
t.setInputWindowInfo(sc, inputWindowHandle);
}
+
+ static boolean isTrustedOverlay(int type) {
+ return type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
+ || type == TYPE_INPUT_METHOD || type == TYPE_INPUT_METHOD_DIALOG
+ || type == TYPE_MAGNIFICATION_OVERLAY || type == TYPE_STATUS_BAR
+ || type == TYPE_NOTIFICATION_SHADE
+ || type == TYPE_NAVIGATION_BAR
+ || type == TYPE_NAVIGATION_BAR_PANEL
+ || type == TYPE_SECURE_SYSTEM_OVERLAY
+ || type == TYPE_DOCK_DIVIDER
+ || type == TYPE_ACCESSIBILITY_OVERLAY
+ || type == TYPE_INPUT_CONSUMER;
+ }
}
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index 42c1a078c7e8..3ffc26a7a8ad 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -62,11 +62,8 @@ interface InsetsControlTarget {
return false;
}
- /**
- * Returns {@code true} if the object controlling the insets is on client.
- */
- default boolean isClientControlled() {
- return true;
+ /** Returns {@code target.getWindow()}, or null if {@code target} is {@code null}. */
+ static WindowState asWindowOrNull(InsetsControlTarget target) {
+ return target != null ? target.getWindow() : null;
}
-
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 254356d673e3..b7287e718bd6 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -46,7 +46,9 @@ import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.DisplayThread;
@@ -97,12 +99,30 @@ class InsetsPolicy {
private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
private boolean mAnimatingShown;
+ /**
+ * Let remote insets controller control system bars regardless of other settings.
+ */
+ private boolean mRemoteInsetsControllerControlsSystemBars;
private final float[] mTmpFloat9 = new float[9];
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
mDisplayContent = displayContent;
mPolicy = displayContent.getDisplayPolicy();
+ mRemoteInsetsControllerControlsSystemBars = mPolicy.getContext().getResources().getBoolean(
+ R.bool.config_remoteInsetsControllerControlsSystemBars);
+ }
+
+ boolean getRemoteInsetsControllerControlsSystemBars() {
+ return mRemoteInsetsControllerControlsSystemBars;
+ }
+
+ /**
+ * Used only for testing.
+ */
+ @VisibleForTesting
+ void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) {
+ mRemoteInsetsControllerControlsSystemBars = controlsSystemBars;
}
/** Updates the target which can control system bars. */
@@ -256,6 +276,11 @@ class InsetsPolicy {
// Notification shade has control anyways, no reason to force anything.
return focusedWin;
}
+ if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+ mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
+ focusedWin.mAttrs.packageName);
+ return mDisplayContent.mRemoteInsetsControlTarget;
+ }
if (forceShowsSystemBarsForWindowingMode) {
// Status bar is forcibly shown for the windowing mode which is a steady state.
// We don't want the client to control the status bar, and we will dispatch the real
@@ -285,6 +310,11 @@ class InsetsPolicy {
// Notification shade has control anyways, no reason to force anything.
return focusedWin;
}
+ if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+ mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
+ focusedWin.mAttrs.packageName);
+ return mDisplayContent.mRemoteInsetsControlTarget;
+ }
if (forceShowsSystemBarsForWindowingMode) {
// Navigation bar is forcibly shown for the windowing mode which is a steady state.
// We don't want the client to control the navigation bar, and we will dispatch the real
@@ -300,6 +330,28 @@ class InsetsPolicy {
return focusedWin;
}
+ /**
+ * Determines whether the remote insets controller should take control of system bars for all
+ * windows.
+ */
+ boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) {
+ if (focusedWin == null) {
+ return false;
+ }
+ if (!mRemoteInsetsControllerControlsSystemBars) {
+ return false;
+ }
+ if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) {
+ // No remote insets control target to take control of insets.
+ return false;
+ }
+ // If necessary, auto can control application windows when
+ // config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases
+ // where we want to dictate system bar inset state for applications.
+ return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+ && focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+ }
+
private boolean forceShowsStatusBarTransiently() {
final WindowState win = mPolicy.getStatusBar();
return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
@@ -321,10 +373,7 @@ class InsetsPolicy {
// We need to force system bars when the docked stack is visible, when the freeform stack
// is visible but also when we are resizing for the transitions when docked stack
// visibility changes.
- return isDockedStackVisible
- || isFreeformStackVisible
- || isResizing
- || mPolicy.getForceShowSystemBars();
+ return isDockedStackVisible || isFreeformStackVisible || isResizing;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1762b6229990..f64149cee8c7 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -351,18 +351,28 @@ class InsetsSourceProvider {
}
private void updateVisibility() {
- // TODO(b/159699383): remove the client controlled check when the insets visibility can be
- // driven by the system UI.
- final boolean isClientControlled = mControlTarget != null
- && mControlTarget.isClientControlled();
- mSource.setVisible(mServerVisible
- && ((!isClientControlled && mDisplayContent.inMultiWindowMode())
- || mClientVisible));
+ mSource.setVisible(mServerVisible && (isMirroredSource() || mClientVisible));
ProtoLog.d(WM_DEBUG_IME,
"InsetsSource updateVisibility serverVisible: %s clientVisible: %s",
mServerVisible, mClientVisible);
}
+ private boolean isMirroredSource() {
+ if (mWin == null) {
+ return false;
+ }
+ final int[] provides = mWin.mAttrs.providesInsetsTypes;
+ if (provides == null) {
+ return false;
+ }
+ for (int i = 0; i < provides.length; i++) {
+ if (provides[i] == ITYPE_IME) {
+ return true;
+ }
+ }
+ return false;
+ }
+
InsetsSourceControl getControl(InsetsControlTarget target) {
if (target == mControlTarget) {
if (!mIsLeashReadyForDispatching && mControl != null) {
@@ -466,7 +476,10 @@ class InsetsSourceProvider {
mCapturedLeash = animationLeash;
final Rect frame = mWin.getWindowFrames().mFrame;
- t.setPosition(mCapturedLeash, frame.left, frame.top);
+ Point position = new Point();
+ mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
+
+ t.setPosition(mCapturedLeash, position.x, position.y);
}
@Override
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 63083faaddb1..9c978fd0c867 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_INVALID;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -44,6 +45,7 @@ import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowManager;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
@@ -74,7 +76,21 @@ class InsetsStateController {
w.notifyInsetsChanged();
}
};
- private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { };
+ private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() {
+ @Override
+ public void notifyInsetsControlChanged() {
+ InsetsSourceControl[] controls = getControlsForDispatch(this);
+ if (controls == null) {
+ return;
+ }
+ for (InsetsSourceControl control : controls) {
+ if (control.getType() == ITYPE_IME) {
+ mDisplayContent.mWmService.mH.post(() ->
+ InputMethodManagerInternal.get().removeImeSurface());
+ }
+ }
+ }
+ };
InsetsStateController(DisplayContent displayContent) {
mDisplayContent = displayContent;
@@ -171,6 +187,7 @@ class InsetsStateController {
state = new InsetsState(state);
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_NAVIGATION_BAR);
+ state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
}
if (aboveIme) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 4c10d5819c10..c36a75b01293 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -40,6 +40,7 @@ import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
import static com.android.server.wm.KeyguardOccludedProto.DISPLAY_ID;
import static com.android.server.wm.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
+import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -49,7 +50,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import java.io.PrintWriter;
@@ -73,11 +73,14 @@ class KeyguardController {
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
private RootWindowContainer mRootWindowContainer;
+ private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
+
KeyguardController(ActivityTaskManagerService service,
ActivityStackSupervisor stackSupervisor) {
mService = service;
mStackSupervisor = stackSupervisor;
+ mSleepTokenAcquirer = mService.new SleepTokenAcquirerImpl("keyguard");
}
void setWindowManager(WindowManagerService windowManager) {
@@ -133,10 +136,11 @@ class KeyguardController {
* Update the Keyguard showing state.
*/
void setKeyguardShown(boolean keyguardShowing, boolean aodShowing) {
- // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
- final boolean keyguardChanged = keyguardShowing != mKeyguardShowing
- || mKeyguardGoingAway && keyguardShowing;
final boolean aodChanged = aodShowing != mAodShowing;
+ // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
+ // Do not reset keyguardChanged status if this is aodChanged.
+ final boolean keyguardChanged = (keyguardShowing != mKeyguardShowing)
+ || (mKeyguardGoingAway && keyguardShowing && !aodChanged);
if (!keyguardChanged && !aodChanged) {
return;
}
@@ -410,17 +414,17 @@ class KeyguardController {
private void updateKeyguardSleepToken(int displayId) {
final KeyguardDisplayState state = getDisplay(displayId);
- if (isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken == null) {
- state.acquiredSleepToken();
- } else if (!isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken != null) {
- state.releaseSleepToken();
+ if (isKeyguardUnoccludedOrAodShowing(displayId)) {
+ state.mSleepTokenAcquirer.acquire(displayId);
+ } else if (!isKeyguardUnoccludedOrAodShowing(displayId)) {
+ state.mSleepTokenAcquirer.release(displayId);
}
}
private KeyguardDisplayState getDisplay(int displayId) {
KeyguardDisplayState state = mDisplayStates.get(displayId);
if (state == null) {
- state = new KeyguardDisplayState(mService, displayId);
+ state = new KeyguardDisplayState(mService, displayId, mSleepTokenAcquirer);
mDisplayStates.append(displayId, state);
}
return state;
@@ -441,29 +445,18 @@ class KeyguardController {
private ActivityRecord mDismissingKeyguardActivity;
private boolean mRequestDismissKeyguard;
private final ActivityTaskManagerService mService;
- private SleepToken mSleepToken;
+ private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
- KeyguardDisplayState(ActivityTaskManagerService service, int displayId) {
+ KeyguardDisplayState(ActivityTaskManagerService service, int displayId,
+ ActivityTaskManagerInternal.SleepTokenAcquirer acquirer) {
mService = service;
mDisplayId = displayId;
+ mSleepTokenAcquirer = acquirer;
}
void onRemoved() {
mDismissingKeyguardActivity = null;
- releaseSleepToken();
- }
-
- void acquiredSleepToken() {
- if (mSleepToken == null) {
- mSleepToken = mService.acquireSleepToken("keyguard", mDisplayId);
- }
- }
-
- void releaseSleepToken() {
- if (mSleepToken != null) {
- mSleepToken.release();
- mSleepToken = null;
- }
+ mSleepTokenAcquirer.release(mDisplayId);
}
void visibilitiesUpdated(KeyguardController controller, DisplayContent display) {
@@ -473,7 +466,7 @@ class KeyguardController {
mOccluded = false;
mDismissingKeyguardActivity = null;
- final ActivityStack stack = getStackForControllingOccluding(display);
+ final Task stack = getStackForControllingOccluding(display);
if (stack != null) {
final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null
@@ -513,18 +506,18 @@ class KeyguardController {
* Only the top non-pinned activity of the focusable stack on each display can control its
* occlusion state.
*/
- private ActivityStack getStackForControllingOccluding(DisplayContent display) {
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ @Nullable
+ private Task getStackForControllingOccluding(DisplayContent display) {
+ return display.getItemFromTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
if (stack != null && stack.isFocusableAndVisible()
&& !stack.inPinnedWindowingMode()) {
return stack;
}
}
- }
- return null;
+ return null;
+ });
}
void dumpStatus(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index 513be7a6becc..56e1187d51da 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -145,10 +145,10 @@ class LaunchParamsController {
}
if (mTmpParams.hasWindowingMode()
- && mTmpParams.mWindowingMode != task.getStack().getWindowingMode()) {
+ && mTmpParams.mWindowingMode != task.getRootTask().getWindowingMode()) {
final int activityType = activity != null
? activity.getActivityType() : task.getActivityType();
- task.getStack().setWindowingMode(task.getDisplayArea().validateWindowingMode(
+ task.getRootTask().setWindowingMode(task.getDisplayArea().validateWindowingMode(
mTmpParams.mWindowingMode, activity, task, activityType));
}
@@ -156,7 +156,7 @@ class LaunchParamsController {
return false;
}
- if (task.getStack().inFreeformWindowingMode()) {
+ if (task.getRootTask().inFreeformWindowingMode()) {
// Only set bounds if it's in freeform mode.
task.setBounds(mTmpParams.mBounds);
return true;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 00a4be3d1e35..28dcbcdf3cc7 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -124,6 +124,19 @@ public class Letterbox {
}
return (emptyCount + noOverlappingCount) == mSurfaces.length;
}
+
+ /**
+ * Returns true if any part of the letterbox overlaps with the given {@code rect}.
+ */
+ public boolean isOverlappingWith(Rect rect) {
+ for (LetterboxSurface surface : mSurfaces) {
+ if (surface.isOverlappingWith(rect)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Hides the letterbox.
*
@@ -261,8 +274,12 @@ public class Letterbox {
}
private void createSurface(SurfaceControl.Transaction t) {
- mSurface = mSurfaceControlFactory.get().setName("Letterbox - " + mType)
- .setFlags(HIDDEN).setColorLayer().build();
+ mSurface = mSurfaceControlFactory.get()
+ .setName("Letterbox - " + mType)
+ .setFlags(HIDDEN)
+ .setColorLayer()
+ .setCallsite("LetterboxSurface.createSurface")
+ .build();
t.setLayer(mSurface, -1)
.setColor(mSurface, new float[]{0, 0, 0})
.setColorSpaceAgnostic(mSurface, true);
@@ -294,6 +311,17 @@ public class Letterbox {
return Math.max(0, mLayoutFrameGlobal.height());
}
+ /**
+ * Returns if the given {@code rect} overlaps with this letterbox piece.
+ * @param rect the area to check for overlap in global coordinates
+ */
+ public boolean isOverlappingWith(Rect rect) {
+ if (mLayoutFrameGlobal.isEmpty()) {
+ return false;
+ }
+ return Rect.intersects(rect, mLayoutFrameGlobal);
+ }
+
public void applySurfaceChanges(SurfaceControl.Transaction t) {
if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
// Nothing changed.
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 892ee717e21f..c7a438d527ad 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -151,7 +151,7 @@ public class LockTaskController {
* The first task in the list, which started the current LockTask session, is called the root
* task. It coincides with the Home task in a typical multi-app kiosk deployment. When there are
* more than one locked tasks, the root task can't be finished. Nor can it be moved to the back
- * of the stack by {@link ActivityStack#moveTaskToBack(Task)};
+ * of the stack by {@link Task#moveTaskToBack(Task)};
*
* Calling {@link Activity#stopLockTask()} on the root task will finish all tasks but itself in
* this list, and the device will exit LockTask mode.
@@ -252,7 +252,7 @@ public class LockTaskController {
/**
* @return whether the given task can be moved to the back of the stack with
- * {@link ActivityStack#moveTaskToBack(Task)}
+ * {@link Task#moveTaskToBack(Task)}
* @see #mLockTaskModeTasks
*/
boolean canMoveTaskToBack(Task task) {
@@ -617,14 +617,14 @@ public class LockTaskController {
mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
lockTaskModeState != LOCK_TASK_MODE_NONE);
mSupervisor.mRootWindowContainer.resumeFocusedStacksTopActivities();
- final ActivityStack stack = task.getStack();
- if (stack != null) {
- stack.getDisplay().mDisplayContent.executeAppTransition();
+ final Task rootTask = task.getRootTask();
+ if (rootTask != null) {
+ rootTask.getDisplay().mDisplayContent.executeAppTransition();
}
} else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(),
- task.getStack(), true /* forceNonResizable */);
+ task.getRootTask(), true /* forceNonResizable */);
}
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 1b58fc1d2d3e..ba2c0b6dc0ac 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -215,7 +215,7 @@ class RecentTasks {
final RootWindowContainer rac = mService.mRootWindowContainer;
final DisplayContent dc = rac.getDisplayContent(displayId).mDisplayContent;
if (dc.pointWithinAppWindow(x, y)) {
- final ActivityStack stack = mService.getTopDisplayFocusedStack();
+ final Task stack = mService.getTopDisplayFocusedStack();
final Task topTask = stack != null ? stack.getTopMostTask() : null;
resetFreezeTaskListReordering(topTask);
}
@@ -323,7 +323,7 @@ class RecentTasks {
@VisibleForTesting
void resetFreezeTaskListReorderingOnTimeout() {
synchronized (mService.mGlobalLock) {
- final ActivityStack focusedStack = mService.getTopDisplayFocusedStack();
+ final Task focusedStack = mService.getTopDisplayFocusedStack();
final Task topTask = focusedStack != null ? focusedStack.getTopMostTask() : null;
resetFreezeTaskListReordering(topTask);
}
@@ -520,9 +520,9 @@ class RecentTasks {
* Kicks off the task persister to write any pending tasks to disk.
*/
void notifyTaskPersisterLocked(Task task, boolean flush) {
- final ActivityStack stack = task != null ? task.getStack() : null;
- if (stack != null && stack.isHomeOrRecentsStack()) {
- // Never persist the home or recents stack.
+ final Task rootTask = task != null ? task.getRootTask() : null;
+ if (rootTask != null && rootTask.isHomeOrRecentsStack()) {
+ // Never persist the home or recents task.
return;
}
syncPersistentTaskIdsLocked();
@@ -554,8 +554,8 @@ class RecentTasks {
}
private static boolean shouldPersistTaskLocked(Task task) {
- final ActivityStack stack = task.getStack();
- return task.isPersistable && (stack == null || !stack.isHomeOrRecentsStack());
+ final Task rootTask = task.getRootTask();
+ return task.isPersistable && (rootTask == null || !rootTask.isHomeOrRecentsStack());
}
void onSystemReadyLocked() {
@@ -975,9 +975,9 @@ class RecentTasks {
final Task task = mTasks.get(i);
if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task
+ " persistable=" + task.isPersistable);
- final ActivityStack stack = task.getStack();
+ final Task rootTask = task.getRootTask();
if ((task.isPersistable || task.inRecents)
- && (stack == null || !stack.isHomeOrRecentsStack())) {
+ && (rootTask == null || !rootTask.isHomeOrRecentsStack())) {
if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
persistentTaskIds.add(task.mTaskId);
} else {
@@ -1325,10 +1325,10 @@ class RecentTasks {
return false;
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
if (DEBUG_RECENTS_TRIM_TASKS) {
- Slog.d(TAG, "\ttop=" + task.getStack().getTopMostTask());
+ Slog.d(TAG, "\ttop=" + task.getRootTask().getTopMostTask());
}
- final ActivityStack stack = task.getStack();
- if (stack != null && stack.getTopMostTask() == task) {
+ final Task rootTask = task.getRootTask();
+ if (rootTask != null && rootTask.getTopMostTask() == task) {
// Only the non-top task of the primary split screen mode is visible
return false;
}
@@ -1344,9 +1344,9 @@ class RecentTasks {
// Tasks managed by/associated with an ActivityView should be excluded from recents.
// singleTaskInstance is set on the VirtualDisplay managed by ActivityView
// TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
- final ActivityStack stack = task.getStack();
- if (stack != null) {
- DisplayContent display = stack.getDisplay();
+ final Task rootTask = task.getRootTask();
+ if (rootTask != null) {
+ DisplayContent display = rootTask.getDisplay();
if (display != null && display.isSingleTaskInstance()) {
return false;
}
@@ -1400,21 +1400,21 @@ class RecentTasks {
/** @return whether the given task can be trimmed even if it is outside the visible range. */
protected boolean isTrimmable(Task task) {
- final ActivityStack stack = task.getStack();
+ final Task rootTask = task.getRootTask();
// No stack for task, just trim it
- if (stack == null) {
+ if (rootTask == null) {
return true;
}
// Ignore tasks from different displays
// TODO (b/115289124): No Recents on non-default displays.
- if (!stack.isOnHomeDisplay()) {
+ if (!rootTask.isOnHomeDisplay()) {
return false;
}
- final ActivityStack rootHomeTask = stack.getDisplayArea().getRootHomeTask();
- // Home stack does not exist. Don't trim the task.
+ final Task rootHomeTask = rootTask.getDisplayArea().getRootHomeTask();
+ // Home task does not exist. Don't trim the task.
if (rootHomeTask == null) {
return false;
}
@@ -1426,8 +1426,8 @@ class RecentTasks {
private void removeUnreachableHiddenTasks(int windowingMode) {
for (int i = mHiddenTasks.size() - 1; i >= 0; i--) {
final Task hiddenTask = mHiddenTasks.get(i);
- if (!hiddenTask.hasChild()) {
- // The task was removed by other path.
+ if (!hiddenTask.hasChild() || hiddenTask.inRecents) {
+ // The task was removed by other path or it became reachable (added to recents).
mHiddenTasks.remove(i);
continue;
}
@@ -1449,6 +1449,9 @@ class RecentTasks {
* of task as the given one.
*/
private void removeForAddTask(Task task) {
+ // The adding task will be in recents so it is not hidden.
+ mHiddenTasks.remove(task);
+
final int removeIndex = findRemoveIndexForAddTask(task);
if (removeIndex == -1) {
// Nothing to trim
@@ -1460,8 +1463,6 @@ class RecentTasks {
// callbacks here.
final Task removedTask = mTasks.remove(removeIndex);
if (removedTask != task) {
- // The added task is in recents so it is not hidden.
- mHiddenTasks.remove(task);
if (removedTask.hasChild()) {
// A non-empty task is replaced by a new task. Because the removed task is no longer
// managed by the recent tasks list, add it to the hidden list to prevent the task
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 00272ad57be4..d7b43bc5537d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -76,7 +76,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
private ActivityRecord mLaunchedTargetActivity;
// The stack to restore the target stack behind when the animation is finished
- private ActivityStack mRestoreTargetBehindStack;
+ private Task mRestoreTargetBehindStack;
RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
ActivityStartController activityStartController, WindowManagerService wm,
@@ -107,7 +107,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
void preloadRecentsActivity() {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Preload recents with %s",
mTargetIntent);
- ActivityStack targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
+ Task targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
ActivityRecord targetActivity = getTargetActivity(targetStack);
if (targetActivity != null) {
@@ -150,8 +150,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
// Invisible activity should be stopped. If the recents activity is alive and its doesn't
// need to relaunch by current configuration, then it may be already in stopped state.
- if (!targetActivity.isState(ActivityStack.ActivityState.STOPPING,
- ActivityStack.ActivityState.STOPPED)) {
+ if (!targetActivity.isState(Task.ActivityState.STOPPING,
+ Task.ActivityState.STOPPED)) {
// Add to stopping instead of stop immediately. So the client has the chance to perform
// traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more
// things (e.g. the measure can be done earlier). The actual stop will be performed when
@@ -166,7 +166,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
// If the activity is associated with the recents stack, then try and get that first
- ActivityStack targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
+ Task targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
ActivityRecord targetActivity = getTargetActivity(targetStack);
final boolean hasExistingActivity = targetActivity != null;
@@ -299,7 +299,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
try {
mWindowManager.cleanupRecentsAnimation(reorderMode);
- final ActivityStack targetStack = mDefaultTaskDisplayArea.getStack(
+ final Task targetStack = mDefaultTaskDisplayArea.getStack(
WINDOWING_MODE_UNDEFINED, mTargetActivityType);
// Prefer to use the original target activity instead of top activity because
// we may have moved another task to top (starting 3p launcher).
@@ -333,7 +333,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
}
if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) {
- final ActivityStack topStack = getTopNonAlwaysOnTopStack();
+ final Task topStack = getTopNonAlwaysOnTopStack();
if (topStack != targetStack) {
ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS,
"Expected target stack=%s"
@@ -347,7 +347,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
taskDisplayArea.moveStackBehindStack(targetStack,
mRestoreTargetBehindStack);
if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) {
- final ActivityStack aboveTargetStack = getStackAbove(targetStack);
+ final Task aboveTargetStack = getStackAbove(targetStack);
if (mRestoreTargetBehindStack != null
&& aboveTargetStack != mRestoreTargetBehindStack) {
ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS,
@@ -411,7 +411,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
}
@Override
- public void onStackOrderChanged(ActivityStack stack) {
+ public void onStackOrderChanged(Task stack) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onStackOrderChanged(): stack=%s", stack);
if (mDefaultTaskDisplayArea.getIndexOf(stack) == -1 || !stack.shouldBeVisible(null)) {
// The stack is not visible, so ignore this change
@@ -466,9 +466,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
/**
* @return The top stack that is not always-on-top.
*/
- private ActivityStack getTopNonAlwaysOnTopStack() {
+ private Task getTopNonAlwaysOnTopStack() {
for (int i = mDefaultTaskDisplayArea.getStackCount() - 1; i >= 0; i--) {
- final ActivityStack s = mDefaultTaskDisplayArea.getStackAt(i);
+ final Task s = mDefaultTaskDisplayArea.getStackAt(i);
if (s.getWindowConfiguration().isAlwaysOnTop()) {
continue;
}
@@ -481,7 +481,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
* @return the top activity in the {@param targetStack} matching the {@param component}, or just
* the top activity of the top task if no task matches the component.
*/
- private ActivityRecord getTargetActivity(ActivityStack targetStack) {
+ private ActivityRecord getTargetActivity(Task targetStack) {
if (targetStack == null) {
return null;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 55bca2ee2791..f5bd4cd866a6 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -362,7 +362,7 @@ public class RecentsAnimationController implements DeathRecipient {
// TODO(b/153090560): Support Recents on multiple task display areas
final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea()
.getVisibleTasks();
- final ActivityStack targetStack = mDisplayContent.getDefaultTaskDisplayArea()
+ final Task targetStack = mDisplayContent.getDefaultTaskDisplayArea()
.getStack(WINDOWING_MODE_UNDEFINED, targetActivityType);
if (targetStack != null) {
final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
@@ -406,7 +406,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
// Save the minimized home height
- final ActivityStack rootHomeTask =
+ final Task rootHomeTask =
mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
mMinimizedHomeBounds = rootHomeTask != null ? rootHomeTask.getBounds() : null;
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index c7f78342c829..c255a18190f7 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -359,17 +359,20 @@ class RemoteAnimationController implements DeathRecipient {
RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
Rect endBounds, Rect startBounds) {
mWindowContainer = windowContainer;
- mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds);
if (startBounds != null) {
mStartBounds = new Rect(startBounds);
+ mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
+ mStartBounds);
mTmpRect.set(startBounds);
mTmpRect.offsetTo(0, 0);
if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
mThumbnailAdapter =
new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,
- mTmpRect);
+ mTmpRect, new Rect());
}
} else {
+ mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
+ new Rect(endPos.x, endPos.y, endBounds.right, endBounds.bottom));
mStartBounds = null;
}
}
@@ -407,13 +410,15 @@ class RemoteAnimationController implements DeathRecipient {
final Point mPosition = new Point();
final Rect mLocalBounds;
final Rect mStackBounds = new Rect();
+ final Rect mStartBounds = new Rect();
RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
- Rect localBounds, Rect stackBounds) {
+ Rect localBounds, Rect stackBounds, Rect startBounds) {
mRecord = record;
mPosition.set(position.x, position.y);
mLocalBounds = localBounds;
mStackBounds.set(stackBounds);
+ mStartBounds.set(startBounds);
}
@Override
@@ -427,13 +432,12 @@ class RemoteAnimationController implements DeathRecipient {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
// Restore position and stack crop until client has a chance to modify it.
- if (mRecord.mStartBounds != null) {
- t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);
- t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),
- mRecord.mStartBounds.height());
+ if (mStartBounds.isEmpty()) {
+ t.setPosition(animationLeash, 0, 0);
+ t.setWindowCrop(animationLeash, -1, -1);
} else {
- t.setPosition(animationLeash, mPosition.x, mPosition.y);
- t.setWindowCrop(animationLeash, mStackBounds.width(), mStackBounds.height());
+ t.setPosition(animationLeash, mStartBounds.left, mStartBounds.top);
+ t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height());
}
mCapturedLeash = animationLeash;
mCapturedFinishCallback = finishCallback;
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 32de699eaae9..cc5ed36e0f47 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
-import static com.android.server.wm.ActivityStack.TAG_ADD_REMOVE;
-import static com.android.server.wm.ActivityStack.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.Task.TAG_ADD_REMOVE;
+import static com.android.server.wm.Task.TAG_TASKS;
import android.app.ActivityOptions;
import android.content.Intent;
@@ -37,7 +37,7 @@ import java.util.ArrayList;
class ResetTargetTaskHelper {
private Task mTask;
private Task mTargetTask;
- private ActivityStack mTargetStack;
+ private Task mTargetStack;
private ActivityRecord mRoot;
private boolean mForceReset;
private boolean mCanMoveOptions;
@@ -61,7 +61,7 @@ class ResetTargetTaskHelper {
mForceReset = forceReset;
mTargetTask = targetTask;
mTargetTaskFound = false;
- mTargetStack = targetTask.getStack();
+ mTargetStack = targetTask.getRootTask();
mActivityReparentPosition = -1;
final PooledConsumer c = PooledLambda.obtainConsumer(
diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java
new file mode 100644
index 000000000000..faed7fa99fdd
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RootDisplayArea.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+
+import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Root of a {@link DisplayArea} hierarchy. It can be either the {@link DisplayContent} as the root
+ * of the whole logical display, or the root of a {@link DisplayArea} group.
+ */
+class RootDisplayArea extends DisplayArea<DisplayArea> {
+
+ /** {@link Feature} that are supported in this {@link DisplayArea} hierarchy. */
+ List<DisplayAreaPolicyBuilder.Feature> mFeatures;
+
+ /**
+ * Mapping from policy supported {@link Feature} to list of {@link DisplayArea} created to cover
+ * all the window types that the {@link Feature} will be applied to.
+ */
+ Map<Feature, List<DisplayArea<? extends WindowContainer>>> mFeatureToDisplayAreas;
+
+ /** Mapping from window layer to {@link DisplayArea.Tokens} that holds windows on that layer. */
+ private DisplayArea.Tokens[] mAreaForLayer;
+
+ /** Whether the hierarchy has been built. */
+ private boolean mHasBuiltHierarchy;
+
+ RootDisplayArea(WindowManagerService wms, String name, int featureId) {
+ super(wms, Type.ANY, name, featureId);
+ }
+
+ /** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */
+ DisplayArea.Tokens findAreaForToken(WindowToken token) {
+ int windowLayerFromType = token.getWindowLayerFromType();
+ if (windowLayerFromType == APPLICATION_LAYER) {
+ throw new IllegalArgumentException(
+ "There shouldn't be WindowToken on APPLICATION_LAYER");
+ } else if (token.mRoundedCornerOverlay) {
+ windowLayerFromType = mAreaForLayer.length - 1;
+ }
+ return mAreaForLayer[windowLayerFromType];
+ }
+
+ /** Callback after {@link DisplayArea} hierarchy has been built. */
+ void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer,
+ Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureToDisplayAreas) {
+ if (mHasBuiltHierarchy) {
+ throw new IllegalStateException("Root should only build the hierarchy once");
+ }
+ mHasBuiltHierarchy = true;
+ mFeatures = Collections.unmodifiableList(features);
+ mAreaForLayer = areaForLayer;
+ mFeatureToDisplayAreas = featureToDisplayAreas;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index c48ab98fc301..06dec7c8023d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -39,15 +39,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
@@ -73,6 +69,11 @@ import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COM
import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
import static com.android.server.wm.RootWindowContainerProto.PENDING_ACTIVITIES;
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
+import static com.android.server.wm.Task.ActivityState.FINISHING;
+import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
@@ -219,6 +220,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// transaction from the global transaction.
private final SurfaceControl.Transaction mDisplayTransaction;
+ /** The token acquirer to put stacks on the displays to sleep */
+ final ActivityTaskManagerInternal.SleepTokenAcquirer mDisplayOffTokenAcquirer;
+
/**
* The modes which affect which tasks are returned when calling
* {@link RootWindowContainer#anyTaskForId(int)}.
@@ -258,7 +262,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* They are used by components that may hide and block interaction with underlying
* activities.
*/
- final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>();
+ final SparseArray<SleepToken> mSleepTokens = new SparseArray<>();
/** Set when a power mode launch has started, but not ended. */
private boolean mPowerModeLaunchStarted;
@@ -313,7 +317,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* Returns the top activity in any existing task matching the given Intent in the input
* result. Returns null if no such task is found.
*/
- void process(ActivityRecord target, ActivityStack parent) {
+ void process(ActivityRecord target, Task parent) {
mTarget = target;
intent = target.intent;
@@ -443,6 +447,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mService = service.mAtmService;
mStackSupervisor = mService.mStackSupervisor;
mStackSupervisor.mRootWindowContainer = this;
+ mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl("Display-off");
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -990,9 +995,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
// Remove all deferred displays stacks, tasks, and activities.
- for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
- mChildren.get(displayNdx).checkCompleteDeferredRemoval();
- }
+ handleCompleteDeferredRemoval();
forAllDisplays(dc -> {
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
@@ -1108,9 +1111,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean displayHasContent = false;
ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON,
- "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w"
- + ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d",
- w, w.mHasSurface, onScreen, w.isDisplayedLw(), w.mAttrs.userActivityTimeout);
+ "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w"
+ + ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d",
+ w, w.mHasSurface, onScreen, w.isDisplayedLw(), w.mAttrs.userActivityTimeout);
if (w.mHasSurface && onScreen) {
if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
mUserActivityTimeout = w.mAttrs.userActivityTimeout;
@@ -1459,16 +1462,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void startHomeOnEmptyDisplays(String reason) {
- for (int i = getChildCount() - 1; i >= 0; i--) {
- final DisplayContent display = getChildAt(i);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- if (taskDisplayArea.topRunningActivity() == null) {
- startHomeOnTaskDisplayArea(mCurrentUser, reason, taskDisplayArea,
- false /* allowInstrumenting */, false /* fromHomeKey */);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ if (taskDisplayArea.topRunningActivity() == null) {
+ startHomeOnTaskDisplayArea(mCurrentUser, reason, taskDisplayArea,
+ false /* allowInstrumenting */, false /* fromHomeKey */);
}
- }
+ });
}
boolean startHomeOnDisplay(int userId, String reason, int displayId) {
@@ -1480,18 +1479,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean fromHomeKey) {
// Fallback to top focused display or default display if the displayId is invalid.
if (displayId == INVALID_DISPLAY) {
- final ActivityStack stack = getTopDisplayFocusedStack();
+ final Task stack = getTopDisplayFocusedStack();
displayId = stack != null ? stack.getDisplayId() : DEFAULT_DISPLAY;
}
final DisplayContent display = getDisplayContent(displayId);
- boolean result = false;
- for (int tcNdx = display.getTaskDisplayAreaCount() - 1; tcNdx >= 0; --tcNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tcNdx);
- result |= startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
- allowInstrumenting, fromHomeKey);
- }
- return result;
+ return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
+ result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
+ allowInstrumenting, fromHomeKey),
+ false /* initValue */);
}
/**
@@ -1509,7 +1505,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean allowInstrumenting, boolean fromHomeKey) {
// Fallback to top focused display area if the provided one is invalid.
if (taskDisplayArea == null) {
- final ActivityStack stack = getTopDisplayFocusedStack();
+ final Task stack = getTopDisplayFocusedStack();
taskDisplayArea = stack != null ? stack.getDisplayArea()
: getDefaultTaskDisplayArea();
}
@@ -1826,36 +1822,32 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
*/
List<IBinder> getTopVisibleActivities() {
final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
- final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ final Task topFocusedStack = getTopDisplayFocusedStack();
// Traverse all displays.
- for (int dNdx = getChildCount() - 1; dNdx >= 0; dNdx--) {
- final DisplayContent display = getChildAt(dNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea =
- display.getTaskDisplayAreaAt(tdaNdx);
- // Traverse all stacks on a display area.
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- // Get top activity from a visible stack and add it to the list.
- if (stack.shouldBeVisible(null /* starting */)) {
- final ActivityRecord top = stack.getTopNonFinishingActivity();
- if (top != null) {
- if (stack == topFocusedStack) {
- topActivityTokens.add(0, top.appToken);
- } else {
- topActivityTokens.add(top.appToken);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ // Traverse all stacks on a display area.
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ // Get top activity from a visible stack and add it to the list.
+ if (stack.shouldBeVisible(null /* starting */)) {
+ final ActivityRecord top = stack.getTopNonFinishingActivity();
+ if (top != null) {
+ if (stack == topFocusedStack) {
+ topActivityTokens.add(0, top.appToken);
+ } else {
+ topActivityTokens.add(top.appToken);
}
}
}
}
- }
+ });
return topActivityTokens;
}
- ActivityStack getTopDisplayFocusedStack() {
+ @Nullable
+ Task getTopDisplayFocusedStack() {
for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack focusedStack = getChildAt(i).getFocusedStack();
+ final Task focusedStack = getChildAt(i).getFocusedStack();
if (focusedStack != null) {
return focusedStack;
}
@@ -1863,8 +1855,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return null;
}
+ @Nullable
ActivityRecord getTopResumedActivity() {
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ final Task focusedStack = getTopDisplayFocusedStack();
if (focusedStack == null) {
return null;
}
@@ -1874,21 +1867,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
// The top focused stack might not have a resumed activity yet - look on all displays in
// focus order.
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final DisplayContent display = getChildAt(i);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- final ActivityRecord resumedActivityOnTaskContainer = taskDisplayArea
- .getFocusedActivity();
- if (resumedActivityOnTaskContainer != null) {
- return resumedActivityOnTaskContainer;
- }
- }
- }
- return null;
+ return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity);
}
- boolean isTopDisplayFocusedStack(ActivityStack stack) {
+ boolean isTopDisplayFocusedStack(Task stack) {
return stack != null && stack == getTopDisplayFocusedStack();
}
@@ -1899,25 +1881,21 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// First, found out what is currently the foreground app, so that we don't blow away the
// previous app if this activity is being hosted by the process that is actually still the
// foreground.
- WindowProcessController fgApp = null;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (isTopDisplayFocusedStack(stack)) {
- final ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity != null) {
- fgApp = resumedActivity.app;
- } else if (stack.mPausingActivity != null) {
- fgApp = stack.mPausingActivity.app;
- }
- break;
+ WindowProcessController fgApp = reduceOnAllTaskDisplayAreas((taskDisplayArea, app) -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ if (isTopDisplayFocusedStack(stack)) {
+ final ActivityRecord resumedActivity = stack.getResumedActivity();
+ if (resumedActivity != null) {
+ app = resumedActivity.app;
+ } else if (stack.mPausingActivity != null) {
+ app = stack.mPausingActivity.app;
}
+ break;
}
}
- }
+ return app;
+ }, null /* initValue */);
// Now set this one as the previous process, only if that really makes sense to.
if (r.hasProcess() && fgApp != null && r.app != fgApp
@@ -1933,7 +1911,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean didSomething = false;
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
- final ActivityStack stack = display.getFocusedStack();
+ final Task stack = display.getFocusedStack();
if (stack == null) {
continue;
}
@@ -2010,7 +1988,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
boolean switchUser(int userId, UserState uss) {
- final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ final Task topFocusedStack = getTopDisplayFocusedStack();
final int focusStackId = topFocusedStack != null
? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
// We dismiss the docked stack whenever we switch users.
@@ -2026,19 +2004,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mCurrentUser = userId;
mStackSupervisor.mStartingUsers.add(uss);
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- stack.switchUser(userId);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ stack.switchUser(userId);
}
- }
+ });
final int restoreStackId = mUserStackInFront.get(userId);
- ActivityStack stack = getStack(restoreStackId);
+ Task stack = getStack(restoreStackId);
if (stack == null) {
stack = getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
}
@@ -2060,7 +2034,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* Update the last used stack id for non-current user (current user's last
* used stack is the focused stack)
*/
- void updateUserStack(int userId, ActivityStack stack) {
+ void updateUserStack(int userId, Task stack) {
if (userId != mCurrentUser) {
if (stack == null) {
stack = getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
@@ -2077,7 +2051,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* @param onTop Indicates whether container should be place on top or on bottom.
*/
void moveStackToTaskDisplayArea(int stackId, TaskDisplayArea taskDisplayArea, boolean onTop) {
- final ActivityStack stack = getStack(stackId);
+ final Task stack = getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException("moveStackToTaskDisplayArea: Unknown stackId="
+ stackId);
@@ -2126,7 +2100,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
boolean moveTopStackActivityToPinnedStack(int stackId) {
- final ActivityStack stack = getStack(stackId);
+ final Task stack = getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException(
"moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId);
@@ -2156,7 +2130,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
try {
final Task task = r.getTask();
- final ActivityStack pinnedStack = taskDisplayArea.getRootPinnedTask();
+ final Task pinnedStack = taskDisplayArea.getRootPinnedTask();
// This will change the pinned stack's windowing mode to its original mode, ensuring
// we only have one stack that is in pinned mode.
@@ -2169,9 +2143,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE, false);
final boolean singleActivity = task.getChildCount() == 1;
- final ActivityStack stack;
+ final Task stack;
if (singleActivity) {
- stack = (ActivityStack) task;
+ stack = task;
} else {
// In the case of multiple activities, we will create a new task for it and then
// move the PIP activity into the task.
@@ -2187,6 +2161,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// On the other hand, ActivityRecord#onParentChanged takes care of setting the
// up-to-dated pinned stack information on this newly created stack.
r.reparent(stack, MAX_VALUE, reason);
+
+ // In the case of this activity entering PIP due to it being moved to the back,
+ // the old activity would have a TRANSIT_TASK_TO_BACK transition that needs to be
+ // ran. But, since its visibility did not change (note how it was STOPPED/not
+ // visible, and with it now at the back stack, it remains not visible), the logic to
+ // add the transition is automatically skipped. We then add this activity manually
+ // to the list of apps being closed, and request its transition to be ran.
+ final ActivityRecord oldTopActivity = task.getTopMostActivity();
+ if (oldTopActivity != null && oldTopActivity.isState(STOPPED)
+ && task.getDisplayContent().mAppTransition.getAppTransition()
+ == TRANSIT_TASK_TO_BACK) {
+ task.getDisplayContent().mClosingApps.add(oldTopActivity);
+ oldTopActivity.mRequestForceTransition = true;
+ }
}
// The intermediate windowing mode to be set on the ActivityRecord later.
// This needs to happen before the re-parenting, otherwise we will always set the
@@ -2223,6 +2211,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
+ @Nullable
ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
mTmpFindTaskResult.clear();
@@ -2236,20 +2225,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- if (taskDisplayArea == preferredTaskDisplayArea) {
- continue;
- }
+ final ActivityRecord task = getItemFromTaskDisplayAreas(taskDisplayArea -> {
+ if (taskDisplayArea == preferredTaskDisplayArea) {
+ return null;
+ }
- taskDisplayArea.findTaskLocked(r, false /* isPreferredDisplay */,
- mTmpFindTaskResult);
- if (mTmpFindTaskResult.mIdealMatch) {
- return mTmpFindTaskResult.mRecord;
- }
+ taskDisplayArea.findTaskLocked(r, false /* isPreferredDisplay */,
+ mTmpFindTaskResult);
+ if (mTmpFindTaskResult.mIdealMatch) {
+ return mTmpFindTaskResult.mRecord;
}
+ return null;
+ });
+ if (task != null) {
+ return task;
}
if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
@@ -2263,24 +2252,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
*/
int finishTopCrashedActivities(WindowProcessController app, String reason) {
- Task finishedTask = null;
- ActivityStack focusedStack = getTopDisplayFocusedStack();
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- // It is possible that request to finish activity might also remove its task and
- // stack, so we need to be careful with indexes in the loop and check child count
- // every time.
- for (int stackNdx = 0; stackNdx < taskDisplayArea.getStackCount(); ++stackNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx);
- final Task t = stack.finishTopCrashedActivityLocked(app, reason);
- if (stack == focusedStack || finishedTask == null) {
- finishedTask = t;
- }
+ Task focusedStack = getTopDisplayFocusedStack();
+ Task finishedTask = reduceOnAllTaskDisplayAreas((taskDisplayArea, task) -> {
+ // It is possible that request to finish activity might also remove its task and
+ // stack, so we need to be careful with indexes in the loop and check child count
+ // every time.
+ for (int stackNdx = 0; stackNdx < taskDisplayArea.getStackCount(); ++stackNdx) {
+ final Task stack = taskDisplayArea.getStackAt(stackNdx);
+ final Task t = stack.finishTopCrashedActivityLocked(app, reason);
+ if (stack == focusedStack || task == null) {
+ task = t;
}
}
- }
+ return task;
+ }, null /* initValue */);
return finishedTask != null ? finishedTask.mTaskId : INVALID_TASK_ID;
}
@@ -2289,7 +2274,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
boolean resumeFocusedStacksTopActivities(
- ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
+ Task targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if (!mStackSupervisor.readyToResume()) {
return false;
@@ -2302,39 +2287,46 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- boolean resumedOnDisplay = false;
final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- final ActivityRecord topRunningActivity = stack.topRunningActivity();
- if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
- continue;
- }
- if (stack == targetStack) {
- // Simply update the result for targetStack because the targetStack had
- // already resumed in above. We don't want to resume it again, especially in
- // some cases, it would cause a second launch failure if app process was
- // dead.
- resumedOnDisplay |= result;
- continue;
- }
- if (taskDisplayArea.isTopStack(stack) && topRunningActivity.isState(RESUMED)) {
- // Kick off any lingering app transitions form the MoveTaskToFront
- // operation, but only consider the top task and stack on that display.
- stack.executeAppTransition(targetOptions);
- } else {
- resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
- }
- }
+ if (display.shouldSleep()) {
+ continue;
}
+
+ final boolean curResult = result;
+ boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas(
+ (taskDisplayArea, resumed) -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ final ActivityRecord topRunningActivity = stack.topRunningActivity();
+ if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
+ continue;
+ }
+ if (stack == targetStack) {
+ // Simply update the result for targetStack because the targetStack
+ // had already resumed in above. We don't want to resume it again,
+ // especially in some cases, it would cause a second launch failure
+ // if app process was dead.
+ resumed |= curResult;
+ continue;
+ }
+ if (taskDisplayArea.isTopStack(stack)
+ && topRunningActivity.isState(RESUMED)) {
+ // Kick off any lingering app transitions form the MoveTaskToFront
+ // operation, but only consider the top task and stack on that
+ // display.
+ stack.executeAppTransition(targetOptions);
+ } else {
+ resumed |= topRunningActivity.makeActiveIfNeeded(target);
+ }
+ }
+ return resumed;
+ }, false /* initValue */);
if (!resumedOnDisplay) {
// In cases when there are no valid activities (e.g. device just booted or launcher
// crashed) it's possible that nothing was resumed on a display. Requesting resume
// of top activity in focused stack explicitly will make sure that at least home
// activity is started and resumed, and no recursion occurs.
- final ActivityStack focusedStack = display.getFocusedStack();
+ final Task focusedStack = display.getFocusedStack();
if (focusedStack != null) {
result |= focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
} else if (targetStack == null) {
@@ -2362,10 +2354,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
// Set the sleeping state of the stacks on the display.
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ display.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
if (displayShouldSleep) {
stack.goToSleepIfPossible(false /* shuttingDown */);
} else {
@@ -2377,7 +2368,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// triggered after contents are drawn on the display.
if (display.isSingleTaskInstance()) {
display.mDisplayContent.prepareAppTransition(
- TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
+ TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false,
+ 0 /* flags */, true /* forceOverride*/);
}
stack.awakeFromSleepingLocked();
if (display.isSingleTaskInstance()) {
@@ -2391,17 +2383,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// process the keyguard going away, which can happen before the sleep
// token is released. As a result, it is important we resume the
// activity here.
- resumeFocusedStacksTopActivities();
+ stack.resumeTopActivityUncheckedLocked(null, null);
}
+ // The visibility update must not be called before resuming the top, so the
+ // display orientation can be updated first if needed. Otherwise there may
+ // have redundant configuration changes due to apply outdated display
+ // orientation (from keyguard) to activity.
+ stack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */);
}
}
- }
+ });
}
}
- protected ActivityStack getStack(int stackId) {
+ protected Task getStack(int stackId) {
for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i).getStack(stackId);
+ final Task stack = getChildAt(i).getStack(stackId);
if (stack != null) {
return stack;
}
@@ -2410,9 +2408,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
/** @see DisplayContent#getStack(int, int) */
- ActivityStack getStack(int windowingMode, int activityType) {
+ Task getStack(int windowingMode, int activityType) {
for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i).getStack(windowingMode, activityType);
+ final Task stack = getChildAt(i).getStack(windowingMode, activityType);
if (stack != null) {
return stack;
}
@@ -2420,7 +2418,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return null;
}
- private ActivityStack getStack(int windowingMode, int activityType,
+ private Task getStack(int windowingMode, int activityType,
int displayId) {
DisplayContent display = getDisplayContent(displayId);
if (display == null) {
@@ -2429,7 +2427,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return display.getStack(windowingMode, activityType);
}
- private ActivityManager.StackInfo getStackInfo(ActivityStack stack) {
+ private ActivityManager.StackInfo getStackInfo(Task stack) {
final TaskDisplayArea taskDisplayArea = stack.getDisplayArea();
ActivityManager.StackInfo info = new ActivityManager.StackInfo();
stack.getBounds(info.bounds);
@@ -2475,7 +2473,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
ActivityManager.StackInfo getStackInfo(int stackId) {
- ActivityStack stack = getStack(stackId);
+ Task stack = getStack(stackId);
if (stack != null) {
return getStackInfo(stack);
}
@@ -2483,12 +2481,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
- final ActivityStack stack = getStack(windowingMode, activityType);
+ final Task stack = getStack(windowingMode, activityType);
return (stack != null) ? getStackInfo(stack) : null;
}
ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType, int displayId) {
- final ActivityStack stack = getStack(windowingMode, activityType, displayId);
+ final Task stack = getStack(windowingMode, activityType, displayId);
return (stack != null) ? getStackInfo(stack) : null;
}
@@ -2496,29 +2494,24 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
ArrayList<ActivityManager.StackInfo> getAllStackInfos(int displayId) {
ArrayList<ActivityManager.StackInfo> list = new ArrayList<>();
if (displayId == INVALID_DISPLAY) {
- for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- list.add(getStackInfo(stack));
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ list.add(getStackInfo(stack));
}
- }
+ });
return list;
}
final DisplayContent display = getDisplayContent(displayId);
if (display == null) {
return list;
}
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ display.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
list.add(getStackInfo(stack));
}
- }
+ });
return list;
}
@@ -2587,7 +2580,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
}
- ActivityStack findStackBehind(ActivityStack stack) {
+ Task findStackBehind(Task stack) {
final TaskDisplayArea taskDisplayArea = stack.getDisplayArea();
if (taskDisplayArea != null) {
for (int i = taskDisplayArea.getStackCount() - 1; i >= 0; i--) {
@@ -2630,20 +2623,29 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- ActivityTaskManagerInternal.SleepToken createSleepToken(String tag, int displayId) {
+ SleepToken createSleepToken(String tag, int displayId) {
final DisplayContent display = getDisplayContent(displayId);
if (display == null) {
throw new IllegalArgumentException("Invalid display: " + displayId);
}
- final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
- mSleepTokens.add(token);
- display.mAllSleepTokens.add(token);
+ final int tokenKey = makeSleepTokenKey(tag, displayId);
+ SleepToken token = mSleepTokens.get(tokenKey);
+ if (token == null) {
+ token = new SleepToken(tag, displayId);
+ mSleepTokens.put(tokenKey, token);
+ display.mAllSleepTokens.add(token);
+ } else {
+ throw new RuntimeException("Create the same sleep token twice: " + token);
+ }
return token;
}
- private void removeSleepToken(SleepTokenImpl token) {
- mSleepTokens.remove(token);
+ void removeSleepToken(SleepToken token) {
+ if (!mSleepTokens.contains(token.mHashKey)) {
+ Slog.d(TAG, "Remove non-exist sleep token: " + token + " from " + Debug.getCallers(6));
+ }
+ mSleepTokens.remove(token.mHashKey);
final DisplayContent display = getDisplayContent(token.mDisplayId);
if (display != null) {
@@ -2719,28 +2721,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Tries to put all activity stacks to sleep. Returns true if all stacks were
// successfully put to sleep.
boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
- boolean allSleep = true;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- // Stacks and activities could be removed while putting activities to sleep if
- // the app process was gone. This prevents us getting exception by accessing an
- // invalid stack index.
- if (sNdx >= taskDisplayArea.getStackCount()) {
- continue;
- }
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (allowDelay) {
- allSleep &= stack.goToSleepIfPossible(shuttingDown);
- } else {
- stack.goToSleep();
- }
+ return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ // Stacks and activities could be removed while putting activities to sleep if
+ // the app process was gone. This prevents us getting exception by accessing an
+ // invalid stack index.
+ if (sNdx >= taskDisplayArea.getStackCount()) {
+ continue;
+ }
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ if (allowDelay) {
+ result &= stack.goToSleepIfPossible(shuttingDown);
+ } else {
+ stack.goToSleep();
}
}
- }
- return allSleep;
+ return result;
+ }, true /* initValue */);
}
void handleAppCrash(WindowProcessController app) {
@@ -2803,7 +2800,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return false;
}
- ActivityStack getLaunchStack(@Nullable ActivityRecord r,
+ Task getLaunchStack(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop) {
return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */,
-1 /* no realCallingPid */, -1 /* no realCallingUid */);
@@ -2821,7 +2818,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
*
* @return The stack to use for the launch or INVALID_STACK_ID.
*/
- ActivityStack getLaunchStack(@Nullable ActivityRecord r,
+ Task getLaunchStack(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop,
@Nullable LaunchParamsController.LaunchParams launchParams, int realCallingPid,
int realCallingUid) {
@@ -2847,12 +2844,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
options.setLaunchTaskId(taskId);
if (task != null) {
- return task.getStack();
+ return task.getRootTask();
}
}
final int activityType = resolveActivityType(r, options, candidateTask);
- ActivityStack stack = null;
+ Task stack = null;
// Next preference for stack goes to the taskDisplayArea candidate.
if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null) {
@@ -2874,7 +2871,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
realCallingPid, realCallingUid, r.info);
if (canLaunchOnDisplayFromStartRequest || canLaunchOnDisplay(r, tdaDisplayId)) {
if (r != null) {
- final ActivityStack result = getValidLaunchStackInTaskDisplayArea(
+ final Task result = getValidLaunchStackInTaskDisplayArea(
taskDisplayArea, r, candidateTask, options, launchParams);
if (result != null) {
return result;
@@ -2894,7 +2891,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// mode we want to launch into.
TaskDisplayArea container = null;
if (candidateTask != null) {
- stack = candidateTask.getStack();
+ stack = candidateTask.getRootTask();
}
if (stack == null && r != null) {
stack = r.getRootTask();
@@ -2956,7 +2953,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
*/
@VisibleForTesting
- ActivityStack getValidLaunchStackInTaskDisplayArea(@NonNull TaskDisplayArea taskDisplayArea,
+ Task getValidLaunchStackInTaskDisplayArea(@NonNull TaskDisplayArea taskDisplayArea,
@NonNull ActivityRecord r, @Nullable Task candidateTask,
@Nullable ActivityOptions options,
@Nullable LaunchParamsController.LaunchParams launchParams) {
@@ -2972,12 +2969,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final TaskDisplayArea attachedTaskDisplayArea = r.getTask() != null
? r.getTask().getDisplayArea() : r.getDisplayArea();
if (attachedTaskDisplayArea == null || attachedTaskDisplayArea == taskDisplayArea) {
- return candidateTask.getStack();
+ return candidateTask.getRootTask();
}
// Or the candidate task is already a root task that can be reused by reparenting
// it to the target display.
if (candidateTask.isRootTask()) {
- final ActivityStack stack = candidateTask.getStack();
+ final Task stack = candidateTask.getRootTask();
stack.reparent(taskDisplayArea, true /* onTop */);
return stack;
}
@@ -2998,7 +2995,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Return the topmost valid stack on the display.
for (int i = taskDisplayArea.getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = taskDisplayArea.getStackAt(i);
+ final Task stack = taskDisplayArea.getStackAt(i);
if (isValidLaunchStack(stack, r, windowingMode)) {
return stack;
}
@@ -3018,7 +3015,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
// TODO: Can probably be consolidated into getLaunchStack()...
- private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r, int windowingMode) {
+ private boolean isValidLaunchStack(Task stack, ActivityRecord r, int windowingMode) {
switch (stack.getActivityType()) {
case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
@@ -3066,9 +3063,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* @param currentFocus The stack that previously had focus.
* @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
* candidate.
- * @return Next focusable {@link ActivityStack}, {@code null} if not found.
+ * @return Next focusable {@link Task}, {@code null} if not found.
*/
- ActivityStack getNextFocusableStack(@NonNull ActivityStack currentFocus,
+ Task getNextFocusableStack(@NonNull Task currentFocus,
boolean ignoreCurrent) {
// First look for next focusable stack on the same display
TaskDisplayArea preferredDisplayArea = currentFocus.getDisplayArea();
@@ -3078,7 +3075,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
preferredDisplayArea = getDisplayContent(currentFocus.mPrevDisplayId)
.getDefaultTaskDisplayArea();
}
- final ActivityStack preferredFocusableStack = preferredDisplayArea.getNextFocusableStack(
+ final Task preferredFocusableStack = preferredDisplayArea.getNextFocusableStack(
currentFocus, ignoreCurrent);
if (preferredFocusableStack != null) {
return preferredFocusableStack;
@@ -3097,7 +3094,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// We've already checked this one
continue;
}
- final ActivityStack nextFocusableStack = display.getDefaultTaskDisplayArea()
+ final Task nextFocusableStack = display.getDefaultTaskDisplayArea()
.getNextFocusableStack(currentFocus, ignoreCurrent);
if (nextFocusableStack != null) {
return nextFocusableStack;
@@ -3108,18 +3105,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
boolean handleAppDied(WindowProcessController app) {
- boolean hasVisibleActivities = false;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- hasVisibleActivities |= stack.handleAppDied(app);
- }
- }
+ if (app.isRemoved()) {
+ // The package of the died process should be force-stopped, so make its activities as
+ // finishing to prevent the process from being started again if the next top (or being
+ // visible) activity also resides in the same process.
+ app.makeFinishingForProcessRemoved();
}
- return hasVisibleActivities;
+ return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ result |= stack.handleAppDied(app);
+ }
+ return result;
+ }, false /* initValue */);
}
void closeSystemDialogActivities(String reason) {
@@ -3150,24 +3148,26 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
private boolean mDoit;
private boolean mEvenPersistent;
private int mUserId;
+ private boolean mOnlyRemoveNoProcess;
private Task mLastTask;
private ComponentName mHomeActivity;
private void reset(String packageName, Set<String> filterByClasses,
- boolean doit, boolean evenPersistent, int userId) {
+ boolean doit, boolean evenPersistent, int userId, boolean onlyRemoveNoProcess) {
mDidSomething = false;
mPackageName = packageName;
mFilterByClasses = filterByClasses;
mDoit = doit;
mEvenPersistent = evenPersistent;
mUserId = userId;
+ mOnlyRemoveNoProcess = onlyRemoveNoProcess;
mLastTask = null;
mHomeActivity = null;
}
boolean process(String packageName, Set<String> filterByClasses,
- boolean doit, boolean evenPersistent, int userId) {
- reset(packageName, filterByClasses, doit, evenPersistent, userId);
+ boolean doit, boolean evenPersistent, int userId, boolean onlyRemoveNoProcess) {
+ reset(packageName, filterByClasses, doit, evenPersistent, userId, onlyRemoveNoProcess);
final PooledFunction f = PooledLambda.obtainFunction(
FinishDisabledPackageActivitiesHelper::processActivity, this,
@@ -3182,9 +3182,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
(r.packageName.equals(mPackageName) && (mFilterByClasses == null
|| mFilterByClasses.contains(r.mActivityComponent.getClassName())))
|| (mPackageName == null && r.mUserId == mUserId);
+ final boolean noProcess = !r.hasProcess();
if ((mUserId == UserHandle.USER_ALL || r.mUserId == mUserId)
&& (sameComponent || r.getTask() == mLastTask)
- && (r.app == null || mEvenPersistent || !r.app.isPersistent())) {
+ && (noProcess || mEvenPersistent || !r.app.isPersistent())) {
if (!mDoit) {
if (r.finishing) {
// If this activity is just finishing, then it is not
@@ -3201,10 +3202,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mHomeActivity = r.mActivityComponent;
}
}
- mDidSomething = true;
- Slog.i(TAG, " Force finishing activity " + r);
+ if (mOnlyRemoveNoProcess) {
+ if (noProcess) {
+ mDidSomething = true;
+ Slog.i(TAG, " Force removing " + r);
+ r.cleanUp(false /* cleanServices */, false /* setState */);
+ r.removeFromHistory("force-stop");
+ }
+ } else {
+ mDidSomething = true;
+ Slog.i(TAG, " Force finishing " + r);
+ r.finishIfPossible("force-stop", true /* oomAdj */);
+ }
mLastTask = r.getTask();
- r.finishIfPossible("force-stop", true);
}
return false;
@@ -3213,9 +3223,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
/** @return true if some activity was finished (or would have finished if doit were true). */
boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses,
- boolean doit, boolean evenPersistent, int userId) {
+ boolean doit, boolean evenPersistent, int userId, boolean onlyRemoveNoProcess) {
return mFinishDisabledPackageActivitiesHelper.process(packageName, filterByClasses, doit,
- evenPersistent, userId);
+ evenPersistent, userId, onlyRemoveNoProcess);
}
void updateActivityApplicationInfo(ApplicationInfo aInfo) {
@@ -3236,18 +3246,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void finishVoiceTask(IVoiceInteractionSession session) {
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- int numTaskContainers = display.getTaskDisplayAreaCount();
- for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- final int numStacks = display.getStackCount();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx);
- stack.finishVoiceTask(session);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ final int numStacks = taskDisplayArea.getStackCount();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final Task stack = taskDisplayArea.getStackAt(stackNdx);
+ stack.finishVoiceTask(session);
}
- }
+ });
}
/**
@@ -3287,7 +3292,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// If the focused stack is not null or not empty, there should have some activities
// resuming or resumed. Make sure these activities are idle.
- final ActivityStack stack = display.getFocusedStack();
+ final Task stack = display.getFocusedStack();
if (stack == null || !stack.hasActivity()) {
continue;
}
@@ -3306,48 +3311,50 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
boolean allResumedActivitiesVisible() {
- boolean foundResumed = false;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- final ActivityRecord r = stack.getResumedActivity();
- if (r != null) {
- if (!r.nowVisible) {
- return false;
+ boolean[] foundResumed = {false};
+ final boolean foundInvisibleResumedActivity = forAllTaskDisplayAreas(
+ taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ final ActivityRecord r = stack.getResumedActivity();
+ if (r != null) {
+ if (!r.nowVisible) {
+ return true;
+ }
+ foundResumed[0] = true;
}
- foundResumed = true;
}
- }
- }
+ return false;
+ });
+ if (foundInvisibleResumedActivity) {
+ return false;
}
- return foundResumed;
+ return foundResumed[0];
}
boolean allPausedActivitiesComplete() {
- boolean pausing = true;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- final ActivityRecord r = stack.mPausingActivity;
- if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
- if (DEBUG_STATES) {
- Slog.d(TAG_STATES, "allPausedActivitiesComplete: r=" + r
- + " state=" + r.getState());
- pausing = false;
- } else {
- return false;
+ boolean[] pausing = {true};
+ final boolean hasActivityNotCompleted = forAllTaskDisplayAreas(
+ taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ final ActivityRecord r = stack.mPausingActivity;
+ if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES, "allPausedActivitiesComplete: r=" + r
+ + " state=" + r.getState());
+ pausing[0] = false;
+ } else {
+ return true;
+ }
}
}
- }
- }
+ return false;
+ });
+ if (hasActivityNotCompleted) {
+ return false;
}
- return pausing;
+ return pausing[0];
}
/**
@@ -3377,8 +3384,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* <li>The top activity explicitly belongs to {@param userId}.</li>
* <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
* </ul>
- *
- * @return {@code true} if the top activity looks like it belongs to {@param userId}.
*/
private void taskTopActivityIsUser(Task task, @UserIdInt int userId) {
// To handle the case that work app is in the task but just is not the top one.
@@ -3396,15 +3401,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void cancelInitializingActivities() {
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- taskDisplayArea.getStackAt(sNdx).cancelInitializingActivities();
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ // We don't want to clear starting window for activities that aren't occluded
+ // as we need to display their starting window until they are done initializing.
+ taskDisplayArea.getStackAt(sNdx).forAllOccludedActivities(
+ ActivityRecord::cancelInitializing);
}
- }
+ });
}
Task anyTaskForId(int id) {
@@ -3439,9 +3443,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (aOptions != null) {
// Resolve the stack the task should be placed in now based on options
// and reparent if needed.
- final ActivityStack launchStack =
+ final Task launchStack =
getLaunchStack(null, aOptions, task, onTop);
- if (launchStack != null && task.getStack() != launchStack) {
+ if (launchStack != null && task.getRootTask() != launchStack) {
final int reparentMode = onTop
? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
@@ -3507,24 +3511,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
} else {
// Set power mode when the activity's process is different than the current top resumed
// activity on all display areas, or if there are no resumed activities in the system.
- boolean noResumedActivities = true;
- boolean allFocusedProcessesDiffer = true;
- for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
- final DisplayContent dc = getChildAt(displayNdx);
- for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
- final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity();
- final WindowProcessController resumedActivityProcess =
- resumedActivity == null ? null : resumedActivity.app;
-
- noResumedActivities &= resumedActivityProcess == null;
- if (resumedActivityProcess != null) {
- allFocusedProcessesDiffer &= !resumedActivityProcess.equals(
- targetActivity.app);
- }
+ boolean[] noResumedActivities = {true};
+ boolean[] allFocusedProcessesDiffer = {true};
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity();
+ final WindowProcessController resumedActivityProcess =
+ resumedActivity == null ? null : resumedActivity.app;
+
+ noResumedActivities[0] &= resumedActivityProcess == null;
+ if (resumedActivityProcess != null) {
+ allFocusedProcessesDiffer[0] &=
+ !resumedActivityProcess.equals(targetActivity.app);
}
- }
- sendPowerModeLaunch = noResumedActivities || allFocusedProcessesDiffer;
+ });
+ sendPowerModeLaunch = noResumedActivities[0] || allFocusedProcessesDiffer[0];
}
if (sendPowerModeLaunch && mService.mPowerManagerInternal != null) {
@@ -3557,7 +3557,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly,
boolean dumpFocusedStackOnly) {
if (dumpFocusedStackOnly) {
- final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ final Task topFocusedStack = getTopDisplayFocusedStack();
if (topFocusedStack != null) {
return topFocusedStack.getDumpActivitiesLocked(name);
} else {
@@ -3565,19 +3565,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
} else {
ArrayList<ActivityRecord> activities = new ArrayList<>();
- int numDisplays = getChildCount();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
- activities.addAll(stack.getDumpActivitiesLocked(name));
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
+ activities.addAll(stack.getDumpActivitiesLocked(name));
}
}
- }
+ });
return activities;
}
}
@@ -3610,64 +3605,61 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
String dumpPackage) {
- boolean printed = false;
- boolean needSep = false;
+ boolean[] printed = {false};
+ boolean[] needSep = {false};
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
DisplayContent displayContent = getChildAt(displayNdx);
- if (printed) {
+ if (printed[0]) {
pw.println();
}
pw.print("Display #"); pw.print(displayContent.mDisplayId);
pw.println(" (activities from top to bottom):");
- for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx);
+ displayContent.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (needSep) {
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
+ if (needSep[0]) {
pw.println();
}
- needSep = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, false);
- printed |= needSep;
+ needSep[0] = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, false);
+ printed[0] |= needSep[0];
}
- }
- for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx);
- printed |= printThisActivity(pw, taskDisplayArea.getFocusedActivity(),
- dumpPackage, needSep, " Resumed: ", () -> {
- pw.println(" Resumed activities in task display areas"
- + " (from top to bottom):");
- });
- }
+ });
+ displayContent.forAllTaskDisplayAreas(taskDisplayArea -> {
+ printed[0] |= printThisActivity(pw, taskDisplayArea.getFocusedActivity(),
+ dumpPackage, needSep[0], " Resumed: ", () ->
+ pw.println(" Resumed activities in task display areas"
+ + " (from top to bottom):"));
+ });
}
- printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ",
+ printed[0] |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ",
"Fin", false, !dumpAll,
false, dumpPackage, true,
- () -> { pw.println(" Activities waiting to finish:"); }, null);
- printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ",
+ () -> pw.println(" Activities waiting to finish:"), null);
+ printed[0] |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ",
"Stop", false, !dumpAll,
false, dumpPackage, true,
- () -> { pw.println(" Activities waiting to stop:"); }, null);
+ () -> pw.println(" Activities waiting to stop:"), null);
- return printed;
+ return printed[0];
}
- private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken {
+ private static int makeSleepTokenKey(String tag, int displayId) {
+ final String tokenKey = tag + displayId;
+ return tokenKey.hashCode();
+ }
+
+ static final class SleepToken {
private final String mTag;
private final long mAcquireTime;
private final int mDisplayId;
+ final int mHashKey;
- public SleepTokenImpl(String tag, int displayId) {
+ SleepToken(String tag, int displayId) {
mTag = tag;
mDisplayId = displayId;
mAcquireTime = SystemClock.uptimeMillis();
- }
-
- @Override
- public void release() {
- synchronized (mService.mGlobalLock) {
- removeSleepToken(this);
- }
+ mHashKey = makeSleepTokenKey(mTag, mDisplayId);
}
@Override
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 3509ba72d058..6cf9432089b4 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -48,7 +48,7 @@ class RunningTasks {
private ArraySet<Integer> mProfileIds;
private boolean mAllowed;
private boolean mFilterOnlyVisibleRecents;
- private ActivityStack mTopDisplayFocusStack;
+ private Task mTopDisplayFocusStack;
private RecentTasks mRecentTasks;
void getTasks(int maxNum, List<RunningTaskInfo> list, boolean filterOnlyVisibleRecents,
@@ -114,7 +114,7 @@ class RunningTasks {
return;
}
- final ActivityStack stack = task.getStack();
+ final Task stack = task.getRootTask();
if (stack == mTopDisplayFocusStack && stack.getTopMostTask() == task) {
// For the focused stack top task, update the last stack active time so that it can be
// used to determine the order of the tasks (it may not be set for newly created tasks)
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index be06ef9a075e..d7b8fb00d05c 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -188,17 +188,20 @@ class ScreenRotationAnimation {
mBackColorSurface = displayContent.makeChildSurface(null)
.setName("BackColorSurface")
.setColorLayer()
+ .setCallsite("ScreenRotationAnimation")
.build();
mScreenshotLayer = displayContent.makeOverlay()
.setName("RotationLayer")
.setBufferSize(mWidth, mHeight)
.setSecure(isSecure)
+ .setCallsite("ScreenRotationAnimation")
.build();
mEnterBlackFrameLayer = displayContent.makeOverlay()
.setName("EnterBlackFrameLayer")
.setContainerLayer()
+ .setCallsite("ScreenRotationAnimation")
.build();
// In case display bounds change, screenshot buffer and surface may mismatch so set a
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 0ae9ca9b882e..759f341c0fe0 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -60,7 +60,10 @@ public class ShellRoot {
mToken = new WindowToken(
dc.mWmService, client.asBinder(), windowType, true, dc, true, false);
mSurfaceControl = mToken.makeChildSurface(null)
- .setContainerLayer().setName("Shell Root Leash " + dc.getDisplayId()).build();
+ .setContainerLayer()
+ .setName("Shell Root Leash " + dc.getDisplayId())
+ .setCallsite("ShellRoot")
+ .build();
mToken.getPendingTransaction().show(mSurfaceControl);
}
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index fa62daaff3fc..39ac16a2f5d3 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -48,6 +48,7 @@ class StrictModeFlash {
.setName("StrictModeFlash")
.setBufferSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
+ .setCallsite("StrictModeFlash")
.build();
// one more than Watermark? arbitrary.
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 0e5d7d910084..33935d61ead2 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -395,7 +395,8 @@ class SurfaceAnimator {
// doesn't work, you will can see the 2/3 button nav bar flicker during seamless
// rotation.
.setHidden(hidden)
- .setEffectLayer();
+ .setEffectLayer()
+ .setCallsite("SurfaceAnimator.createAnimationLeash");
final SurfaceControl leash = builder.build();
t.setWindowCrop(leash, width, height);
t.setPosition(leash, x, y);
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 33970c73011d..5cea786c3367 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -161,6 +161,7 @@ class SurfaceFreezer {
.setBufferSize(width, height)
.setFormat(PixelFormat.TRANSLUCENT)
.setParent(parent)
+ .setCallsite("SurfaceFreezer.Snapshot")
.build();
ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bce371b3d71c..821bd7071fb4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
+import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -28,6 +29,7 @@ import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -37,7 +39,10 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
@@ -55,36 +60,91 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
+import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.Task.ActivityState.PAUSING;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.Task.ActivityState.STARTED;
+import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.Task.ActivityState.STOPPING;
+import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
+import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
+import static com.android.server.wm.TaskProto.BOUNDS;
+import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
+import static com.android.server.wm.TaskProto.DISPLAY_ID;
+import static com.android.server.wm.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
+import static com.android.server.wm.TaskProto.MIN_HEIGHT;
+import static com.android.server.wm.TaskProto.MIN_WIDTH;
+import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
+import static com.android.server.wm.TaskProto.REAL_ACTIVITY;
+import static com.android.server.wm.TaskProto.RESIZE_MODE;
+import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY;
+import static com.android.server.wm.TaskProto.ROOT_TASK_ID;
+import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
+import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
+import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
@@ -103,11 +163,20 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityManager.TaskSnapshot;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppGlobals;
+import android.app.IActivityController;
+import android.app.RemoteAction;
+import android.app.ResultInfo;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -117,8 +186,12 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Debug;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -127,8 +200,10 @@ import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import android.view.DisplayInfo;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
@@ -137,36 +212,54 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.ITaskOrganizer;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
+import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.AppTimeTracker;
import com.android.server.protolog.common.ProtoLog;
-import com.android.server.wm.ActivityStack.ActivityState;
+import com.android.server.uri.NeededUriGrants;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
class Task extends WindowContainer<WindowContainer> {
private static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_ATM;
- private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
+ static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
- private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+ static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+ private static final String TAG_APP = TAG + POSTFIX_APP;
+ static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
+ private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
+ private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+ private static final String TAG_STACK = TAG + POSTFIX_STACK;
+ private static final String TAG_STATES = TAG + POSTFIX_STATES;
+ private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+ private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
+ private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
+ static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
private static final String ATTR_TASKID = "task_id";
private static final String TAG_INTENT = "intent";
@@ -202,6 +295,14 @@ class Task extends WindowContainer<WindowContainer> {
private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
+ // Set to false to disable the preview that is shown while a new activity
+ // is being started.
+ private static final boolean SHOW_APP_STARTING_PREVIEW = true;
+
+ // How long to wait for all background Activities to redraw following a call to
+ // convertToTranslucent().
+ private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
+
// Current version of the task record we persist. Used to check if we need to run any upgrade
// code.
static final int PERSIST_TASK_VERSION = 1;
@@ -226,6 +327,58 @@ class Task extends WindowContainer<WindowContainer> {
// Do not move the stack as a part of reparenting
static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
+ @IntDef(prefix = {"STACK_VISIBILITY"}, value = {
+ STACK_VISIBILITY_VISIBLE,
+ STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ STACK_VISIBILITY_INVISIBLE,
+ })
+ @interface StackVisibility {}
+
+ /** Stack is visible. No other stacks on top that fully or partially occlude it. */
+ static final int STACK_VISIBILITY_VISIBLE = 0;
+
+ /** Stack is partially occluded by other translucent stack(s) on top of it. */
+ static final int STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
+
+ /** Stack is completely invisible. */
+ static final int STACK_VISIBILITY_INVISIBLE = 2;
+
+ enum ActivityState {
+ INITIALIZING,
+ STARTED,
+ RESUMED,
+ PAUSING,
+ PAUSED,
+ STOPPING,
+ STOPPED,
+ FINISHING,
+ DESTROYING,
+ DESTROYED,
+ RESTARTING_PROCESS
+ }
+
+ // The topmost Activity passed to convertToTranslucent(). When non-null it means we are
+ // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
+ // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
+ // Activity in mTranslucentActivityWaiting is notified via
+ // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
+ // background activity being drawn then the same call will be made with a true value.
+ ActivityRecord mTranslucentActivityWaiting = null;
+ ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<>();
+
+ /**
+ * Set when we know we are going to be calling updateConfiguration()
+ * soon, so want to skip intermediate config checks.
+ */
+ boolean mConfigWillChange;
+
+ /**
+ * Used to keep resumeTopActivityUncheckedLocked() from being entered recursively
+ */
+ boolean mInResumeTopActivity = false;
+
+ int mCurrentUser;
+
String affinity; // The affinity name for this task, or null; may change identity.
String rootAffinity; // Initial base affinity, or null; does not change from initial root.
String mWindowLayoutAffinity; // Launch param affinity of this task or null. Used when saving
@@ -433,6 +586,13 @@ class Task extends WindowContainer<WindowContainer> {
static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
private int mForceHiddenFlags = 0;
+ // TODO(b/160201781): Revisit double invocation issue in Task#removeChild.
+ /**
+ * Skip {@link ActivityStackSupervisor#removeTask(Task, boolean, boolean, String)} execution if
+ * {@code true} to prevent double traversal of {@link #mChildren} in a loop.
+ */
+ boolean mInRemoveTask;
+
// When non-null, this is a transaction that will get applied on the next frame returned after
// a relayout is requested from the client. While this is only valid on a leaf task; since the
// transaction can effect an ancestor task, this also needs to keep track of the ancestor task
@@ -440,6 +600,268 @@ class Task extends WindowContainer<WindowContainer> {
SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
Task mMainWindowSizeChangeTask;
+ // If this is true, we are in the bounds animating mode. The task will be down or upscaled to
+ // perfectly fit the region it would have been cropped to. We may also avoid certain logic we
+ // would otherwise apply while resizing, while resizing in the bounds animating mode.
+ private boolean mBoundsAnimating = false;
+ // Set when an animation has been requested but has not yet started from the UI thread. This is
+ // cleared when the animation actually starts.
+ private boolean mBoundsAnimatingRequested = false;
+ private Rect mBoundsAnimationTarget = new Rect();
+ private Rect mBoundsAnimationSourceHintBounds = new Rect();
+
+ Rect mPreAnimationBounds = new Rect();
+
+ private final AnimatingActivityRegistry mAnimatingActivityRegistry =
+ new AnimatingActivityRegistry();
+
+ private boolean mTopActivityOccludesKeyguard;
+ private ActivityRecord mTopDismissingKeyguardActivity;
+
+ private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
+
+ private final Handler mHandler;
+
+ private class ActivityStackHandler extends Handler {
+
+ ActivityStackHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case TRANSLUCENT_TIMEOUT_MSG: {
+ synchronized (mAtmService.mGlobalLock) {
+ notifyActivityDrawnLocked(null);
+ }
+ } break;
+ }
+ }
+ }
+
+ private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
+ private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
+ new EnsureActivitiesVisibleHelper(this);
+ private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
+ new EnsureVisibleActivitiesConfigHelper();
+ private class EnsureVisibleActivitiesConfigHelper {
+ private boolean mUpdateConfig;
+ private boolean mPreserveWindow;
+ private boolean mBehindFullscreen;
+
+ void reset(boolean preserveWindow) {
+ mPreserveWindow = preserveWindow;
+ mUpdateConfig = false;
+ mBehindFullscreen = false;
+ }
+
+ void process(ActivityRecord start, boolean preserveWindow) {
+ if (start == null || !start.mVisibleRequested) {
+ return;
+ }
+ reset(preserveWindow);
+
+ final PooledFunction f = PooledLambda.obtainFunction(
+ EnsureVisibleActivitiesConfigHelper::processActivity, this,
+ PooledLambda.__(ActivityRecord.class));
+ forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
+ f.recycle();
+
+ if (mUpdateConfig) {
+ // Ensure the resumed state of the focus activity if we updated the configuration of
+ // any activity.
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
+ }
+
+ boolean processActivity(ActivityRecord r) {
+ mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
+ mBehindFullscreen |= r.occludesParent();
+ return mBehindFullscreen;
+ }
+ }
+
+ private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
+ new CheckBehindFullscreenActivityHelper();
+ private class CheckBehindFullscreenActivityHelper {
+ private boolean mAboveTop;
+ private boolean mBehindFullscreenActivity;
+ private ActivityRecord mToCheck;
+ private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
+ private boolean mHandlingOccluded;
+
+ private void reset(ActivityRecord toCheck,
+ Consumer<ActivityRecord> handleBehindFullscreenActivity) {
+ mToCheck = toCheck;
+ mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
+ mAboveTop = true;
+ mBehindFullscreenActivity = false;
+
+ if (!shouldBeVisible(null)) {
+ // The stack is not visible, so no activity in it should be displaying a starting
+ // window. Mark all activities below top and behind fullscreen.
+ mAboveTop = false;
+ mBehindFullscreenActivity = true;
+ }
+
+ mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
+ }
+
+ boolean process(ActivityRecord toCheck,
+ Consumer<ActivityRecord> handleBehindFullscreenActivity) {
+ reset(toCheck, handleBehindFullscreenActivity);
+
+ if (!mHandlingOccluded && mBehindFullscreenActivity) {
+ return true;
+ }
+
+ final ActivityRecord topActivity = topRunningActivity();
+ final PooledFunction f = PooledLambda.obtainFunction(
+ CheckBehindFullscreenActivityHelper::processActivity, this,
+ PooledLambda.__(ActivityRecord.class), topActivity);
+ forAllActivities(f);
+ f.recycle();
+
+ return mBehindFullscreenActivity;
+ }
+
+ /** Returns {@code true} to stop the outer loop and indicate the result is computed. */
+ private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
+ if (mAboveTop) {
+ if (r == topActivity) {
+ if (r == mToCheck) {
+ // It is the top activity in a visible stack.
+ mBehindFullscreenActivity = false;
+ return true;
+ }
+ mAboveTop = false;
+ }
+ mBehindFullscreenActivity |= r.occludesParent();
+ return false;
+ }
+
+ if (mHandlingOccluded) {
+ // Iterating through all occluded activities.
+ if (mBehindFullscreenActivity) {
+ mHandleBehindFullscreenActivity.accept(r);
+ }
+ } else if (r == mToCheck) {
+ return true;
+ } else if (mBehindFullscreenActivity) {
+ // It is occluded before {@param toCheck} is found.
+ return true;
+ }
+ mBehindFullscreenActivity |= r.occludesParent();
+ return false;
+ }
+ }
+
+ // TODO: Can we just loop through WindowProcessController#mActivities instead of doing this?
+ private final RemoveHistoryRecordsForApp mRemoveHistoryRecordsForApp =
+ new RemoveHistoryRecordsForApp();
+ private class RemoveHistoryRecordsForApp {
+ private boolean mHasVisibleActivities;
+ private boolean mIsProcessRemoved;
+ private WindowProcessController mApp;
+ private ArrayList<ActivityRecord> mToRemove = new ArrayList<>();
+
+ boolean process(WindowProcessController app) {
+ mToRemove.clear();
+ mHasVisibleActivities = false;
+ mApp = app;
+ mIsProcessRemoved = app.isRemoved();
+
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ RemoveHistoryRecordsForApp::addActivityToRemove, this,
+ PooledLambda.__(ActivityRecord.class));
+ forAllActivities(c);
+ c.recycle();
+
+ while (!mToRemove.isEmpty()) {
+ processActivity(mToRemove.remove(0));
+ }
+
+ mApp = null;
+ return mHasVisibleActivities;
+ }
+
+ private void addActivityToRemove(ActivityRecord r) {
+ if (r.app == mApp) {
+ mToRemove.add(r);
+ }
+ }
+
+ private void processActivity(ActivityRecord r) {
+ if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Record " + r + ": app=" + r.app);
+
+ if (r.app != mApp) {
+ return;
+ }
+ if (r.isVisible() || r.mVisibleRequested) {
+ // While an activity launches a new activity, it's possible that the old
+ // activity is already requested to be hidden (mVisibleRequested=false), but
+ // this visibility is not yet committed, so isVisible()=true.
+ mHasVisibleActivities = true;
+ }
+ final boolean remove;
+ if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
+ || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
+ && r.launchCount < 3 && !r.finishing) {
+ // If the process crashed during a resize, always try to relaunch it, unless
+ // it has failed more than twice. Skip activities that's already finishing
+ // cleanly by itself.
+ remove = false;
+ } else if ((!r.hasSavedState() && !r.stateNotNeeded
+ && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
+ // Don't currently have state for the activity, or
+ // it is finishing -- always remove it.
+ remove = true;
+ } else if (!r.mVisibleRequested && r.launchCount > 2
+ && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
+ // We have launched this activity too many times since it was
+ // able to run, so give up and remove it.
+ // (Note if the activity is visible, we don't remove the record.
+ // We leave the dead window on the screen but the process will
+ // not be restarted unless user explicitly tap on it.)
+ remove = true;
+ } else {
+ // The process may be gone, but the activity lives on!
+ remove = false;
+ }
+ if (remove) {
+ if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
+ "Removing activity " + r + " from stack "
+ + ": hasSavedState=" + r.hasSavedState()
+ + " stateNotNeeded=" + r.stateNotNeeded
+ + " finishing=" + r.finishing
+ + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
+ if (!r.finishing || mIsProcessRemoved) {
+ Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
+ EventLogTags.writeWmFinishActivity(r.mUserId,
+ System.identityHashCode(r), r.getTask().mTaskId,
+ r.shortComponentName, "proc died without state saved");
+ }
+ } else {
+ // We have the current state for this activity, so
+ // it can be restarted later when needed.
+ if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
+ if (DEBUG_APP) Slog.v(TAG_APP,
+ "Clearing app during removeHistory for activity " + r);
+ r.app = null;
+ // Set nowVisible to previous visible state. If the app was visible while
+ // it died, we leave the dead window on screen so it's basically visible.
+ // This is needed when user later tap on the dead window, we need to stop
+ // other apps when user transfers focus to the restarted activity.
+ r.nowVisible = r.mVisibleRequested;
+ }
+ r.cleanUp(true /* cleanServices */, true /* setState */);
+ if (remove) {
+ r.removeFromHistory("appDied");
+ }
+ }
+ }
+
private final FindRootHelper mFindRootHelper = new FindRootHelper();
private class FindRootHelper {
private ActivityRecord mRoot;
@@ -500,12 +922,22 @@ class Task extends WindowContainer<WindowContainer> {
boolean mCreatedByOrganizer;
/**
- * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
- * ActivityInfo, Intent, TaskDescription)} instead.
+ * Don't use constructor directly. Use {@link TaskDisplayArea#createStackUnchecked()} instead.
+ */
+ Task(ActivityTaskManagerService atmService, int id, int activityType,
+ ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+ this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
+ null /*taskDescription*/, null /*stack*/);
+ mCreatedByOrganizer = createdByOrganizer;
+ setActivityType(activityType);
+ }
+
+ /**
+ * Don't use constructor directly. Use {@link Task#reuseOrCreateTask()} instead.
*/
Task(ActivityTaskManagerService atmService, int _taskId, ActivityInfo info, Intent _intent,
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
- TaskDescription _taskDescription, ActivityStack stack) {
+ TaskDescription _taskDescription, Task stack) {
this(atmService, _taskId, _intent, null /*_affinityIntent*/, null /*_affinity*/,
null /*_rootAffinity*/, null /*_realActivity*/, null /*_origActivity*/,
false /*_rootWasReset*/, false /*_autoRemoveRecents*/, false /*_askedCompatMode*/,
@@ -531,7 +963,7 @@ class Task extends WindowContainer<WindowContainer> {
@Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture,
boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
ActivityInfo info, IVoiceInteractionSession _voiceSession,
- IVoiceInteractor _voiceInteractor, ActivityStack stack) {
+ IVoiceInteractor _voiceInteractor, Task stack) {
super(atmService.mWindowManager);
EventLogTags.writeWmTaskCreated(_taskId, stack != null ? getRootTaskId() : INVALID_TASK_ID);
@@ -580,6 +1012,8 @@ class Task extends WindowContainer<WindowContainer> {
mMinHeight = minHeight;
}
mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
+ mHandler = new ActivityStackHandler(mStackSupervisor.mLooper);
+ mCurrentUser = mAtmService.mAmInternal.getCurrentUserId();
}
Task reuseAsLeafTask(IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
@@ -720,7 +1154,7 @@ class Task extends WindowContainer<WindowContainer> {
}
/** Convenience method to reparent a task to the top or bottom position of the stack. */
- boolean reparent(ActivityStack preferredStack, boolean toTop,
+ boolean reparent(Task preferredStack, boolean toTop,
@ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
String reason) {
return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate, deferResume,
@@ -731,7 +1165,7 @@ class Task extends WindowContainer<WindowContainer> {
* Convenience method to reparent a task to the top or bottom position of the stack, with
* an option to skip scheduling the picture-in-picture mode change.
*/
- boolean reparent(ActivityStack preferredStack, boolean toTop,
+ boolean reparent(Task preferredStack, boolean toTop,
@ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
boolean schedulePictureInPictureModeChange, String reason) {
return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate,
@@ -739,7 +1173,7 @@ class Task extends WindowContainer<WindowContainer> {
}
/** Convenience method to reparent a task to a specific position of the stack. */
- boolean reparent(ActivityStack preferredStack, int position,
+ boolean reparent(Task preferredStack, int position,
@ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
String reason) {
return reparent(preferredStack, position, moveStackMode, animate, deferResume,
@@ -765,14 +1199,14 @@ class Task extends WindowContainer<WindowContainer> {
*/
// TODO: Inspect all call sites and change to just changing windowing mode of the stack vs.
// re-parenting the task. Can only be done when we are no longer using static stack Ids.
- boolean reparent(ActivityStack preferredStack, int position,
+ boolean reparent(Task preferredStack, int position,
@ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
boolean schedulePictureInPictureModeChange, String reason) {
final ActivityStackSupervisor supervisor = mStackSupervisor;
final RootWindowContainer root = mRootWindowContainer;
final WindowManagerService windowManager = mAtmService.mWindowManager;
- final ActivityStack sourceStack = getStack();
- final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
+ final Task sourceStack = getRootTask();
+ final Task toStack = supervisor.getReparentTargetStack(this, preferredStack,
position == MAX_VALUE);
if (toStack == sourceStack) {
return false;
@@ -1165,6 +1599,12 @@ class Task extends WindowContainer<WindowContainer> {
}
mRootWindowContainer.updateUIDsPresentOnDisplay();
+
+ // Resume next focusable stack after reparenting to another display if we aren't removing
+ // the prevous display.
+ if (oldDisplay != null && oldDisplay.isRemoving()) {
+ postReparent();
+ }
}
void cleanUpActivityReferences(ActivityRecord r) {
@@ -1363,7 +1803,7 @@ class Task extends WindowContainer<WindowContainer> {
// A rootable task that is now being added to be the child of an organized task. Making
// sure the stack references is keep updated.
if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
- getDisplayArea().addStackReferenceIfNeeded((ActivityStack) child);
+ getDisplayArea().addStackReferenceIfNeeded((Task) child);
}
// Make sure the list of display UID whitelists is updated
@@ -1412,8 +1852,8 @@ class Task extends WindowContainer<WindowContainer> {
void removeChild(WindowContainer r, String reason) {
// A rootable child task that is now being removed from an organized task. Making sure
// the stack references is keep updated.
- if (mTaskOrganizer != null && mCreatedByOrganizer && r.asTask() != null) {
- getDisplayArea().removeStackReferenceIfNeeded((ActivityStack) r);
+ if (mCreatedByOrganizer && r.asTask() != null) {
+ getDisplayArea().removeStackReferenceIfNeeded((Task) r);
}
if (!mChildren.contains(r)) {
Slog.e(TAG, "removeChild: r=" + r + " not found in t=" + this);
@@ -1453,7 +1893,7 @@ class Task extends WindowContainer<WindowContainer> {
// Remove entire task if it doesn't have any activity left and it isn't marked for reuse
// or created by task organizer.
if (!isRootTask()) {
- getStack().removeChild(this, reason);
+ getRootTask().removeChild(this, reason);
}
EventLogTags.writeWmTaskRemoved(mTaskId,
"removeChild: last r=" + r + " in t=" + this);
@@ -1492,13 +1932,10 @@ class Task extends WindowContainer<WindowContainer> {
return autoRemoveRecents || (!hasChild() && !getHasBeenVisible());
}
- /**
- * Completely remove all activities associated with an existing
- * task starting at a specified index.
- */
- private void performClearTaskAtIndexLocked(String reason) {
+ /** Completely remove all activities associated with an existing task. */
+ void performClearTask(String reason) {
// Broken down into to cases to avoid object create due to capturing mStack.
- if (getStack() == null) {
+ if (getRootTask() == null) {
forAllActivities((r) -> {
if (r.finishing) return;
// Task was restored from persistent storage.
@@ -1520,7 +1957,7 @@ class Task extends WindowContainer<WindowContainer> {
*/
void performClearTaskLocked() {
mReuseTask = true;
- performClearTaskAtIndexLocked("clear-task-all");
+ performClearTask("clear-task-all");
mReuseTask = false;
}
@@ -1581,11 +2018,6 @@ class Task extends WindowContainer<WindowContainer> {
return false;
}
- void removeTaskActivitiesLocked(String reason) {
- // Just remove the entire task.
- performClearTaskAtIndexLocked(reason);
- }
-
String lockTaskAuthToString() {
switch (mLockTaskAuth) {
case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK";
@@ -1882,8 +2314,7 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
+ private void onConfigurationChangedInner(Configuration newParentConfig) {
// Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
// restore the last recorded non-fullscreen bounds.
final boolean prevPersistTaskBounds = getWindowConfiguration().persistTaskBounds();
@@ -1911,7 +2342,7 @@ class Task extends WindowContainer<WindowContainer> {
final boolean pipChanging = wasInPictureInPicture != inPinnedWindowingMode();
if (pipChanging) {
- mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(this, getStack());
+ mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(this, getRootTask());
} else if (wasInMultiWindowMode != inMultiWindowMode()) {
mStackSupervisor.scheduleUpdateMultiWindowMode(this);
}
@@ -1960,6 +2391,78 @@ class Task extends WindowContainer<WindowContainer> {
}
}
+ @Override
+ public void onConfigurationChanged(Configuration newParentConfig) {
+ // Calling Task#onConfigurationChanged() for leaf task since the ops in this method are
+ // particularly for ActivityStack, like preventing bounds changes when inheriting certain
+ // windowing mode.
+ if (!isRootTask()) {
+ onConfigurationChangedInner(newParentConfig);
+ return;
+ }
+
+ final int prevWindowingMode = getWindowingMode();
+ final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
+ final int prevRotation = getWindowConfiguration().getRotation();
+ final Rect newBounds = mTmpRect;
+ // Initialize the new bounds by previous bounds as the input and output for calculating
+ // override bounds in pinned (pip) or split-screen mode.
+ getBounds(newBounds);
+
+ onConfigurationChangedInner(newParentConfig);
+
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ if (taskDisplayArea == null) {
+ return;
+ }
+
+ if (prevWindowingMode != getWindowingMode()) {
+ taskDisplayArea.onStackWindowingModeChanged(this);
+ }
+
+ final DisplayContent display = getDisplay();
+ if (display == null ) {
+ return;
+ }
+
+ final boolean windowingModeChanged = prevWindowingMode != getWindowingMode();
+ final int overrideWindowingMode = getRequestedOverrideWindowingMode();
+ // Update bounds if applicable
+ boolean hasNewOverrideBounds = false;
+ // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
+ if ((overrideWindowingMode != WINDOWING_MODE_PINNED)
+ && !getRequestedOverrideBounds().isEmpty()) {
+ // If the parent (display) has rotated, rotate our bounds to best-fit where their
+ // bounds were on the pre-rotated display.
+ final int newRotation = getWindowConfiguration().getRotation();
+ final boolean rotationChanged = prevRotation != newRotation;
+ if (rotationChanged) {
+ display.mDisplayContent.rotateBounds(
+ newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation,
+ newBounds);
+ hasNewOverrideBounds = true;
+ }
+ }
+
+ if (windowingModeChanged) {
+ taskDisplayArea.onStackWindowingModeChanged(this);
+ }
+ if (hasNewOverrideBounds) {
+ if (inSplitScreenWindowingMode()) {
+ setBounds(newBounds);
+ } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
+ // For pinned stack, resize is now part of the {@link WindowContainerTransaction}
+ resize(new Rect(newBounds), PRESERVE_WINDOWS, true /* deferResume */);
+ }
+ }
+ if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
+ // Since always on top is only on when the stack is freeform or pinned, the state
+ // can be toggled when the windowing mode changes. We must make sure the stack is
+ // placed properly when always on top state changes.
+ taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
+ }
+ }
+
/**
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
*/
@@ -2192,10 +2695,11 @@ class Task extends WindowContainer<WindowContainer> {
DisplayInfo displayInfo) {
outNonDecorBounds.set(bounds);
outStableBounds.set(bounds);
- if (getStack() == null || getStack().getDisplay() == null) {
+ final Task rootTask = getRootTask();
+ if (rootTask == null || rootTask.getDisplay() == null) {
return;
}
- DisplayPolicy policy = getStack().getDisplay().mDisplayContent.getDisplayPolicy();
+ DisplayPolicy policy = rootTask.getDisplay().mDisplayContent.getDisplayPolicy();
if (policy == null) {
return;
}
@@ -2534,8 +3038,8 @@ class Task extends WindowContainer<WindowContainer> {
/** Updates the task's bounds and override configuration to match what is expected for the
* input stack. */
- void updateOverrideConfigurationForStack(ActivityStack inStack) {
- final ActivityStack stack = getStack();
+ void updateOverrideConfigurationForStack(Task inStack) {
+ final Task stack = getRootTask();
if (stack != null && stack == inStack) {
return;
@@ -2548,7 +3052,7 @@ class Task extends WindowContainer<WindowContainer> {
/** Returns the bounds that should be used to launch this task. */
Rect getLaunchBounds() {
- final ActivityStack stack = getStack();
+ final Task stack = getRootTask();
if (stack == null) {
return null;
}
@@ -2583,7 +3087,7 @@ class Task extends WindowContainer<WindowContainer> {
@Override
DisplayContent getDisplayContent() {
// TODO: Why aren't we just using our own display content vs. parent's???
- final ActivityStack stack = getStack();
+ final Task stack = getRootTask();
return stack != null && stack != this
? stack.getDisplayContent() : super.getDisplayContent();
}
@@ -2593,11 +3097,6 @@ class Task extends WindowContainer<WindowContainer> {
return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
}
- // TODO: Migrate callers to getRootTask()
- ActivityStack getStack() {
- return (ActivityStack) getRootTask();
- }
-
/** @return Id of root task. */
int getRootTaskId() {
return getRootTask().mTaskId;
@@ -2638,7 +3137,7 @@ class Task extends WindowContainer<WindowContainer> {
* Find next proper focusable stack and make it focused.
* @return The stack that now got the focus, {@code null} if none found.
*/
- ActivityStack adjustFocusToNextFocusableTask(String reason) {
+ Task adjustFocusToNextFocusableTask(String reason) {
return adjustFocusToNextFocusableTask(reason, false /* allowFocusSelf */,
true /* moveDisplayToTop */);
}
@@ -2651,7 +3150,7 @@ class Task extends WindowContainer<WindowContainer> {
}
final Task focusableTask = parent.getTask((task) -> (allowFocusSelf || task != this)
- && ((ActivityStack) task).isFocusableAndVisible());
+ && ((Task) task).isFocusableAndVisible());
if (focusableTask == null && parent.asTask() != null) {
return parent.asTask().getNextFocusableTask(allowFocusSelf);
} else {
@@ -2666,18 +3165,17 @@ class Task extends WindowContainer<WindowContainer> {
* @param moveDisplayToTop Whether to move display to top while making the task focused.
* @return The root task that now got the focus, {@code null} if none found.
*/
- ActivityStack adjustFocusToNextFocusableTask(String reason, boolean allowFocusSelf,
+ Task adjustFocusToNextFocusableTask(String reason, boolean allowFocusSelf,
boolean moveDisplayToTop) {
- ActivityStack focusableTask = (ActivityStack) getNextFocusableTask(allowFocusSelf);
+ Task focusableTask = getNextFocusableTask(allowFocusSelf);
if (focusableTask == null) {
- focusableTask = mRootWindowContainer.getNextFocusableStack((ActivityStack) this,
- !allowFocusSelf);
+ focusableTask = mRootWindowContainer.getNextFocusableStack(this, !allowFocusSelf);
}
if (focusableTask == null) {
return null;
}
- final ActivityStack rootTask = (ActivityStack) focusableTask.getRootTask();
+ final Task rootTask = focusableTask.getRootTask();
if (!moveDisplayToTop) {
// There may be multiple task layers above this task, so when relocating the task to the
// top, we should move this task and each of its parent task that below display area to
@@ -2808,7 +3306,7 @@ class Task extends WindowContainer<WindowContainer> {
// No reason to defer removal of a Task that doesn't have any child.
return false;
}
- return hasWindowsAlive() && getStack().isAnimating(TRANSITION | CHILDREN);
+ return hasWindowsAlive() && getRootTask().isAnimating(TRANSITION | CHILDREN);
}
@Override
@@ -2827,9 +3325,9 @@ class Task extends WindowContainer<WindowContainer> {
}
// TODO: Consolidate this with Task.reparent()
- void reparent(ActivityStack stack, int position, boolean moveParents, String reason) {
+ void reparent(Task stack, int position, boolean moveParents, String reason) {
if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
- + " from stack=" + getStack());
+ + " from stack=" + getRootTask());
EventLogTags.writeWmTaskRemoved(mTaskId, "reParentTask:" + reason);
reparent(stack, position);
@@ -2856,9 +3354,13 @@ class Task extends WindowContainer<WindowContainer> {
/** Set the task bounds. Passing in null sets the bounds to fullscreen. */
@Override
public int setBounds(Rect bounds) {
+ if (isRootTask()) {
+ return setBounds(getRequestedOverrideBounds(), bounds);
+ }
+
int rotation = Surface.ROTATION_0;
- final DisplayContent displayContent = getStack() != null
- ? getStack().getDisplayContent() : null;
+ final DisplayContent displayContent = getRootTask() != null
+ ? getRootTask().getDisplayContent() : null;
if (displayContent != null) {
rotation = displayContent.getDisplayInfo().rotation;
}
@@ -2870,6 +3372,17 @@ class Task extends WindowContainer<WindowContainer> {
}
@Override
+ public boolean isCompatible(int windowingMode, int activityType) {
+ // TODO: Should we just move this to ConfigurationContainer?
+ if (activityType == ACTIVITY_TYPE_UNDEFINED) {
+ // Undefined activity types end up in a standard stack once the stack is created on a
+ // display, so they should be considered compatible.
+ activityType = ACTIVITY_TYPE_STANDARD;
+ }
+ return super.isCompatible(windowingMode, activityType);
+ }
+
+ @Override
public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
ConfigurationContainer requestingContainer) {
if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
@@ -2903,6 +3416,9 @@ class Task extends WindowContainer<WindowContainer> {
mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
mTaskId, displayId);
}
+ if (isRootTask()) {
+ updateSurfaceBounds();
+ }
}
boolean isResizeable(boolean checkSupportsPip) {
@@ -2993,7 +3509,13 @@ class Task extends WindowContainer<WindowContainer> {
/** Bounds of the task to be used for dimming, as well as touch related tests. */
void getDimBounds(Rect out) {
- final DisplayContent displayContent = getStack().getDisplayContent();
+ if (isRootTask()) {
+ getBounds(out);
+ return;
+ }
+
+ final Task rootTask = getRootTask();
+ final DisplayContent displayContent = rootTask.getDisplayContent();
// It doesn't matter if we in particular are part of the resize, since we couldn't have
// a DimLayer anyway if we weren't visible.
final boolean dockedResizing = displayContent != null
@@ -3017,9 +3539,9 @@ class Task extends WindowContainer<WindowContainer> {
// stack bounds and so we don't even want to use them. Even if the app should not be
// resized the Dim should keep up with the divider.
if (dockedResizing) {
- getStack().getBounds(out);
+ rootTask.getBounds(out);
} else {
- getStack().getBounds(mTmpRect);
+ rootTask.getBounds(mTmpRect);
mTmpRect.intersect(getBounds());
out.set(mTmpRect);
}
@@ -3032,7 +3554,8 @@ class Task extends WindowContainer<WindowContainer> {
void setDragResizing(boolean dragResizing, int dragResizeMode) {
if (mDragResizing != dragResizing) {
// No need to check if the mode is allowed if it's leaving dragResize
- if (dragResizing && !DragResizeMode.isModeAllowedForStack(getStack(), dragResizeMode)) {
+ if (dragResizing
+ && !DragResizeMode.isModeAllowedForStack(getRootTask(), dragResizeMode)) {
throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
+ getRootTaskId() + " dragResizeMode=" + dragResizeMode);
}
@@ -3176,6 +3699,33 @@ class Task extends WindowContainer<WindowContainer> {
return false;
}
+ /** Returns the top-most activity that occludes the given one, or {@code null} if none. */
+ @Nullable
+ ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
+ final ActivityRecord top = getActivity(ActivityRecord::occludesParent,
+ true /* traverseTopToBottom */, activity);
+ return top != activity ? top : null;
+ }
+
+ /** Iterates through all occluded activities. */
+ void forAllOccludedActivities(Consumer<ActivityRecord> handleOccludedActivity) {
+ if (!shouldBeVisible(null /* starting */)) {
+ // The stack is invisible so all activities are occluded.
+ forAllActivities(handleOccludedActivity);
+ return;
+ }
+ final ActivityRecord topOccluding = getOccludingActivityAbove(null);
+ if (topOccluding == null) {
+ // No activities are occluded.
+ return;
+ }
+ // Invoke the callback on the activities behind the top occluding activity.
+ forAllActivities(r -> {
+ handleOccludedActivity.accept(r);
+ return false;
+ }, topOccluding, false /* includeBoundary */, true /* traverseTopToBottom */);
+ }
+
@Override
public SurfaceControl.Builder makeAnimationLeash() {
return super.makeAnimationLeash().setMetadata(METADATA_TASK_ID, mTaskId);
@@ -3195,9 +3745,9 @@ class Task extends WindowContainer<WindowContainer> {
@Override
Rect getAnimationBounds(int appStackClipMode) {
// TODO(b/131661052): we should remove appStackClipMode with hierarchical animations.
- if (appStackClipMode == STACK_CLIP_BEFORE_ANIM && getStack() != null) {
+ if (appStackClipMode == STACK_CLIP_BEFORE_ANIM && getRootTask() != null) {
// Using the stack bounds here effectively applies the clipping before animation.
- return getStack().getBounds();
+ return getRootTask().getBounds();
}
return super.getAnimationBounds(appStackClipMode);
}
@@ -3547,6 +4097,20 @@ class Task extends WindowContainer<WindowContainer> {
child.dump(pw, doublePrefix, dumpAll);
}
}
+
+ if (!mExitingActivities.isEmpty()) {
+ pw.println();
+ pw.println(prefix + "Exiting application tokens:");
+ for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
+ WindowToken token = mExitingActivities.get(i);
+ pw.print(doublePrefix + "Exiting App #" + i);
+ pw.print(' '); pw.print(token);
+ pw.println(':');
+ token.dump(pw, doublePrefix, dumpAll);
+ }
+ pw.println();
+ }
+ mAnimatingActivityRegistry.dump(pw, "AnimatingApps:", prefix);
}
@@ -3642,7 +4206,7 @@ class Task extends WindowContainer<WindowContainer> {
*
* @param starting The currently starting activity or null if there is none.
*/
- @ActivityStack.StackVisibility
+ @Task.StackVisibility
int getVisibility(ActivityRecord starting) {
if (!isAttached() || isForceHidden()) {
return STACK_VISIBILITY_INVISIBLE;
@@ -4263,7 +4827,7 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- final Task task = new ActivityStack(stackSupervisor.mService, taskId, intent,
+ final Task task = new Task(stackSupervisor.mService, taskId, intent,
affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
lastTimeOnTop, neverRelinquishIdentity, taskDescription, taskAffiliation,
@@ -4508,6 +5072,7 @@ class Task extends WindowContainer<WindowContainer> {
*/
void setMainWindowSizeChangeTransaction(SurfaceControl.Transaction t) {
setMainWindowSizeChangeTransaction(t, this);
+ forAllWindows(WindowState::requestRedrawForSync, true);
}
private void setMainWindowSizeChangeTransaction(SurfaceControl.Transaction t, Task origin) {
@@ -4577,4 +5142,2655 @@ class Task extends WindowContainer<WindowContainer> {
return TASK;
}
+ @Override
+ public void setWindowingMode(int windowingMode) {
+ // Reset the cached result of toString()
+ stringName = null;
+
+ // Calling Task#setWindowingMode() for leaf task since this is the a specialization of
+ // {@link #setWindowingMode(int)} for ActivityStack.
+ if (!isRootTask()) {
+ super.setWindowingMode(windowingMode);
+ return;
+ }
+
+ setWindowingMode(windowingMode, false /* creating */);
+ }
+
+ /**
+ * Specialization of {@link #setWindowingMode(int)} for this subclass.
+ *
+ * @param preferredWindowingMode the preferred windowing mode. This may not be honored depending
+ * on the state of things. For example, WINDOWING_MODE_UNDEFINED will resolve to the
+ * previous non-transient mode if this stack is currently in a transient mode.
+ * @param creating {@code true} if this is being run during ActivityStack construction.
+ */
+ void setWindowingMode(int preferredWindowingMode, boolean creating) {
+ mWmService.inSurfaceTransaction(() -> setWindowingModeInSurfaceTransaction(
+ preferredWindowingMode, creating));
+ }
+
+ private void setWindowingModeInSurfaceTransaction(int preferredWindowingMode,
+ boolean creating) {
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ if (taskDisplayArea == null) {
+ Slog.d(TAG, "taskDisplayArea is null, bail early");
+ return;
+ }
+ final int currentMode = getWindowingMode();
+ final int currentOverrideMode = getRequestedOverrideWindowingMode();
+ final Task topTask = getTopMostTask();
+ int windowingMode = preferredWindowingMode;
+
+ // Need to make sure windowing mode is supported. If we in the process of creating the stack
+ // no need to resolve the windowing mode again as it is already resolved to the right mode.
+ if (!creating) {
+ if (!taskDisplayArea.isValidWindowingMode(windowingMode, null /* ActivityRecord */,
+ topTask, getActivityType())) {
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ }
+ }
+
+ final boolean alreadyInSplitScreenMode = taskDisplayArea.isSplitScreenModeActivated();
+
+ if (creating && alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+ && isActivityTypeStandardOrUndefined()) {
+ // If the stack is being created explicitly in fullscreen mode, dismiss split-screen
+ // and display a warning toast about it.
+ mAtmService.getTaskChangeNotificationController()
+ .notifyActivityDismissingDockedStack();
+ taskDisplayArea.onSplitScreenModeDismissed(this);
+ }
+
+ if (currentMode == windowingMode) {
+ // You are already in the window mode, so we can skip most of the work below. However,
+ // it's possible that we have inherited the current windowing mode from a parent. So,
+ // fulfill this method's contract by setting the override mode directly.
+ getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ return;
+ }
+
+ final ActivityRecord topActivity = getTopNonFinishingActivity();
+
+ // For now, assume that the Stack's windowing mode is what will actually be used
+ // by it's activities. In the future, there may be situations where this doesn't
+ // happen; so at that point, this message will need to handle that.
+ int likelyResolvedMode = windowingMode;
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ final ConfigurationContainer parent = getParent();
+ likelyResolvedMode = parent != null ? parent.getWindowingMode()
+ : WINDOWING_MODE_FULLSCREEN;
+ }
+ if (currentMode == WINDOWING_MODE_PINNED) {
+ mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
+ }
+ if (likelyResolvedMode == WINDOWING_MODE_PINNED
+ && taskDisplayArea.getRootPinnedTask() != null) {
+ // Can only have 1 pip at a time, so replace an existing pip
+ taskDisplayArea.getRootPinnedTask().dismissPip();
+ }
+ if (likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
+ && topActivity != null && !topActivity.noDisplay
+ && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) {
+ // Inform the user that they are starting an app that may not work correctly in
+ // multi-window mode.
+ final String packageName = topActivity.info.applicationInfo.packageName;
+ mAtmService.getTaskChangeNotificationController().notifyActivityForcedResizable(
+ topTask.mTaskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
+ }
+
+ mAtmService.deferWindowLayout();
+ try {
+ if (topActivity != null) {
+ mStackSupervisor.mNoAnimActivities.add(topActivity);
+ }
+ super.setWindowingMode(windowingMode);
+ // setWindowingMode triggers an onConfigurationChanged cascade which can result in a
+ // different resolved windowing mode (usually when preferredWindowingMode is UNDEFINED).
+ windowingMode = getWindowingMode();
+
+ if (creating) {
+ // Nothing else to do if we don't have a window container yet. E.g. call from ctor.
+ return;
+ }
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && alreadyInSplitScreenMode) {
+ // We already have a split-screen stack in this display, so just move the tasks over.
+ // TODO: Figure-out how to do all the stuff in
+ // AMS.setTaskWindowingModeSplitScreenPrimary
+ throw new IllegalArgumentException("Setting primary split-screen windowing mode"
+ + " while there is already one isn't currently supported");
+ //return;
+ }
+ } finally {
+ mAtmService.continueWindowLayout();
+ }
+
+ mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
+
+ /** Resume next focusable stack after reparenting to another display. */
+ void postReparent() {
+ adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */,
+ true /* moveDisplayToTop */);
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ // Update visibility of activities before notifying WM. This way it won't try to resize
+ // windows that are no longer visible.
+ mRootWindowContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS);
+ }
+
+ DisplayContent getDisplay() {
+ return getDisplayContent();
+ }
+
+ /** @return true if the stack can only contain one task */
+ boolean isSingleTaskInstance() {
+ final DisplayContent display = getDisplay();
+ return display != null && display.isSingleTaskInstance();
+ }
+
+ final boolean isHomeOrRecentsStack() {
+ return isActivityTypeHome() || isActivityTypeRecents();
+ }
+
+ final boolean isOnHomeDisplay() {
+ return getDisplayId() == DEFAULT_DISPLAY;
+ }
+
+ void moveToFront(String reason) {
+ moveToFront(reason, null);
+ }
+
+ /**
+ * @param reason The reason for moving the stack to the front.
+ * @param task If non-null, the task will be moved to the top of the stack.
+ */
+ void moveToFront(String reason, Task task) {
+ if (!isAttached()) {
+ return;
+ }
+
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+
+ if (inSplitScreenSecondaryWindowingMode()) {
+ // If the stack is in split-screen secondary mode, we need to make sure we move the
+ // primary split-screen stack forward in the case it is currently behind a fullscreen
+ // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't
+ // cutting between them.
+ // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
+ final Task topFullScreenStack =
+ taskDisplayArea.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ if (topFullScreenStack != null) {
+ final Task primarySplitScreenStack =
+ taskDisplayArea.getRootSplitScreenPrimaryTask();
+ if (primarySplitScreenStack != null
+ && taskDisplayArea.getIndexOf(topFullScreenStack)
+ > taskDisplayArea.getIndexOf(primarySplitScreenStack)) {
+ primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
+ }
+ }
+ }
+
+ if (!isActivityTypeHome() && returnsToHomeStack()) {
+ // Make sure the home stack is behind this stack since that is where we should return to
+ // when this stack is no longer visible.
+ taskDisplayArea.moveHomeStackToFront(reason + " returnToHome");
+ }
+
+ if (isRootTask()) {
+ taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */,
+ reason);
+ }
+ if (task == null) {
+ task = this;
+ }
+ task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
+ }
+
+ /**
+ * This moves 'task' to the back of this task and also recursively moves this task to the back
+ * of its parents (if applicable).
+ *
+ * @param reason The reason for moving the stack to the back.
+ * @param task If non-null, the task will be moved to the bottom of the stack.
+ **/
+ void moveToBack(String reason, Task task) {
+ if (!isAttached()) {
+ return;
+ }
+ final TaskDisplayArea displayArea = getDisplayArea();
+ if (!mCreatedByOrganizer) {
+ // If this is just a normal task, so move to back of parent and then move 'task' to
+ // back of this.
+ final WindowContainer parent = getParent();
+ final Task parentTask = parent != null ? parent.asTask() : null;
+ if (parentTask != null) {
+ parentTask.moveToBack(reason, this);
+ } else {
+ displayArea.positionChildAt(POSITION_BOTTOM, this, false /*includingParents*/,
+ reason);
+ }
+ if (task != null && task != this) {
+ positionChildAtBottom(task);
+ }
+ return;
+ }
+ if (task == null || task == this) {
+ return;
+ }
+ // This is a created-by-organizer task. In this case, let the organizer deal with this
+ // task's ordering. However, we still need to move 'task' to back. The intention is that
+ // this ends up behind the home-task so that it is made invisible; so, if the home task
+ // is not a child of this, reparent 'task' to the back of the home task's actual parent.
+ displayArea.positionTaskBehindHome(task);
+ }
+
+ // TODO: Should each user have there own stacks?
+ @Override
+ void switchUser(int userId) {
+ if (mCurrentUser == userId) {
+ return;
+ }
+ mCurrentUser = userId;
+
+ super.switchUser(userId);
+ if (isLeafTask() && showToCurrentUser()) {
+ getParent().positionChildAt(POSITION_TOP, this, false /*includeParents*/);
+ }
+ }
+
+ void minimalResumeActivityLocked(ActivityRecord r) {
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + r + " (starting new instance)"
+ + " callers=" + Debug.getCallers(5));
+ r.setState(RESUMED, "minimalResumeActivityLocked");
+ r.completeResumeLocked();
+ }
+
+ private void clearLaunchTime(ActivityRecord r) {
+ // Make sure that there is no activity waiting for this to launch.
+ if (!mStackSupervisor.mWaitingActivityLaunched.isEmpty()) {
+ mStackSupervisor.removeIdleTimeoutForActivity(r);
+ mStackSupervisor.scheduleIdleTimeout(r);
+ }
+ }
+
+ void awakeFromSleepingLocked() {
+ // Ensure activities are no longer sleeping.
+ forAllActivities((Consumer<ActivityRecord>) (r) -> r.setSleeping(false));
+ if (mPausingActivity != null) {
+ Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
+ mPausingActivity.activityPaused(true);
+ }
+ }
+
+ void checkReadyForSleep() {
+ if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
+ mStackSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
+ }
+ }
+
+ /**
+ * Tries to put the activities in the stack to sleep.
+ *
+ * If the stack is not in a state where its activities can be put to sleep, this function will
+ * start any necessary actions to move the stack into such a state. It is expected that this
+ * function get called again when those actions complete.
+ *
+ * @param shuttingDown true when the called because the device is shutting down.
+ * @return true if the stack finished going to sleep, false if the stack only started the
+ * process of going to sleep (checkReadyForSleep will be called when that process finishes).
+ */
+ boolean goToSleepIfPossible(boolean shuttingDown) {
+ boolean shouldSleep = true;
+
+ if (mResumedActivity != null) {
+ // Still have something resumed; can't sleep until it is paused.
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep needs to pause " + mResumedActivity);
+ if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+ "Sleep => pause with userLeaving=false");
+
+ startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */);
+ shouldSleep = false ;
+ } else if (mPausingActivity != null) {
+ // Still waiting for something to pause; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
+ shouldSleep = false;
+ }
+
+ if (!shuttingDown) {
+ if (containsActivityFromStack(mStackSupervisor.mStoppingActivities)) {
+ // Still need to tell some activities to stop; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
+ + mStackSupervisor.mStoppingActivities.size() + " activities");
+
+ mStackSupervisor.scheduleIdle();
+ shouldSleep = false;
+ }
+ }
+
+ if (shouldSleep) {
+ goToSleep();
+ }
+
+ return shouldSleep;
+ }
+
+ void goToSleep() {
+ // Make sure all visible activities are now sleeping. This will update the activity's
+ // visibility and onStop() will be called.
+ forAllActivities((r) -> {
+ if (r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING, STOPPED)) {
+ r.setSleeping(true);
+ }
+ });
+
+ // Ensure visibility after updating sleep states without updating configuration,
+ // as activities are about to be sent to sleep.
+ ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS);
+ }
+
+ private boolean containsActivityFromStack(List<ActivityRecord> rs) {
+ for (ActivityRecord r : rs) {
+ if (r.getRootTask() == this) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Start pausing the currently resumed activity. It is an error to call this if there
+ * is already an activity being paused or there is no resumed activity.
+ *
+ * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+ * @param uiSleeping True if this is happening with the user interface going to sleep (the
+ * screen turning off).
+ * @param resuming The activity we are currently trying to resume or null if this is not being
+ * called as part of resuming the top activity, so we shouldn't try to instigate
+ * a resume here if not null.
+ * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+ * it to tell us when it is done.
+ */
+ final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
+ ActivityRecord resuming) {
+ if (mPausingActivity != null) {
+ Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ + " state=" + mPausingActivity.getState());
+ if (!shouldSleepActivities()) {
+ // Avoid recursion among check for sleep and complete pause during sleeping.
+ // Because activity will be paused immediately after resume, just let pause
+ // be completed by the order of activity paused from clients.
+ completePauseLocked(false, resuming);
+ }
+ }
+ ActivityRecord prev = mResumedActivity;
+
+ if (prev == null) {
+ if (resuming == null) {
+ Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
+ return false;
+ }
+
+ if (prev == resuming) {
+ Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
+ return false;
+ }
+
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSING: " + prev);
+ else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
+ mPausingActivity = prev;
+ mLastPausedActivity = prev;
+ mLastNoHistoryActivity = prev.isNoHistory() ? prev : null;
+ prev.setState(PAUSING, "startPausingLocked");
+ prev.getTask().touchActiveTime();
+ clearLaunchTime(prev);
+
+ mAtmService.updateCpuStats();
+
+ boolean pauseImmediately = false;
+ if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
+ // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
+ // activity to be paused, while at the same time resuming the new resume activity
+ // only if the previous activity can't go into Pip since we want to give Pip
+ // activities a chance to enter Pip before resuming the next activity.
+ final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
+ "shouldResumeWhilePausing", userLeaving);
+ if (!lastResumedCanPip) {
+ pauseImmediately = true;
+ }
+ }
+
+ if (prev.attachedToProcess()) {
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
+ try {
+ EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
+ prev.shortComponentName, "userLeaving=" + userLeaving);
+
+ mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+ prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
+ prev.configChangeFlags, pauseImmediately));
+ } catch (Exception e) {
+ // Ignore exception, if process died other code will cleanup.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mLastNoHistoryActivity = null;
+ }
+ } else {
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mLastNoHistoryActivity = null;
+ }
+
+ // If we are not going to sleep, we want to ensure the device is
+ // awake until the next activity is started.
+ if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
+ mStackSupervisor.acquireLaunchWakelock();
+ }
+
+ if (mPausingActivity != null) {
+ // Have the window manager pause its key dispatching until the new
+ // activity has started. If we're pausing the activity just because
+ // the screen is being turned off and the UI is sleeping, don't interrupt
+ // key dispatch; the same activity will pick it up again on wakeup.
+ if (!uiSleeping) {
+ prev.pauseKeyDispatchingLocked();
+ } else if (DEBUG_PAUSE) {
+ Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off");
+ }
+
+ if (pauseImmediately) {
+ // If the caller said they don't want to wait for the pause, then complete
+ // the pause now.
+ completePauseLocked(false, resuming);
+ return false;
+
+ } else {
+ prev.schedulePauseTimeout();
+ return true;
+ }
+
+ } else {
+ // This activity failed to schedule the
+ // pause, so just treat it as being paused now.
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
+ if (resuming == null) {
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
+ return false;
+ }
+ }
+
+ @VisibleForTesting
+ void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
+ ActivityRecord prev = mPausingActivity;
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);
+
+ if (prev != null) {
+ prev.setWillCloseOrEnterPip(false);
+ final boolean wasStopping = prev.isState(STOPPING);
+ prev.setState(PAUSED, "completePausedLocked");
+ if (prev.finishing) {
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
+ prev = prev.completeFinishing("completePausedLocked");
+ } else if (prev.hasProcess()) {
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
+ + " wasStopping=" + wasStopping
+ + " visibleRequested=" + prev.mVisibleRequested);
+ if (prev.deferRelaunchUntilPaused) {
+ // Complete the deferred relaunch that was waiting for pause to complete.
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
+ prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
+ } else if (wasStopping) {
+ // We are also stopping, the stop request must have gone soon after the pause.
+ // We can't clobber it, because the stop confirmation will not be handled.
+ // We don't need to schedule another stop, we only need to let it happen.
+ prev.setState(STOPPING, "completePausedLocked");
+ } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
+ // Clear out any deferred client hide we might currently have.
+ prev.setDeferHidingClient(false);
+ // If we were visible then resumeTopActivities will release resources before
+ // stopping.
+ prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
+ "completePauseLocked");
+ }
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);
+ prev = null;
+ }
+ // It is possible the activity was freezing the screen before it was paused.
+ // In that case go ahead and remove the freeze this activity has on the screen
+ // since it is no longer visible.
+ if (prev != null) {
+ prev.stopFreezingScreenLocked(true /*force*/);
+ }
+ mPausingActivity = null;
+ }
+
+ if (resumeNext) {
+ final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack();
+ if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {
+ mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
+ } else {
+ checkReadyForSleep();
+ final ActivityRecord top = topStack != null ? topStack.topRunningActivity() : null;
+ if (top == null || (prev != null && top != prev)) {
+ // If there are no more activities available to run, do resume anyway to start
+ // something. Also if the top activity on the stack is not the just paused
+ // activity, we need to go ahead and resume it to ensure we complete an
+ // in-flight app switch.
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
+ }
+ }
+
+ if (prev != null) {
+ prev.resumeKeyDispatchingLocked();
+
+ if (prev.hasProcess() && prev.cpuTimeAtResume > 0) {
+ final long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
+ if (diff > 0) {
+ final Runnable r = PooledLambda.obtainRunnable(
+ ActivityManagerInternal::updateForegroundTimeIfOnBattery,
+ mAtmService.mAmInternal, prev.info.packageName,
+ prev.info.applicationInfo.uid,
+ diff);
+ mAtmService.mH.post(r);
+ }
+ }
+ prev.cpuTimeAtResume = 0; // reset it
+ }
+
+ mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
+
+ // Notify when the task stack has changed, but only if visibilities changed (not just
+ // focus). Also if there is an active pinned stack - we always want to notify it about
+ // task stack changes, because its positioning may depend on it.
+ if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
+ || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
+ mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
+ mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
+ }
+ }
+
+ boolean isTopStackInDisplayArea() {
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ return taskDisplayArea != null && taskDisplayArea.isTopStack(this);
+ }
+
+ /**
+ * @return {@code true} if this is the focused stack on its current display, {@code false}
+ * otherwise.
+ */
+ boolean isFocusedStackOnDisplay() {
+ final DisplayContent display = getDisplay();
+ return display != null && this == display.getFocusedStack();
+ }
+
+ /**
+ * Make sure that all activities that need to be visible in the stack (that is, they
+ * currently can be seen by the user) actually are and update their configuration.
+ * @param starting The top most activity in the task.
+ * The activity is either starting or resuming.
+ * Caller should ensure starting activity is visible.
+ * @param preserveWindows Flag indicating whether windows should be preserved when updating
+ * configuration in {@link mEnsureActivitiesVisibleHelper}.
+ * @param configChanges Parts of the configuration that changed for this activity for evaluating
+ * if the screen should be frozen as part of
+ * {@link mEnsureActivitiesVisibleHelper}.
+ *
+ */
+ void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
+ boolean preserveWindows) {
+ ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
+ }
+
+ /**
+ * Ensure visibility with an option to also update the configuration of visible activities.
+ * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
+ * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
+ * @param starting The top most activity in the task.
+ * The activity is either starting or resuming.
+ * Caller should ensure starting activity is visible.
+ * @param notifyClients Flag indicating whether the visibility updates should be sent to the
+ * clients in {@link mEnsureActivitiesVisibleHelper}.
+ * @param preserveWindows Flag indicating whether windows should be preserved when updating
+ * configuration in {@link mEnsureActivitiesVisibleHelper}.
+ * @param configChanges Parts of the configuration that changed for this activity for evaluating
+ * if the screen should be frozen as part of
+ * {@link mEnsureActivitiesVisibleHelper}.
+ */
+ // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
+ void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
+ boolean preserveWindows, boolean notifyClients) {
+ mTopActivityOccludesKeyguard = false;
+ mTopDismissingKeyguardActivity = null;
+ mStackSupervisor.beginActivityVisibilityUpdate();
+ try {
+ mEnsureActivitiesVisibleHelper.process(
+ starting, configChanges, preserveWindows, notifyClients);
+
+ if (mTranslucentActivityWaiting != null &&
+ mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
+ // Nothing is getting drawn or everything was already visible, don't wait for timeout.
+ notifyActivityDrawnLocked(null);
+ }
+ } finally {
+ mStackSupervisor.endActivityVisibilityUpdate();
+ }
+ }
+
+ /**
+ * @return true if the top visible activity wants to occlude the Keyguard, false otherwise
+ */
+ boolean topActivityOccludesKeyguard() {
+ return mTopActivityOccludesKeyguard;
+ }
+
+ /**
+ * Returns true if this stack should be resized to match the bounds specified by
+ * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
+ */
+ boolean shouldResizeStackWithLaunchBounds() {
+ return inPinnedWindowingMode();
+ }
+
+ // TODO(NOW!)
+ /**
+ * Returns {@code true} if this is the top-most split-screen-primary or
+ * split-screen-secondary stack, {@code false} otherwise.
+ */
+ boolean isTopSplitScreenStack() {
+ return inSplitScreenWindowingMode()
+ && this == getDisplayArea().getTopStackInWindowingMode(getWindowingMode());
+ }
+
+ /**
+ * @return the top most visible activity that wants to dismiss Keyguard
+ */
+ ActivityRecord getTopDismissingKeyguardActivity() {
+ return mTopDismissingKeyguardActivity;
+ }
+
+ /**
+ * Checks whether {@param r} should be visible depending on Keyguard state and updates
+ * {@link #mTopActivityOccludesKeyguard} and {@link #mTopDismissingKeyguardActivity} if
+ * necessary.
+ *
+ * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
+ */
+ boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) {
+ int displayId = getDisplayId();
+ if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
+
+ final boolean keyguardOrAodShowing = mStackSupervisor.getKeyguardController()
+ .isKeyguardOrAodShowing(displayId);
+ final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
+ final boolean showWhenLocked = r.canShowWhenLocked();
+ final boolean dismissKeyguard = r.containsDismissKeyguardWindow();
+ if (shouldBeVisible) {
+ if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
+ mTopDismissingKeyguardActivity = r;
+ }
+
+ // Only the top activity may control occluded, as we can't occlude the Keyguard if the
+ // top app doesn't want to occlude it.
+ if (isTop) {
+ mTopActivityOccludesKeyguard |= showWhenLocked;
+ }
+
+ final boolean canShowWithKeyguard = canShowWithInsecureKeyguard()
+ && mStackSupervisor.getKeyguardController().canDismissKeyguard();
+ if (canShowWithKeyguard) {
+ return true;
+ }
+ }
+ if (keyguardOrAodShowing) {
+ // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
+ // right away and AOD isn't visible.
+ return shouldBeVisible && mStackSupervisor.getKeyguardController()
+ .canShowActivityWhileKeyguardShowing(r, dismissKeyguard);
+ } else if (keyguardLocked) {
+ return shouldBeVisible && mStackSupervisor.getKeyguardController().canShowWhileOccluded(
+ dismissKeyguard, showWhenLocked);
+ } else {
+ return shouldBeVisible;
+ }
+ }
+
+ /**
+ * Check if the display to which this stack is attached has
+ * {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
+ */
+ boolean canShowWithInsecureKeyguard() {
+ final DisplayContent displayContent = getDisplay();
+ if (displayContent == null) {
+ throw new IllegalStateException("Stack is not attached to any display, stackId="
+ + getRootTaskId());
+ }
+
+ final int flags = displayContent.mDisplay.getFlags();
+ return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
+ }
+
+ void checkTranslucentActivityWaiting(ActivityRecord top) {
+ if (mTranslucentActivityWaiting != top) {
+ mUndrawnActivitiesBelowTopTranslucent.clear();
+ if (mTranslucentActivityWaiting != null) {
+ // Call the callback with a timeout indication.
+ notifyActivityDrawnLocked(null);
+ mTranslucentActivityWaiting = null;
+ }
+ mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+ }
+ }
+
+ void convertActivityToTranslucent(ActivityRecord r) {
+ mTranslucentActivityWaiting = r;
+ mUndrawnActivitiesBelowTopTranslucent.clear();
+ mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
+ }
+
+ /**
+ * Called as activities below the top translucent activity are redrawn. When the last one is
+ * redrawn notify the top activity by calling
+ * {@link Activity#onTranslucentConversionComplete}.
+ *
+ * @param r The most recent background activity to be drawn. Or, if r is null then a timeout
+ * occurred and the activity will be notified immediately.
+ */
+ void notifyActivityDrawnLocked(ActivityRecord r) {
+ if ((r == null)
+ || (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
+ mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
+ // The last undrawn activity below the top has just been drawn. If there is an
+ // opaque activity at the top, notify it that it can become translucent safely now.
+ final ActivityRecord waitingActivity = mTranslucentActivityWaiting;
+ mTranslucentActivityWaiting = null;
+ mUndrawnActivitiesBelowTopTranslucent.clear();
+ mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+
+ if (waitingActivity != null) {
+ mWmService.setWindowOpaqueLocked(waitingActivity.appToken, false);
+ if (waitingActivity.attachedToProcess()) {
+ try {
+ waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
+ waitingActivity.appToken, r != null);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+ }
+
+ /** @see ActivityRecord#cancelInitializing() */
+ void cancelInitializingActivities() {
+ // We don't want to clear starting window for activities that aren't behind fullscreen
+ // activities as we need to display their starting window until they are done initializing.
+ checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
+ }
+
+ /**
+ * If an activity {@param toCheck} is given, this method returns {@code true} if the activity
+ * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
+ * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
+ * activities to the function.
+ */
+ boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
+ Consumer<ActivityRecord> handleBehindFullscreenActivity) {
+ return mCheckBehindFullscreenActivityHelper.process(
+ toCheck, handleBehindFullscreenActivity);
+ }
+
+ /**
+ * Ensure that the top activity in the stack is resumed.
+ *
+ * @param prev The previously resumed activity, for when in the process
+ * of pausing; can be null to call from elsewhere.
+ * @param options Activity options.
+ *
+ * @return Returns true if something is being resumed, or false if
+ * nothing happened.
+ *
+ * NOTE: It is not safe to call this method directly as it can cause an activity in a
+ * non-focused stack to be resumed.
+ * Use {@link RootWindowContainer#resumeFocusedStacksTopActivities} to resume the
+ * right activity for the current system state.
+ */
+ @GuardedBy("mService")
+ boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
+ if (mInResumeTopActivity) {
+ // Don't even start recursing.
+ return false;
+ }
+
+ boolean result = false;
+ try {
+ // Protect against recursion.
+ mInResumeTopActivity = true;
+ result = resumeTopActivityInnerLocked(prev, options);
+
+ // When resuming the top activity, it may be necessary to pause the top activity (for
+ // example, returning to the lock screen. We suppress the normal pause logic in
+ // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the
+ // end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here
+ // to ensure any necessary pause logic occurs. In the case where the Activity will be
+ // shown regardless of the lock screen, the call to
+ // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.
+ final ActivityRecord next = topRunningActivity(true /* focusableOnly */);
+ if (next == null || !next.canTurnScreenOn()) {
+ checkReadyForSleep();
+ }
+ } finally {
+ mInResumeTopActivity = false;
+ }
+
+ return result;
+ }
+
+ @GuardedBy("mService")
+ private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
+ if (!mAtmService.isBooting() && !mAtmService.isBooted()) {
+ // Not ready yet!
+ return false;
+ }
+
+ // Find the next top-most activity to resume in this stack that is not finishing and is
+ // focusable. If it is not focusable, we will fall into the case below to resume the
+ // top activity in the next focusable task.
+ ActivityRecord next = topRunningActivity(true /* focusableOnly */);
+
+ final boolean hasRunningActivity = next != null;
+
+ // TODO: Maybe this entire condition can get removed?
+ if (hasRunningActivity && !isAttached()) {
+ return false;
+ }
+
+ mRootWindowContainer.cancelInitializingActivities();
+
+ // Remember how we'll process this pause/resume situation, and ensure
+ // that the state is reset however we wind up proceeding.
+ boolean userLeaving = mStackSupervisor.mUserLeaving;
+ mStackSupervisor.mUserLeaving = false;
+
+ if (!hasRunningActivity) {
+ // There are no activities left in the stack, let's look somewhere else.
+ return resumeNextFocusableActivityWhenStackIsEmpty(prev, options);
+ }
+
+ next.delayedResume = false;
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+
+ // If the top activity is the resumed one, nothing to do.
+ if (mResumedActivity == next && next.isState(RESUMED)
+ && taskDisplayArea.allResumedActivitiesComplete()) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ if (DEBUG_STATES) Slog.d(TAG_STATES,
+ "resumeTopActivityLocked: Top activity resumed " + next);
+ return false;
+ }
+
+ if (!next.canResumeByCompat()) {
+ return false;
+ }
+
+ // If we are currently pausing an activity, then don't do anything until that is done.
+ final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
+ if (!allPausedComplete) {
+ if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) {
+ Slog.v(TAG_PAUSE, "resumeTopActivityLocked: Skip resume: some activity pausing.");
+ }
+ return false;
+ }
+
+ // If we are sleeping, and there is no resumed activity, and the top activity is paused,
+ // well that is the state we want.
+ if (shouldSleepOrShutDownActivities()
+ && mLastPausedActivity == next
+ && mRootWindowContainer.allPausedActivitiesComplete()) {
+ // If the current top activity may be able to occlude keyguard but the occluded state
+ // has not been set, update visibility and check again if we should continue to resume.
+ boolean nothingToResume = true;
+ if (!mAtmService.mShuttingDown) {
+ final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard
+ && next.canShowWhenLocked();
+ final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next
+ && next.containsDismissKeyguardWindow();
+
+ if (canShowWhenLocked || mayDismissKeyguard) {
+ ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS);
+ nothingToResume = shouldSleepActivities();
+ } else if (next.currentLaunchCanTurnScreenOn() && next.canTurnScreenOn()) {
+ nothingToResume = false;
+ }
+ }
+ if (nothingToResume) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ if (DEBUG_STATES) Slog.d(TAG_STATES,
+ "resumeTopActivityLocked: Going to sleep and all paused");
+ return false;
+ }
+ }
+
+ // Make sure that the user who owns this activity is started. If not,
+ // we will just leave it as is because someone should be bringing
+ // another user's activities to the top of the stack.
+ if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
+ Slog.w(TAG, "Skipping resume of top activity " + next
+ + ": user " + next.mUserId + " is stopped");
+ return false;
+ }
+
+ // The activity may be waiting for stop, but that is no longer
+ // appropriate for it.
+ mStackSupervisor.mStoppingActivities.remove(next);
+ next.setSleeping(false);
+
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
+
+ // If we are currently pausing an activity, then don't do anything until that is done.
+ if (!mRootWindowContainer.allPausedActivitiesComplete()) {
+ if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
+ "resumeTopActivityLocked: Skip resume: some activity pausing.");
+
+ return false;
+ }
+
+ mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
+
+ ActivityRecord lastResumed = null;
+ final Task lastFocusedStack = taskDisplayArea.getLastFocusedStack();
+ if (lastFocusedStack != null && lastFocusedStack != this) {
+ // So, why aren't we using prev here??? See the param comment on the method. prev
+ // doesn't represent the last resumed activity. However, the last focus stack does if
+ // it isn't null.
+ lastResumed = lastFocusedStack.mResumedActivity;
+ if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {
+ // The user isn't leaving if this stack is the multi-window mode and the last
+ // focused stack should still be visible.
+ if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
+ + " next=" + next + " lastResumed=" + lastResumed);
+ userLeaving = false;
+ }
+ }
+
+ boolean pausing = taskDisplayArea.pauseBackStacks(userLeaving, next);
+ if (mResumedActivity != null) {
+ if (DEBUG_STATES) Slog.d(TAG_STATES,
+ "resumeTopActivityLocked: Pausing " + mResumedActivity);
+ pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next);
+ }
+ if (pausing) {
+ if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
+ "resumeTopActivityLocked: Skip resume: need to start pausing");
+ // At this point we want to put the upcoming activity's process
+ // at the top of the LRU list, since we know we will be needing it
+ // very soon and it would be a waste to let it get killed if it
+ // happens to be sitting towards the end.
+ if (next.attachedToProcess()) {
+ next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* activityChange */, false /* updateOomAdj */,
+ false /* addPendingTopUid */);
+ } else if (!next.isProcessRunning()) {
+ // Since the start-process is asynchronous, if we already know the process of next
+ // activity isn't running, we can start the process earlier to save the time to wait
+ // for the current activity to be paused.
+ final boolean isTop = this == taskDisplayArea.getFocusedStack();
+ mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
+ isTop ? "pre-top-activity" : "pre-activity");
+ }
+ if (lastResumed != null) {
+ lastResumed.setWillCloseOrEnterPip(true);
+ }
+ return true;
+ } else if (mResumedActivity == next && next.isState(RESUMED)
+ && taskDisplayArea.allResumedActivitiesComplete()) {
+ // It is possible for the activity to be resumed when we paused back stacks above if the
+ // next activity doesn't have to wait for pause to complete.
+ // So, nothing else to-do except:
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ if (DEBUG_STATES) Slog.d(TAG_STATES,
+ "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
+ return true;
+ }
+
+ // If the most recent activity was noHistory but was only stopped rather
+ // than stopped+finished because the device went to sleep, we need to make
+ // sure to finish it as we're making a new activity topmost.
+ if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
+ !mLastNoHistoryActivity.finishing) {
+ if (DEBUG_STATES) Slog.d(TAG_STATES,
+ "no-history finish of " + mLastNoHistoryActivity + " on new resume");
+ mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
+ mLastNoHistoryActivity = null;
+ }
+
+ if (prev != null && prev != next && next.nowVisible) {
+
+ // The next activity is already visible, so hide the previous
+ // activity's windows right now so we can show the new one ASAP.
+ // We only do this if the previous is finishing, which should mean
+ // it is on top of the one being resumed so hiding it quickly
+ // is good. Otherwise, we want to do the normal route of allowing
+ // the resumed activity to be shown so we can decide if the
+ // previous should actually be hidden depending on whether the
+ // new one is found to be full-screen or not.
+ if (prev.finishing) {
+ prev.setVisibility(false);
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+ "Not waiting for visible to hide: " + prev
+ + ", nowVisible=" + next.nowVisible);
+ } else {
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+ "Previous already visible but still waiting to hide: " + prev
+ + ", nowVisible=" + next.nowVisible);
+ }
+
+ }
+
+ // Launching this app's activity, make sure the app is no longer
+ // considered stopped.
+ try {
+ mAtmService.getPackageManager().setPackageStoppedState(
+ next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
+ } catch (RemoteException e1) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + next.packageName + ": " + e);
+ }
+
+ // We are starting up the next activity, so tell the window manager
+ // that the previous one will be hidden soon. This way it can know
+ // to ignore it when computing the desired screen orientation.
+ boolean anim = true;
+ final DisplayContent dc = taskDisplayArea.mDisplayContent;
+ if (prev != null) {
+ if (prev.finishing) {
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
+ "Prepare close transition: prev=" + prev);
+ if (mStackSupervisor.mNoAnimActivities.contains(prev)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE, false);
+ } else {
+ dc.prepareAppTransition(
+ prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE
+ : TRANSIT_TASK_CLOSE, false);
+ }
+ prev.setVisibility(false);
+ } else {
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
+ "Prepare open transition: prev=" + prev);
+ if (mStackSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE, false);
+ } else {
+ dc.prepareAppTransition(
+ prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN
+ : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND
+ : TRANSIT_TASK_OPEN, false);
+ }
+ }
+ } else {
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
+ if (mStackSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE, false);
+ } else {
+ dc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
+ }
+ }
+
+ if (anim) {
+ next.applyOptionsLocked();
+ } else {
+ next.clearOptionsLocked();
+ }
+
+ mStackSupervisor.mNoAnimActivities.clear();
+
+ if (next.attachedToProcess()) {
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
+ + " stopped=" + next.stopped
+ + " visibleRequested=" + next.mVisibleRequested);
+
+ // If the previous activity is translucent, force a visibility update of
+ // the next activity, so that it's added to WM's opening app list, and
+ // transition animation can be set up properly.
+ // For example, pressing Home button with a translucent activity in focus.
+ // Launcher is already visible in this case. If we don't add it to opening
+ // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
+ // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
+ final boolean lastActivityTranslucent = lastFocusedStack != null
+ && (lastFocusedStack.inMultiWindowMode()
+ || (lastFocusedStack.mLastPausedActivity != null
+ && !lastFocusedStack.mLastPausedActivity.occludesParent()));
+
+ // This activity is now becoming visible.
+ if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
+ next.setVisibility(true);
+ }
+
+ // schedule launch ticks to collect information about slow apps.
+ next.startLaunchTickingLocked();
+
+ ActivityRecord lastResumedActivity =
+ lastFocusedStack == null ? null : lastFocusedStack.mResumedActivity;
+ final ActivityState lastState = next.getState();
+
+ mAtmService.updateCpuStats();
+
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
+ + " (in existing)");
+
+ next.setState(RESUMED, "resumeTopActivityInnerLocked");
+
+ next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* activityChange */, true /* updateOomAdj */,
+ true /* addPendingTopUid */);
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order.
+ boolean notUpdated = true;
+
+ // Activity should also be visible if set mLaunchTaskBehind to true (see
+ // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
+ if (shouldBeVisible(next)) {
+ // We have special rotation behavior when here is some active activity that
+ // requests specific orientation or Keyguard is locked. Make sure all activity
+ // visibilities are set correctly as well as the transition is updated if needed
+ // to get the correct rotation behavior. Otherwise the following call to update
+ // the orientation may cause incorrect configurations delivered to client as a
+ // result of invisible window resize.
+ // TODO: Remove this once visibilities are set correctly immediately when
+ // starting an activity.
+ notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+ true /* markFrozenIfConfigChanged */, false /* deferResume */);
+ }
+
+ if (notUpdated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivity();
+ if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
+ "Activity config changed during resume: " + next
+ + ", new next: " + nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mStackSupervisor.scheduleResumeTopActivities();
+ }
+ if (!next.mVisibleRequested || next.stopped) {
+ next.setVisibility(true);
+ }
+ next.completeResumeLocked();
+ return true;
+ }
+
+ try {
+ final ClientTransaction transaction =
+ ClientTransaction.obtain(next.app.getThread(), next.appToken);
+ // Deliver all pending results.
+ ArrayList<ResultInfo> a = next.results;
+ if (a != null) {
+ final int N = a.size();
+ if (!next.finishing && N > 0) {
+ if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
+ "Delivering results to " + next + ": " + a);
+ transaction.addCallback(ActivityResultItem.obtain(a));
+ }
+ }
+
+ if (next.newIntents != null) {
+ transaction.addCallback(
+ NewIntentItem.obtain(next.newIntents, true /* resume */));
+ }
+
+ // Well the app will no longer be stopped.
+ // Clear app token stopped state in window manager if needed.
+ next.notifyAppResumed(next.stopped);
+
+ EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
+ next.getTask().mTaskId, next.shortComponentName);
+
+ next.setSleeping(false);
+ mAtmService.getAppWarningsLocked().onResumeActivity(next);
+ next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
+ next.clearOptionsLocked();
+ transaction.setLifecycleStateRequest(
+ ResumeActivityItem.obtain(next.app.getReportedProcState(),
+ dc.isNextTransitionForward()));
+ mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+
+ if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ + next);
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
+ + lastState + ": " + next);
+ next.setState(lastState, "resumeTopActivityInnerLocked");
+
+ // lastResumedActivity being non-null implies there is a lastStack present.
+ if (lastResumedActivity != null) {
+ lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
+ }
+
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedStack != null
+ && lastFocusedStack.isTopStackInDisplayArea()) {
+ next.showStartingWindow(null /* prev */, false /* newTask */,
+ false /* taskSwitch */);
+ }
+ mStackSupervisor.startSpecificActivity(next, true, false);
+ return true;
+ }
+
+ // From this point on, if something goes wrong there is no way
+ // to recover the activity.
+ try {
+ next.completeResumeLocked();
+ } catch (Exception e) {
+ // If any exception gets thrown, toss away this
+ // activity and try the next one.
+ Slog.w(TAG, "Exception thrown during resume of " + next, e);
+ next.finishIfPossible("resume-exception", true /* oomAdj */);
+ return true;
+ }
+ } else {
+ // Whoops, need to restart this activity!
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW) {
+ next.showStartingWindow(null /* prev */, false /* newTask */,
+ false /* taskSwich */);
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
+ }
+ if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
+ mStackSupervisor.startSpecificActivity(next, true, true);
+ }
+
+ return true;
+ }
+
+ /**
+ * Resume the next eligible activity in a focusable stack when this one does not have any
+ * running activities left. The focus will be adjusted to the next focusable stack and
+ * top running activities will be resumed in all focusable stacks. However, if the current stack
+ * is a home stack - we have to keep it focused, start and resume a home activity on the current
+ * display instead to make sure that the display is not empty.
+ */
+ private boolean resumeNextFocusableActivityWhenStackIsEmpty(ActivityRecord prev,
+ ActivityOptions options) {
+ final String reason = "noMoreActivities";
+
+ if (!isActivityTypeHome()) {
+ final Task nextFocusedStack = adjustFocusToNextFocusableTask(reason);
+ if (nextFocusedStack != null) {
+ // Try to move focus to the next visible stack with a running activity if this
+ // stack is not covering the entire screen or is on a secondary display with no home
+ // stack.
+ return mRootWindowContainer.resumeFocusedStacksTopActivities(nextFocusedStack,
+ prev, null /* targetOptions */);
+ }
+ }
+
+ // If the current stack is a home stack, or if focus didn't switch to a different stack -
+ // just start up the Launcher...
+ ActivityOptions.abort(options);
+ if (DEBUG_STATES) Slog.d(TAG_STATES,
+ "resumeNextFocusableActivityWhenStackIsEmpty: " + reason + ", go home");
+ return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea());
+ }
+
+ void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
+ boolean newTask, boolean keepCurTransition, ActivityOptions options) {
+ Task rTask = r.getTask();
+ final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
+ final boolean isOrhasTask = rTask == this || hasChild(rTask);
+ // mLaunchTaskBehind tasks get placed at the back of the task stack.
+ if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) {
+ // Last activity in task had been removed or ActivityManagerService is reusing task.
+ // Insert or replace.
+ // Might not even be in.
+ positionChildAtTop(rTask);
+ }
+ Task task = null;
+ if (!newTask && isOrhasTask) {
+ // Starting activity cannot be occluding activity, otherwise starting window could be
+ // remove immediately without transferring to starting activity.
+ final ActivityRecord occludingActivity = getOccludingActivityAbove(r);
+ if (occludingActivity != null) {
+ // Here it is! Now, if this is not yet visible (occluded by another task) to the
+ // user, then just add it without starting; it will get started when the user
+ // navigates back to it.
+ if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task,
+ new RuntimeException("here").fillInStackTrace());
+ rTask.positionChildAtTop(r);
+ ActivityOptions.abort(options);
+ return;
+ }
+ }
+
+ // Place a new activity at top of stack, so it is next to interact with the user.
+
+ // If we are not placing the new activity frontmost, we do not want to deliver the
+ // onUserLeaving callback to the actual frontmost activity
+ final Task activityTask = r.getTask();
+ if (task == activityTask && mChildren.indexOf(task) != (getChildCount() - 1)) {
+ mStackSupervisor.mUserLeaving = false;
+ if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+ "startActivity() behind front, mUserLeaving=false");
+ }
+
+ task = activityTask;
+
+ // Slot the activity into the history stack and proceed
+ if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
+ new RuntimeException("here").fillInStackTrace());
+ task.positionChildAtTop(r);
+
+ // The transition animation and starting window are not needed if {@code allowMoveToFront}
+ // is false, because the activity won't be visible.
+ if ((!isHomeOrRecentsStack() || hasActivity()) && allowMoveToFront) {
+ final DisplayContent dc = getDisplay().mDisplayContent;
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
+ "Prepare open transition: starting " + r);
+ if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ dc.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
+ mStackSupervisor.mNoAnimActivities.add(r);
+ } else {
+ int transit = TRANSIT_ACTIVITY_OPEN;
+ if (newTask) {
+ if (r.mLaunchTaskBehind) {
+ transit = TRANSIT_TASK_OPEN_BEHIND;
+ } else if (getDisplay().isSingleTaskInstance()) {
+ // If a new task is being launched in a single task display, we don't need
+ // to play normal animation, but need to trigger a callback when an app
+ // transition is actually handled. So ignore already prepared activity, and
+ // override it.
+ transit = TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
+ keepCurTransition = false;
+ } else {
+ // If a new task is being launched, then mark the existing top activity as
+ // supporting picture-in-picture while pausing only if the starting activity
+ // would not be considered an overlay on top of the current activity
+ // (eg. not fullscreen, or the assistant)
+ if (canEnterPipOnTaskSwitch(focusedTopActivity,
+ null /* toFrontTask */, r, options)) {
+ focusedTopActivity.supportsEnterPipOnTaskSwitch = true;
+ }
+ transit = TRANSIT_TASK_OPEN;
+ }
+ }
+ dc.prepareAppTransition(transit, keepCurTransition);
+ mStackSupervisor.mNoAnimActivities.remove(r);
+ }
+ boolean doShow = true;
+ if (newTask) {
+ // Even though this activity is starting fresh, we still need
+ // to reset it to make sure we apply affinities to move any
+ // existing activities from other tasks in to it.
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ resetTaskIfNeeded(r, r);
+ doShow = topRunningNonDelayedActivityLocked(null) == r;
+ }
+ } else if (options != null && options.getAnimationType()
+ == ActivityOptions.ANIM_SCENE_TRANSITION) {
+ doShow = false;
+ }
+ if (r.mLaunchTaskBehind) {
+ // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
+ // tell WindowManager that r is visible even though it is at the back of the stack.
+ r.setVisibility(true);
+ ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ // Go ahead to execute app transition for this activity since the app transition
+ // will not be triggered through the resume channel.
+ getDisplay().mDisplayContent.executeAppTransition();
+ } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
+ // Figure out if we are transitioning from another activity that is
+ // "has the same starting icon" as the next one. This allows the
+ // window manager to keep the previous window it had previously
+ // created, if it still had one.
+ Task prevTask = r.getTask();
+ ActivityRecord prev = prevTask.topActivityWithStartingWindow();
+ if (prev != null) {
+ // We don't want to reuse the previous starting preview if:
+ // (1) The current activity is in a different task.
+ if (prev.getTask() != prevTask) {
+ prev = null;
+ }
+ // (2) The current activity is already displayed.
+ else if (prev.nowVisible) {
+ prev = null;
+ }
+ }
+ r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
+ }
+ } else {
+ // If this is the first activity, don't do any fancy animations,
+ // because there is nothing for it to animate on top of.
+ ActivityOptions.abort(options);
+ }
+ }
+
+ /**
+ * @return Whether the switch to another task can trigger the currently running activity to
+ * enter PiP while it is pausing (if supported). Only one of {@param toFrontTask} or
+ * {@param toFrontActivity} should be set.
+ */
+ private boolean canEnterPipOnTaskSwitch(ActivityRecord pipCandidate,
+ Task toFrontTask, ActivityRecord toFrontActivity, ActivityOptions opts) {
+ if (opts != null && opts.disallowEnterPictureInPictureWhileLaunching()) {
+ // Ensure the caller has requested not to trigger auto-enter PiP
+ return false;
+ }
+ if (pipCandidate == null || pipCandidate.inPinnedWindowingMode()) {
+ // Ensure that we do not trigger entering PiP an activity on the pinned stack
+ return false;
+ }
+ final Task targetStack = toFrontTask != null
+ ? toFrontTask.getRootTask() : toFrontActivity.getRootTask();
+ if (targetStack != null && targetStack.isActivityTypeAssistant()) {
+ // Ensure the task/activity being brought forward is not the assistant
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) {
+ return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
+ }
+
+ /**
+ * Reset the task by reparenting the activities that have same affinity to the task or
+ * reparenting the activities that have different affinityies out of the task, while these
+ * activities allow task reparenting.
+ *
+ * @param taskTop Top activity of the task might be reset.
+ * @param newActivity The activity that going to be started.
+ * @return The non-finishing top activity of the task after reset or the original task top
+ * activity if all activities within the task are finishing.
+ */
+ ActivityRecord resetTaskIfNeeded(ActivityRecord taskTop, ActivityRecord newActivity) {
+ final boolean forceReset =
+ (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
+ final Task task = taskTop.getTask();
+
+ // If ActivityOptions are moved out and need to be aborted or moved to taskTop.
+ final ActivityOptions topOptions = sResetTargetTaskHelper.process(task, forceReset);
+
+ if (mChildren.contains(task)) {
+ final ActivityRecord newTop = task.getTopNonFinishingActivity();
+ if (newTop != null) {
+ taskTop = newTop;
+ }
+ }
+
+ if (topOptions != null) {
+ // If we got some ActivityOptions from an activity on top that
+ // was removed from the task, propagate them to the new real top.
+ taskTop.updateOptionsLocked(topOptions);
+ }
+
+ return taskTop;
+ }
+
+ /**
+ * Finish the topmost activity that belongs to the crashed app. We may also finish the activity
+ * that requested launch of the crashed one to prevent launch-crash loop.
+ * @param app The app that crashed.
+ * @param reason Reason to perform this action.
+ * @return The task that was finished in this stack, {@code null} if top running activity does
+ * not belong to the crashed app.
+ */
+ final Task finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
+ final ActivityRecord r = topRunningActivity();
+ if (r == null || r.app != app) {
+ return null;
+ }
+ if (r.isActivityTypeHome() && mAtmService.mHomeProcess == app) {
+ // Home activities should not be force-finished as we have nothing else to go
+ // back to. AppErrors will get to it after two crashes in MIN_CRASH_INTERVAL.
+ Slog.w(TAG, " Not force finishing home activity "
+ + r.intent.getComponent().flattenToShortString());
+ return null;
+ }
+ Slog.w(TAG, " Force finishing activity "
+ + r.intent.getComponent().flattenToShortString());
+ Task finishedTask = r.getTask();
+ getDisplay().mDisplayContent.prepareAppTransition(
+ TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+ r.finishIfPossible(reason, false /* oomAdj */);
+
+ // Also terminate any activities below it that aren't yet stopped, to avoid a situation
+ // where one will get re-start our crashing activity once it gets resumed again.
+ final ActivityRecord activityBelow = getActivityBelow(r);
+ if (activityBelow != null) {
+ if (activityBelow.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
+ if (!activityBelow.isActivityTypeHome()
+ || mAtmService.mHomeProcess != activityBelow.app) {
+ Slog.w(TAG, " Force finishing activity "
+ + activityBelow.intent.getComponent().flattenToShortString());
+ activityBelow.finishIfPossible(reason, false /* oomAdj */);
+ }
+ }
+ }
+
+ return finishedTask;
+ }
+
+ void finishVoiceTask(IVoiceInteractionSession session) {
+ final PooledConsumer c = PooledLambda.obtainConsumer(Task::finishIfVoiceTask,
+ PooledLambda.__(Task.class), session.asBinder());
+ forAllLeafTasks(c, true /* traverseTopToBottom */);
+ c.recycle();
+ }
+
+ private static void finishIfVoiceTask(Task tr, IBinder binder) {
+ if (tr.voiceSession != null && tr.voiceSession.asBinder() == binder) {
+ tr.forAllActivities((r) -> {
+ if (r.finishing) return;
+ r.finishIfPossible("finish-voice", false /* oomAdj */);
+ tr.mAtmService.updateOomAdj();
+ });
+ } else {
+ // Check if any of the activities are using voice
+ final PooledFunction f = PooledLambda.obtainFunction(
+ Task::finishIfVoiceActivity, PooledLambda.__(ActivityRecord.class),
+ binder);
+ tr.forAllActivities(f);
+ f.recycle();
+ }
+ }
+
+ private static boolean finishIfVoiceActivity(ActivityRecord r, IBinder binder) {
+ if (r.voiceSession == null || r.voiceSession.asBinder() != binder) return false;
+ // Inform of cancellation
+ r.clearVoiceSessionLocked();
+ try {
+ r.app.getThread().scheduleLocalVoiceInteractionStarted(r.appToken, null);
+ } catch (RemoteException re) {
+ // Ok Boomer...
+ }
+ r.mAtmService.finishRunningVoiceLocked();
+ return true;
+ }
+
+ /** Finish all activities in the stack without waiting. */
+ void finishAllActivitiesImmediately() {
+ if (!hasChild()) {
+ removeIfPossible();
+ return;
+ }
+ forAllActivities((r) -> {
+ Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
+ r.destroyIfPossible("finishAllActivitiesImmediately");
+ });
+ }
+
+ /** @return true if the stack behind this one is a standard activity type. */
+ private boolean inFrontOfStandardStack() {
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ if (taskDisplayArea == null) {
+ return false;
+ }
+ final int index = taskDisplayArea.getIndexOf(this);
+ if (index == 0) {
+ return false;
+ }
+ final Task stackBehind = taskDisplayArea.getChildAt(index - 1);
+ return stackBehind.isActivityTypeStandard();
+ }
+
+ boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
+ // Basic case: for simple app-centric recents, we need to recreate
+ // the task if the affinity has changed.
+
+ final String affinity = ActivityRecord.getTaskAffinityWithUid(destAffinity, srec.getUid());
+ if (srec == null || srec.getTask().affinity == null
+ || !srec.getTask().affinity.equals(affinity)) {
+ return true;
+ }
+ // Document-centric case: an app may be split in to multiple documents;
+ // they need to re-create their task if this current activity is the root
+ // of a document, unless simply finishing it will return them to the
+ // correct app behind.
+ final Task task = srec.getTask();
+ if (srec.isRootOfTask() && task.getBaseIntent() != null
+ && task.getBaseIntent().isDocument()) {
+ // Okay, this activity is at the root of its task. What to do, what to do...
+ if (!inFrontOfStandardStack()) {
+ // Finishing won't return to an application, so we need to recreate.
+ return true;
+ }
+ // We now need to get the task below it to determine what to do.
+ final Task prevTask = getTaskBelow(task);
+ if (prevTask == null) {
+ Slog.w(TAG, "shouldUpRecreateTask: task not in history for " + srec);
+ return false;
+ }
+ if (!task.affinity.equals(prevTask.affinity)) {
+ // These are different apps, so need to recreate.
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean navigateUpTo(ActivityRecord srec, Intent destIntent, NeededUriGrants destGrants,
+ int resultCode, Intent resultData, NeededUriGrants resultGrants) {
+ if (!srec.attachedToProcess()) {
+ // Nothing to do if the caller is not attached, because this method should be called
+ // from an alive activity.
+ return false;
+ }
+ final Task task = srec.getTask();
+ if (!srec.isDescendantOf(this)) {
+ return false;
+ }
+
+ ActivityRecord parent = task.getActivityBelow(srec);
+ boolean foundParentInTask = false;
+ final ComponentName dest = destIntent.getComponent();
+ if (task.getBottomMostActivity() != srec && dest != null) {
+ final ActivityRecord candidate = task.getActivity(
+ (ar) -> ar.info.packageName.equals(dest.getPackageName())
+ && ar.info.name.equals(dest.getClassName()), srec,
+ false /*includeBoundary*/, true /*traverseTopToBottom*/);
+ if (candidate != null) {
+ parent = candidate;
+ foundParentInTask = true;
+ }
+ }
+
+ // TODO: There is a dup. of this block of code in ActivityTaskManagerService.finishActivity
+ // We should consolidate.
+ IActivityController controller = mAtmService.mController;
+ if (controller != null) {
+ ActivityRecord next = topRunningActivity(srec.appToken, INVALID_TASK_ID);
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean resumeOK = true;
+ try {
+ resumeOK = controller.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mAtmService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
+ }
+
+ if (!resumeOK) {
+ return false;
+ }
+ }
+ }
+ final long origId = Binder.clearCallingIdentity();
+
+ final int[] resultCodeHolder = new int[1];
+ resultCodeHolder[0] = resultCode;
+ final Intent[] resultDataHolder = new Intent[1];
+ resultDataHolder[0] = resultData;
+ final NeededUriGrants[] resultGrantsHolder = new NeededUriGrants[1];
+ resultGrantsHolder[0] = resultGrants;
+ final ActivityRecord finalParent = parent;
+ task.forAllActivities((ar) -> {
+ if (ar == finalParent) return true;
+
+ ar.finishIfPossible(resultCodeHolder[0], resultDataHolder[0], resultGrantsHolder[0],
+ "navigate-up", true /* oomAdj */);
+ // Only return the supplied result for the first activity finished
+ resultCodeHolder[0] = Activity.RESULT_CANCELED;
+ resultDataHolder[0] = null;
+ return false;
+ }, srec, true, true);
+ resultCode = resultCodeHolder[0];
+ resultData = resultDataHolder[0];
+
+ if (parent != null && foundParentInTask) {
+ final int callingUid = srec.info.applicationInfo.uid;
+ final int parentLaunchMode = parent.info.launchMode;
+ final int destIntentFlags = destIntent.getFlags();
+ if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
+ (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+ parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, srec.packageName);
+ } else {
+ try {
+ ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
+ destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
+ srec.mUserId);
+ // TODO(b/64750076): Check if calling pid should really be -1.
+ final int res = mAtmService.getActivityStartController()
+ .obtainStarter(destIntent, "navigateUpTo")
+ .setCaller(srec.app.getThread())
+ .setActivityInfo(aInfo)
+ .setResultTo(parent.appToken)
+ .setCallingPid(-1)
+ .setCallingUid(callingUid)
+ .setCallingPackage(srec.packageName)
+ .setCallingFeatureId(parent.launchedFromFeatureId)
+ .setRealCallingPid(-1)
+ .setRealCallingUid(callingUid)
+ .setComponentSpecified(true)
+ .execute();
+ foundParentInTask = res == ActivityManager.START_SUCCESS;
+ } catch (RemoteException e) {
+ foundParentInTask = false;
+ }
+ parent.finishIfPossible(resultCode, resultData, resultGrants,
+ "navigate-top", true /* oomAdj */);
+ }
+ }
+ Binder.restoreCallingIdentity(origId);
+ return foundParentInTask;
+ }
+
+ void removeLaunchTickMessages() {
+ forAllActivities(ActivityRecord::removeLaunchTickRunnable);
+ }
+
+ private void updateTransitLocked(int transit, ActivityOptions options, boolean forceOverride) {
+ if (options != null) {
+ ActivityRecord r = topRunningActivity();
+ if (r != null && !r.isState(RESUMED)) {
+ r.updateOptionsLocked(options);
+ } else {
+ ActivityOptions.abort(options);
+ }
+ }
+ getDisplay().mDisplayContent.prepareAppTransition(transit, false,
+ 0 /* flags */, forceOverride);
+ }
+
+ final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
+ AppTimeTracker timeTracker, String reason) {
+ moveTaskToFront(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
+ }
+
+ final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
+ AppTimeTracker timeTracker, boolean deferResume, String reason) {
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
+
+ final Task topStack = getDisplayArea().getTopStack();
+ final ActivityRecord topActivity = topStack != null
+ ? topStack.getTopNonFinishingActivity() : null;
+
+ if (tr != this && !tr.isDescendantOf(this)) {
+ // nothing to do!
+ if (noAnimation) {
+ ActivityOptions.abort(options);
+ } else if (isSingleTaskInstance()) {
+ // When a task is moved front on the display which can only contain one task, start
+ // a special transition.
+ // {@link AppTransitionController#handleAppTransitionReady} later picks up the
+ // transition, and schedules
+ // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is triggered
+ // after contents are drawn on the display.
+ updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
+ true /* forceOverride */);
+ } else {
+ updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
+ }
+ return;
+ }
+
+ if (timeTracker != null) {
+ // The caller wants a time tracker associated with this task.
+ final PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setAppTimeTracker,
+ PooledLambda.__(ActivityRecord.class), timeTracker);
+ tr.forAllActivities(c);
+ c.recycle();
+ }
+
+ try {
+ // Defer updating the IME target since the new IME target will try to get computed
+ // before updating all closing and opening apps, which can cause the ime target to
+ // get calculated incorrectly.
+ getDisplay().deferUpdateImeTarget();
+
+ // Shift all activities with this task up to the top
+ // of the stack, keeping them in the same internal order.
+ positionChildAtTop(tr);
+
+ // Don't refocus if invisible to current user
+ final ActivityRecord top = tr.getTopNonFinishingActivity();
+ if (top == null || !top.okToShowLocked()) {
+ if (top != null) {
+ mStackSupervisor.mRecentTasks.add(top.getTask());
+ }
+ ActivityOptions.abort(options);
+ return;
+ }
+
+ // Set focus to the top running activity of this stack.
+ final ActivityRecord r = topRunningActivity();
+ if (r != null) {
+ r.moveFocusableActivityToTop(reason);
+ }
+
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
+ if (noAnimation) {
+ getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_NONE, false);
+ if (r != null) {
+ mStackSupervisor.mNoAnimActivities.add(r);
+ }
+ ActivityOptions.abort(options);
+ } else if (isSingleTaskInstance()) {
+ updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
+ true /* forceOverride */);
+ } else {
+ updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
+ }
+
+ // If a new task is moved to the front, then mark the existing top activity as
+ // supporting
+
+ // picture-in-picture while paused only if the task would not be considered an oerlay
+ // on top
+ // of the current activity (eg. not fullscreen, or the assistant)
+ if (canEnterPipOnTaskSwitch(topActivity, tr, null /* toFrontActivity */,
+ options)) {
+ topActivity.supportsEnterPipOnTaskSwitch = true;
+ }
+
+ if (!deferResume) {
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
+ EventLogTags.writeWmTaskToFront(tr.mUserId, tr.mTaskId);
+ mAtmService.getTaskChangeNotificationController()
+ .notifyTaskMovedToFront(tr.getTaskInfo());
+ } finally {
+ getDisplay().continueUpdateImeTarget();
+ }
+ }
+
+ /**
+ * Worker method for rearranging history stack. Implements the function of moving all
+ * activities for a specific task (gathering them if disjoint) into a single group at the
+ * bottom of the stack.
+ *
+ * If a watcher is installed, the action is preflighted and the watcher has an opportunity
+ * to premeptively cancel the move.
+ *
+ * @param tr The task to collect and move to the bottom.
+ * @return Returns true if the move completed, false if not.
+ */
+ boolean moveTaskToBack(Task tr) {
+ Slog.i(TAG, "moveTaskToBack: " + tr);
+
+ // In LockTask mode, moving a locked task to the back of the stack may expose unlocked
+ // ones. Therefore we need to check if this operation is allowed.
+ if (!mAtmService.getLockTaskController().canMoveTaskToBack(tr)) {
+ return false;
+ }
+
+ // If we have a watcher, preflight the move before committing to it. First check
+ // for *other* available tasks, but if none are available, then try again allowing the
+ // current task to be selected.
+ if (isTopStackInDisplayArea() && mAtmService.mController != null) {
+ ActivityRecord next = topRunningActivity(null, tr.mTaskId);
+ if (next == null) {
+ next = topRunningActivity(null, INVALID_TASK_ID);
+ }
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean moveOK = true;
+ try {
+ moveOK = mAtmService.mController.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mAtmService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
+ }
+ if (!moveOK) {
+ return false;
+ }
+ }
+ }
+
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
+ + tr.mTaskId);
+
+ getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
+ moveToBack("moveTaskToBackLocked", tr);
+
+ if (inPinnedWindowingMode()) {
+ mStackSupervisor.removeStack(this);
+ return true;
+ }
+
+ mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
+ getDisplay().mDisplayId, false /* markFrozenIfConfigChanged */,
+ false /* deferResume */);
+
+ ActivityRecord topActivity = getDisplayArea().topRunningActivity();
+ Task topStack = topActivity.getRootTask();
+ if (topStack != null && topStack != this && topActivity.isState(RESUMED)) {
+ // Usually resuming a top activity triggers the next app transition, but nothing's got
+ // resumed in this case, so we need to execute it explicitly.
+ getDisplay().mDisplayContent.executeAppTransition();
+ } else {
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
+ return true;
+ }
+
+ /**
+ * Ensures all visible activities at or below the input activity have the right configuration.
+ */
+ void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
+ mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
+ }
+
+ // TODO: Can only be called from special methods in ActivityStackSupervisor.
+ // Need to consolidate those calls points into this resize method so anyone can call directly.
+ void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + getRootTaskId());
+ mAtmService.deferWindowLayout();
+ try {
+ // TODO: Why not just set this on the stack directly vs. on each tasks?
+ // Update override configurations of all tasks in the stack.
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ Task::processTaskResizeBounds, PooledLambda.__(Task.class),
+ displayedBounds);
+ forAllTasks(c, true /* traverseTopToBottom */);
+ c.recycle();
+
+ if (mBoundsAnimating) {
+ // Force to update task surface bounds and relayout windows, since configBounds
+ // remains unchanged during bounds animation.
+ updateSurfaceBounds();
+ getDisplay().setLayoutNeeded();
+ mWmService.requestTraversal();
+ }
+
+ if (!deferResume) {
+ ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows);
+ }
+ } finally {
+ mAtmService.continueWindowLayout();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
+ private static void processTaskResizeBounds(Task task, Rect displayedBounds) {
+ if (!task.isResizeable()) return;
+
+ task.setBounds(displayedBounds);
+ }
+
+ /**
+ * Until we can break this "set task bounds to same as stack bounds" behavior, this
+ * basically resizes both stack and task bounds to the same bounds.
+ */
+ private void setTaskBounds(Rect bounds) {
+ final PooledConsumer c = PooledLambda.obtainConsumer(Task::setTaskBoundsInner,
+ PooledLambda.__(Task.class), bounds);
+ forAllLeafTasks(c, true /* traverseTopToBottom */);
+ c.recycle();
+ }
+
+ private static void setTaskBoundsInner(Task task, Rect bounds) {
+ task.setBounds(task.isResizeable() ? bounds : null);
+ }
+
+ boolean willActivityBeVisible(IBinder token) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ return false;
+ }
+
+ // See if there is an occluding activity on-top of this one.
+ final ActivityRecord occludingActivity = getOccludingActivityAbove(r);
+ if (occludingActivity != null) return false;
+
+ if (r.finishing) Slog.e(TAG, "willActivityBeVisible: Returning false,"
+ + " would have returned true for r=" + r);
+ return !r.finishing;
+ }
+
+ void unhandledBackLocked() {
+ final ActivityRecord topActivity = getTopMostActivity();
+ if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
+ "Performing unhandledBack(): top activity: " + topActivity);
+ if (topActivity != null) {
+ topActivity.finishIfPossible("unhandled-back", true /* oomAdj */);
+ }
+ }
+
+ /**
+ * Reset local parameters because an app's activity died.
+ * @param app The app of the activity that died.
+ * @return result from removeHistoryRecordsForAppLocked.
+ */
+ boolean handleAppDied(WindowProcessController app) {
+ if (mPausingActivity != null && mPausingActivity.app == app) {
+ if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
+ "App died while pausing: " + mPausingActivity);
+ mPausingActivity = null;
+ }
+ if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+ mLastPausedActivity = null;
+ mLastNoHistoryActivity = null;
+ }
+
+ mStackSupervisor.removeHistoryRecords(app);
+ return mRemoveHistoryRecordsForApp.process(app);
+ }
+
+ boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
+ String dumpPackage, final boolean needSep) {
+ Runnable headerPrinter = () -> {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" Stack #" + getRootTaskId()
+ + ": type=" + activityTypeToString(getActivityType())
+ + " mode=" + windowingModeToString(getWindowingMode()));
+ pw.println(" isSleeping=" + shouldSleepActivities());
+ pw.println(" mBounds=" + getRequestedOverrideBounds());
+ };
+
+ boolean printed = false;
+
+ if (dumpPackage == null) {
+ // If we are not filtering by package, we want to print absolutely everything,
+ // so always print the header even if there are no tasks/activities inside.
+ headerPrinter.run();
+ headerPrinter = null;
+ printed = true;
+ }
+
+ printed |= printThisActivity(pw, mPausingActivity, dumpPackage, false,
+ " mPausingActivity: ", null);
+ printed |= printThisActivity(pw, getResumedActivity(), dumpPackage, false,
+ " mResumedActivity: ", null);
+ if (dumpAll) {
+ printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
+ " mLastPausedActivity: ", null);
+ printed |= printThisActivity(pw, mLastNoHistoryActivity, dumpPackage,
+ false, " mLastNoHistoryActivity: ", null);
+ }
+
+ printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter);
+
+ return printed;
+ }
+
+ private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) {
+ if (!hasChild()) {
+ return false;
+ }
+ final AtomicBoolean printedHeader = new AtomicBoolean(false);
+ final AtomicBoolean printed = new AtomicBoolean(false);
+ forAllLeafTasks((task) -> {
+ final String prefix = " ";
+ Runnable headerPrinter = () -> {
+ printed.set(true);
+ if (!printedHeader.get()) {
+ if (needSep) {
+ pw.println("");
+ }
+ if (header != null) {
+ header.run();
+ }
+ printedHeader.set(true);
+ }
+ pw.print(prefix); pw.print("* "); pw.println(task);
+ pw.print(prefix); pw.print(" mBounds=");
+ pw.println(task.getRequestedOverrideBounds());
+ pw.print(prefix); pw.print(" mMinWidth="); pw.print(task.mMinWidth);
+ pw.print(" mMinHeight="); pw.println(task.mMinHeight);
+ if (mLastNonFullscreenBounds != null) {
+ pw.print(prefix);
+ pw.print(" mLastNonFullscreenBounds=");
+ pw.println(task.mLastNonFullscreenBounds);
+ }
+ task.dump(pw, prefix + " ");
+ };
+ if (dumpPackage == null) {
+ // If we are not filtering by package, we want to print absolutely everything,
+ // so always print the header even if there are no activities inside.
+ headerPrinter.run();
+ headerPrinter = null;
+ }
+ final ArrayList<ActivityRecord> activities = new ArrayList<>();
+ // Add activities by traversing the hierarchy from bottom to top, since activities
+ // are dumped in reverse order in {@link ActivityStackSupervisor#dumpHistoryList()}.
+ task.forAllActivities((Consumer<ActivityRecord>) activities::add,
+ false /* traverseTopToBottom */);
+ dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
+ dumpPackage, false, headerPrinter, task);
+ }, true /* traverseTopToBottom */);
+ return printed.get();
+ }
+
+ ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
+ ArrayList<ActivityRecord> activities = new ArrayList<>();
+
+ if ("all".equals(name)) {
+ forAllActivities((Consumer<ActivityRecord>) activities::add);
+ } else if ("top".equals(name)) {
+ final ActivityRecord topActivity = getTopMostActivity();
+ if (topActivity != null) {
+ activities.add(topActivity);
+ }
+ } else {
+ ActivityManagerService.ItemMatcher matcher = new ActivityManagerService.ItemMatcher();
+ matcher.build(name);
+
+ forAllActivities((r) -> {
+ if (matcher.match(r, r.intent.getComponent())) {
+ activities.add(r);
+ }
+ });
+ }
+
+ return activities;
+ }
+
+ ActivityRecord restartPackage(String packageName) {
+ ActivityRecord starting = topRunningActivity();
+
+ // All activities that came from the package must be
+ // restarted as if there was a config change.
+ PooledConsumer c = PooledLambda.obtainConsumer(Task::restartPackage,
+ PooledLambda.__(ActivityRecord.class), starting, packageName);
+ forAllActivities(c);
+ c.recycle();
+
+ return starting;
+ }
+
+ private static void restartPackage(
+ ActivityRecord r, ActivityRecord starting, String packageName) {
+ if (r.info.packageName.equals(packageName)) {
+ r.forceNewConfig = true;
+ if (starting != null && r == starting && r.mVisibleRequested) {
+ r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
+ }
+ }
+ }
+
+ Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) {
+ return reuseOrCreateTask(info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
+ toTop, null /*activity*/, null /*source*/, null /*options*/);
+ }
+ // TODO: Can be removed once we change callpoints creating stacks to be creating tasks.
+ /** Either returns this current task to be re-used or creates a new child task. */
+ Task reuseOrCreateTask(ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession,
+ IVoiceInteractor voiceInteractor, boolean toTop, ActivityRecord activity,
+ ActivityRecord source, ActivityOptions options) {
+
+ Task task;
+ if (DisplayContent.alwaysCreateStack(getWindowingMode(), getActivityType())) {
+ // This stack will only contain one task, so just return itself since all stacks ara now
+ // tasks and all tasks are now stacks.
+ task = reuseAsLeafTask(voiceSession, voiceInteractor, intent, info, activity);
+ } else {
+ // Create child task since this stack can contain multiple tasks.
+ final int taskId = activity != null
+ ? mStackSupervisor.getNextTaskIdForUser(activity.mUserId)
+ : mStackSupervisor.getNextTaskIdForUser();
+ task = new Task(mAtmService, taskId, info, intent, voiceSession,
+ voiceInteractor, null /* taskDescription */, this);
+
+ // add the task to stack first, mTaskPositioner might need the stack association
+ addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+ }
+
+ int displayId = getDisplayId();
+ if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
+ final boolean isLockscreenShown = mAtmService.mStackSupervisor.getKeyguardController()
+ .isKeyguardOrAodShowing(displayId);
+ if (!mStackSupervisor.getLaunchParamsController()
+ .layoutTask(task, info.windowLayout, activity, source, options)
+ && !getRequestedOverrideBounds().isEmpty()
+ && task.isResizeable() && !isLockscreenShown) {
+ task.setBounds(getRequestedOverrideBounds());
+ }
+
+ return task;
+ }
+
+ void addChild(WindowContainer child, final boolean toTop, boolean showForAllUsers) {
+ if (isSingleTaskInstance() && hasChild()) {
+ throw new IllegalStateException("Can only have one child on stack=" + this);
+ }
+
+ Task task = child.asTask();
+ try {
+
+ if (task != null) {
+ task.setForceShowForAllUsers(showForAllUsers);
+ }
+ // We only want to move the parents to the parents if we are creating this task at the
+ // top of its stack.
+ addChild(child, toTop ? MAX_VALUE : 0, toTop /*moveParents*/);
+ } finally {
+ if (task != null) {
+ task.setForceShowForAllUsers(false);
+ }
+ }
+ }
+
+ void positionChildAt(Task task, int position) {
+ if (task.getRootTask() != this) {
+ throw new IllegalArgumentException("AS.positionChildAt: task=" + task
+ + " is not a child of stack=" + this + " current parent=" + task.getRootTask());
+ }
+
+ task.updateOverrideConfigurationForStack(this);
+
+ final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
+ final boolean wasResumed = topRunningActivity == task.getRootTask().mResumedActivity;
+
+ boolean toTop = position >= getChildCount();
+ boolean includingParents = toTop || getDisplayArea().getNextFocusableStack(this,
+ true /* ignoreCurrent */) == null;
+ if (WindowManagerDebugConfig.DEBUG_STACK) {
+ Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position);
+ }
+ positionChildAt(position, task, includingParents);
+ task.updateTaskMovement(toTop);
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+
+
+ // TODO: Investigate if this random code is really needed.
+ if (task.voiceSession != null) {
+ try {
+ task.voiceSession.taskStarted(task.intent, task.mTaskId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ if (wasResumed) {
+ if (mResumedActivity != null) {
+ Log.wtf(TAG, "mResumedActivity was already set when moving mResumedActivity from"
+ + " other stack to this stack mResumedActivity=" + mResumedActivity
+ + " other mResumedActivity=" + topRunningActivity);
+ }
+ topRunningActivity.setState(RESUMED, "positionChildAt");
+ }
+
+ // The task might have already been running and its visibility needs to be synchronized with
+ // the visibility of the stack / windows.
+ ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
+
+ public void setAlwaysOnTop(boolean alwaysOnTop) {
+ if (isAlwaysOnTop() == alwaysOnTop) {
+ return;
+ }
+ super.setAlwaysOnTop(alwaysOnTop);
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ // positionChildAtTop() must be called even when always on top gets turned off because we
+ // need to make sure that the stack is moved from among always on top windows to below other
+ // always on top windows. Since the position the stack should be inserted into is calculated
+ // properly in {@link DisplayContent#getTopInsertPosition()} in both cases, we can just
+ // request that the stack is put at top here.
+ taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
+ }
+
+ /** NOTE: Should only be called from {@link Task#reparent}. */
+ void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
+ boolean setPause, String reason) {
+ if (!moveToFront) {
+ return;
+ }
+
+ final ActivityState origState = r.getState();
+ // If the activity owns the last resumed activity, transfer that together,
+ // so that we don't resume the same activity again in the new stack.
+ // Apps may depend on onResume()/onPause() being called in pairs.
+ if (setResume) {
+ r.setState(RESUMED, "moveToFrontAndResumeStateIfNeeded");
+ }
+ // If the activity was previously pausing, then ensure we transfer that as well
+ if (setPause) {
+ mPausingActivity = r;
+ r.schedulePauseTimeout();
+ }
+ // Move the stack in which we are placing the activity to the front.
+ moveToFront(reason);
+ // If the original state is resumed, there is no state change to update focused app.
+ // So here makes sure the activity focus is set if it is the top.
+ if (origState == RESUMED && r == mRootWindowContainer.getTopResumedActivity()) {
+ mAtmService.setResumedActivityUncheckLocked(r, reason);
+ }
+ }
+
+ void dismissPip() {
+ if (!isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException(
+ "You can't move tasks from non-standard stacks.");
+ }
+ if (getWindowingMode() != WINDOWING_MODE_PINNED) {
+ throw new IllegalArgumentException(
+ "Can't exit pinned mode if it's not pinned already.");
+ }
+
+ mWmService.inSurfaceTransaction(() -> {
+ final Task task = getBottomMostTask();
+ setWindowingMode(WINDOWING_MODE_UNDEFINED);
+
+ getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
+
+ mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
+ MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext,
+ task.effectiveUid, task.realActivity.flattenToString());
+ });
+ }
+
+ void prepareFreezingTaskBounds() {
+ forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */);
+ }
+
+ private int setBounds(Rect existing, Rect bounds) {
+ if (equivalentBounds(existing, bounds)) {
+ return BOUNDS_CHANGE_NONE;
+ }
+
+ final int result = super.setBounds(!inMultiWindowMode() ? null : bounds);
+
+ updateSurfaceBounds();
+ return result;
+ }
+
+ @Override
+ public void getBounds(Rect bounds) {
+ bounds.set(getBounds());
+ }
+
+ /**
+ * @return the final bounds for the bounds animation.
+ */
+ void getFinalAnimationBounds(Rect outBounds) {
+ outBounds.set(mBoundsAnimationTarget);
+ }
+
+ /**
+ * @return the final source bounds for the bounds animation.
+ */
+ void getFinalAnimationSourceHintBounds(Rect outBounds) {
+ outBounds.set(mBoundsAnimationSourceHintBounds);
+ }
+
+ /**
+ * Put a Task in this stack. Used for adding only.
+ * When task is added to top of the stack, the entire branch of the hierarchy (including stack
+ * and display) will be brought to top.
+ * @param child The child to add.
+ * @param position Target position to add the task to.
+ */
+ private void addChild(WindowContainer child, int position, boolean moveParents) {
+ // Add child task.
+ addChild(child, null);
+
+ // Move child to a proper position, as some restriction for position might apply.
+ positionChildAt(position, child, moveParents /* includingParents */);
+ }
+
+ void positionChildAtTop(Task child) {
+ if (child == null) {
+ // TODO: Fix the call-points that cause this to happen.
+ return;
+ }
+
+ if (child == this) {
+ // TODO: Fix call-points
+ moveToFront("positionChildAtTop");
+ return;
+ }
+
+ positionChildAt(POSITION_TOP, child, true /* includingParents */);
+ child.updateTaskMovement(true);
+
+ final DisplayContent displayContent = getDisplayContent();
+ displayContent.layoutAndAssignWindowLayersIfNeeded();
+ }
+
+ void positionChildAtBottom(Task child) {
+ // If there are other focusable stacks on the display, the z-order of the display should not
+ // be changed just because a task was placed at the bottom. E.g. if it is moving the topmost
+ // task to bottom, the next focusable stack on the same display should be focused.
+ final Task nextFocusableStack = getDisplayArea().getNextFocusableStack(
+ child.getRootTask(), true /* ignoreCurrent */);
+ positionChildAtBottom(child, nextFocusableStack == null /* includingParents */);
+ child.updateTaskMovement(true);
+ }
+
+ @VisibleForTesting
+ void positionChildAtBottom(Task child, boolean includingParents) {
+ if (child == null) {
+ // TODO: Fix the call-points that cause this to happen.
+ return;
+ }
+
+ positionChildAt(POSITION_BOTTOM, child, includingParents);
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ }
+
+ @Override
+ void onChildPositionChanged(WindowContainer child) {
+ if (isOrganized()) {
+ mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
+ }
+
+ if (!mChildren.contains(child)) {
+ return;
+ }
+
+ final boolean isTop = getTopChild() == child;
+
+ final Task task = child.asTask();
+ if (task != null) {
+ task.updateTaskMovement(isTop);
+ }
+
+ if (isTop) {
+ final DisplayContent displayContent = getDisplayContent();
+ displayContent.layoutAndAssignWindowLayersIfNeeded();
+ }
+ }
+
+ void reparent(TaskDisplayArea newParent, boolean onTop) {
+ reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ }
+
+ private void updateSurfaceBounds() {
+ updateSurfaceSize(getSyncTransaction());
+ updateSurfacePosition();
+ scheduleAnimation();
+ }
+
+ @Override
+ void getRelativePosition(Point outPos) {
+ super.getRelativePosition(outPos);
+ final int outset = getTaskOutset();
+ outPos.x -= outset;
+ outPos.y -= outset;
+ }
+
+ boolean shouldIgnoreInput() {
+ if (inSplitScreenPrimaryWindowingMode() && !isFocusable()) {
+ return true;
+ }
+ if (mAtmService.mHasLeanbackFeature && inPinnedWindowingMode()
+ && !isFocusedStackOnDisplay()) {
+ // Preventing Picture-in-Picture stack from receiving input on TVs.
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Sets the current picture-in-picture aspect ratio.
+ */
+ void setPictureInPictureAspectRatio(float aspectRatio) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
+ return;
+ }
+
+ final DisplayContent displayContent = getDisplayContent();
+ if (displayContent == null) {
+ return;
+ }
+
+ if (!inPinnedWindowingMode()) {
+ return;
+ }
+
+ final PinnedStackController pinnedStackController =
+ getDisplayContent().getPinnedStackController();
+
+ if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
+ return;
+ }
+
+ // 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);
+ }
+
+ /**
+ * Sets the current picture-in-picture actions.
+ */
+ void setPictureInPictureActions(List<RemoteAction> actions) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
+ return;
+ }
+
+ if (!inPinnedWindowingMode()) {
+ return;
+ }
+
+ getDisplayContent().getPinnedStackController().setActions(actions);
+ }
+
+ public boolean isForceScaled() {
+ return mBoundsAnimating;
+ }
+
+ /** Returns true if a removal action is still being deferred. */
+ boolean handleCompleteDeferredRemoval() {
+ if (isAnimating(TRANSITION | CHILDREN)) {
+ return true;
+ }
+
+ return super.handleCompleteDeferredRemoval();
+ }
+
+ public DisplayInfo getDisplayInfo() {
+ return mDisplayContent.getDisplayInfo();
+ }
+
+ AnimatingActivityRegistry getAnimatingActivityRegistry() {
+ return mAnimatingActivityRegistry;
+ }
+
+ void executeAppTransition(ActivityOptions options) {
+ getDisplay().mDisplayContent.executeAppTransition();
+ ActivityOptions.abort(options);
+ }
+
+ boolean shouldSleepActivities() {
+ final DisplayContent display = getDisplay();
+
+ // Do not sleep activities in this stack if we're marked as focused and the keyguard
+ // is in the process of going away.
+ if (isFocusedStackOnDisplay()
+ && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
+ return false;
+ }
+
+ return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
+ }
+
+ boolean shouldSleepOrShutDownActivities() {
+ return shouldSleepActivities() || mAtmService.mShuttingDown;
+ }
+
+ /** Bounds of the stack without adjusting for other factors in the system like visibility
+ * of docked stack.
+ * Most callers should be using {@link ConfigurationContainer#getRequestedOverrideBounds} a
+ * it takes into consideration other system factors. */
+ void getRawBounds(Rect out) {
+ out.set(getRawBounds());
+ }
+
+ private Rect getRawBounds() {
+ return super.getBounds();
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel) {
+ if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ return;
+ }
+
+ final long token = proto.start(fieldId);
+ super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
+
+ proto.write(TaskProto.ID, mTaskId);
+ proto.write(DISPLAY_ID, getDisplayId());
+ proto.write(ROOT_TASK_ID, getRootTaskId());
+
+ if (mResumedActivity != null) {
+ mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+ }
+ if (realActivity != null) {
+ proto.write(REAL_ACTIVITY, realActivity.flattenToShortString());
+ }
+ if (origActivity != null) {
+ proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString());
+ }
+ proto.write(ACTIVITY_TYPE, getActivityType());
+ proto.write(RESIZE_MODE, mResizeMode);
+ proto.write(MIN_WIDTH, mMinWidth);
+ proto.write(MIN_HEIGHT, mMinHeight);
+
+ proto.write(FILLS_PARENT, matchParentBounds());
+ getRawBounds().dumpDebug(proto, BOUNDS);
+
+ if (mLastNonFullscreenBounds != null) {
+ mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS);
+ }
+
+ proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
+
+ if (mSurfaceControl != null) {
+ proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
+ proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
+ }
+
+ proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
+
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 7b690383f5f9..246753ad2bde 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -32,8 +32,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
@@ -42,6 +40,8 @@ import static com.android.server.wm.DisplayContent.alwaysCreateStack;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.RootWindowContainer.TAG_STATES;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -52,6 +52,7 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.os.UserHandle;
+import android.util.IntArray;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
@@ -65,11 +66,14 @@ import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* {@link DisplayArea} that represents a section of a screen that contains app window containers.
*/
-final class TaskDisplayArea extends DisplayArea<ActivityStack> {
+final class TaskDisplayArea extends DisplayArea<Task> {
DisplayContent mDisplayContent;
@@ -96,16 +100,19 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
// Cached reference to some special tasks we tend to get a lot so we don't need to loop
// through the list to find them.
- private ActivityStack mRootHomeTask;
- private ActivityStack mRootPinnedTask;
- private ActivityStack mRootSplitScreenPrimaryTask;
+ private Task mRootHomeTask;
+ private Task mRootPinnedTask;
+ private Task mRootSplitScreenPrimaryTask;
// TODO(b/159029784): Remove when getStack() behavior is cleaned-up
- private ActivityStack mRootRecentsTask;
+ private Task mRootRecentsTask;
- private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
- private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>();
- private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>();
+ private final ArrayList<Task> mTmpAlwaysOnTopStacks = new ArrayList<>();
+ private final ArrayList<Task> mTmpNormalStacks = new ArrayList<>();
+ private final ArrayList<Task> mTmpHomeStacks = new ArrayList<>();
+ private final IntArray mTmpNeedsZBoostIndexes = new IntArray();
+ private int mTmpLayerForSplitScreenDividerAnchor;
+ private int mTmpLayerForAnimationLayer;
private ArrayList<Task> mTmpTasks = new ArrayList<>();
@@ -121,7 +128,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* have the topmost index, it is used as a preferred candidate to prevent being unable to resume
* target stack properly when there are other focusable always-on-top stacks.
*/
- ActivityStack mPreferredTopFocusableStack;
+ Task mPreferredTopFocusableStack;
private final RootWindowContainer.FindTaskResult
mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
@@ -131,7 +138,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* stack has been resumed. If stacks are changing position this will hold the old stack until
* the new stack becomes resumed after which it will be set to current focused stack.
*/
- ActivityStack mLastFocusedStack;
+ Task mLastFocusedStack;
/**
* All of the stacks on this display. Order matters, topmost stack is in front of all other
* stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls
@@ -157,7 +164,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* Returns the topmost stack on the display that is compatible with the input windowing mode
* and activity type. Null is no compatible stack on the display.
*/
- ActivityStack getStack(int windowingMode, int activityType) {
+ Task getStack(int windowingMode, int activityType) {
if (activityType == ACTIVITY_TYPE_HOME) {
return mRootHomeTask;
} else if (activityType == ACTIVITY_TYPE_RECENTS) {
@@ -169,7 +176,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return mRootSplitScreenPrimaryTask;
}
for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i);
+ final Task stack = getChildAt(i);
if (activityType == ACTIVITY_TYPE_UNDEFINED
&& windowingMode == stack.getWindowingMode()) {
// Passing in undefined type means we want to match the topmost stack with the
@@ -184,33 +191,33 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
@VisibleForTesting
- ActivityStack getTopStack() {
+ Task getTopStack() {
final int count = getChildCount();
return count > 0 ? getChildAt(count - 1) : null;
}
// TODO: Figure-out a way to remove since it might be a source of confusion.
- int getIndexOf(ActivityStack stack) {
- return mChildren.indexOf(stack);
+ int getIndexOf(Task task) {
+ return mChildren.indexOf(task);
}
- @Nullable ActivityStack getRootHomeTask() {
+ @Nullable Task getRootHomeTask() {
return mRootHomeTask;
}
- @Nullable ActivityStack getRootRecentsTask() {
+ @Nullable Task getRootRecentsTask() {
return mRootRecentsTask;
}
- ActivityStack getRootPinnedTask() {
+ Task getRootPinnedTask() {
return mRootPinnedTask;
}
- ActivityStack getRootSplitScreenPrimaryTask() {
+ Task getRootSplitScreenPrimaryTask() {
return mRootSplitScreenPrimaryTask;
}
- ActivityStack getRootSplitScreenSecondaryTask() {
+ Task getRootSplitScreenSecondaryTask() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).inSplitScreenSecondaryWindowingMode()) {
return mChildren.get(i);
@@ -229,7 +236,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return visibleTasks;
}
- void onStackWindowingModeChanged(ActivityStack stack) {
+ void onStackWindowingModeChanged(Task stack) {
removeStackReferenceIfNeeded(stack);
addStackReferenceIfNeeded(stack);
if (stack == mRootPinnedTask && getTopStack() != stack) {
@@ -238,7 +245,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
}
- void addStackReferenceIfNeeded(ActivityStack stack) {
+ void addStackReferenceIfNeeded(Task stack) {
if (stack.isActivityTypeHome()) {
if (mRootHomeTask != null) {
if (!stack.isDescendantOf(mRootHomeTask)) {
@@ -283,7 +290,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
}
- void removeStackReferenceIfNeeded(ActivityStack stack) {
+ void removeStackReferenceIfNeeded(Task stack) {
if (stack == mRootHomeTask) {
mRootHomeTask = null;
} else if (stack == mRootRecentsTask) {
@@ -296,19 +303,22 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
@Override
- void addChild(ActivityStack stack, int position) {
- if (DEBUG_STACK) Slog.d(TAG_WM, "Set stack=" + stack + " on taskDisplayArea=" + this);
- addStackReferenceIfNeeded(stack);
- position = findPositionForStack(position, stack, true /* adding */);
+ void addChild(Task task, int position) {
+ if (DEBUG_STACK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);
+ if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1) {
+ throw new IllegalStateException("addChild: Can only have one task on display=" + this);
+ }
- super.addChild(stack, position);
- mAtmService.updateSleepIfNeededLocked();
+ addStackReferenceIfNeeded(task);
+ position = findPositionForStack(position, task, true /* adding */);
- positionStackAt(stack, position);
+ super.addChild(task, position);
+ mAtmService.updateSleepIfNeededLocked();
+ onStackOrderChanged(task);
}
@Override
- protected void removeChild(ActivityStack stack) {
+ protected void removeChild(Task stack) {
super.removeChild(stack);
onStackRemoved(stack);
mAtmService.updateSleepIfNeededLocked();
@@ -321,27 +331,42 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return true;
}
- @Override
- void positionChildAt(int position, ActivityStack child, boolean includingParents) {
- final boolean moveToTop = position >= getChildCount() - 1;
- final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0);
+ void positionChildAt(int position, Task child, boolean includingParents,
+ String updateLastFocusedTaskReason) {
+ final Task prevFocusedTask = updateLastFocusedTaskReason != null ? getFocusedStack() : null;
- // Reset mPreferredTopFocusableStack before positioning to top or {@link
- // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
- // resumed activity.
- final boolean wasContained = mChildren.contains(child);
- if (moveToTop && wasContained && child.isFocusable()) {
- mPreferredTopFocusableStack = null;
+ positionChildAt(position, child, includingParents);
+
+ if (updateLastFocusedTaskReason == null) {
+ return;
+ }
+
+ final Task currentFocusedStack = getFocusedStack();
+ if (currentFocusedStack == prevFocusedTask) {
+ return;
}
+ mLastFocusedStack = prevFocusedTask;
+ EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
+ mDisplayContent.mDisplayId,
+ currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
+ mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
+ updateLastFocusedTaskReason);
+ }
+
+ @Override
+ void positionChildAt(int position, Task child, boolean includingParents) {
+ final boolean moveToTop = position >= getChildCount() - 1;
+ final boolean moveToBottom = position <= 0;
+
+ final int oldPosition = mChildren.indexOf(child);
if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
// This stack is always-on-top, override the default behavior.
Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
// Moving to its current position, as we must call super but we don't want to
// perform any meaningful action.
- final int currentPosition = mChildren.indexOf(child);
- super.positionChildAt(currentPosition, child, false /* includingParents */);
+ super.positionChildAt(oldPosition, child, false /* includingParents */);
return;
}
// We don't allow untrusted display to top when task stack moves to top,
@@ -352,17 +377,14 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
final int targetPosition = findPositionForStack(position, child, false /* adding */);
super.positionChildAt(targetPosition, child, false /* includingParents */);
- if (includingParents && (moveToTop || moveToBottom)) {
- // The DisplayContent children do not re-order, but we still want to move the
- // display of this stack container because the intention of positioning is to have
- // higher z-order to gain focus.
- mDisplayContent.positionDisplayAt(moveToTop ? POSITION_TOP : POSITION_BOTTOM,
- true /* includingParents */);
+ if (includingParents && getParent() != null && (moveToTop || moveToBottom)) {
+ getParent().positionChildAt(moveToTop ? POSITION_TOP : POSITION_BOTTOM,
+ this /* child */, true /* includingParents */);
}
child.updateTaskMovement(moveToTop);
- mDisplayContent.setLayoutNeeded();
+ mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
// The insert position may be adjusted to non-top when there is always-on-top stack. Since
// the original position is preferred to be top, the stack should have higher priority when
@@ -374,6 +396,38 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
} else if (mPreferredTopFocusableStack == child) {
mPreferredTopFocusableStack = null;
}
+
+ // Update the top resumed activity because the preferred top focusable task may be changed.
+ mAtmService.mStackSupervisor.updateTopResumedActivityIfNeeded();
+
+ if (mChildren.indexOf(child) != oldPosition) {
+ onStackOrderChanged(child);
+ }
+ }
+
+ @Override
+ boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
+ boolean traverseTopToBottom) {
+ return callback.apply(this);
+ }
+
+ @Override
+ void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) {
+ callback.accept(this);
+ }
+
+ @Nullable
+ @Override
+ <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator,
+ @Nullable R initValue, boolean traverseTopToBottom) {
+ return accumulator.apply(this, initValue);
+ }
+
+ @Nullable
+ @Override
+ <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
+ boolean traverseTopToBottom) {
+ return callback.apply(this);
}
/**
@@ -384,7 +438,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
*
* @return the priority of the stack
*/
- private int getPriority(ActivityStack stack) {
+ private int getPriority(Task stack) {
if (mWmService.mAssistantOnTopOfDream && stack.isActivityTypeAssistant()) return 4;
if (stack.isActivityTypeDream()) return 3;
if (stack.inPinnedWindowingMode()) return 2;
@@ -392,7 +446,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return 0;
}
- private int findMinPositionForStack(ActivityStack stack) {
+ private int findMinPositionForStack(Task stack) {
int minPosition = POSITION_BOTTOM;
for (int i = 0; i < mChildren.size(); ++i) {
if (getPriority(getStackAt(i)) < getPriority(stack)) {
@@ -414,9 +468,9 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return minPosition;
}
- private int findMaxPositionForStack(ActivityStack stack) {
+ private int findMaxPositionForStack(Task stack) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
- final ActivityStack curr = getStackAt(i);
+ final Task curr = getStackAt(i);
// Since a stack could be repositioned while still being one of the children, we check
// if 'curr' is the same stack and skip it if so
final boolean sameStack = curr == stack;
@@ -446,7 +500,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* @param adding Flag indicates whether we're adding a new stack or positioning an existing.
* @return The proper position for the stack.
*/
- private int findPositionForStack(int requestedPosition, ActivityStack stack, boolean adding) {
+ private int findPositionForStack(int requestedPosition, Task stack, boolean adding) {
// The max possible position we can insert the stack at.
int maxPosition = findMaxPositionForStack(stack);
// The min possible position we can insert the stack at.
@@ -564,16 +618,23 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
// Apps and their containers are not allowed to specify an orientation while using
// root tasks...except for the home stack if it is not resizable and currently
// visible (top of) its root task.
- if (mRootHomeTask != null && mRootHomeTask.isVisible()
- && !mRootHomeTask.isResizeable()) {
+ if (mRootHomeTask != null && !mRootHomeTask.isResizeable()) {
// Manually nest one-level because because getOrientation() checks fillsParent()
// which checks that requestedOverrideBounds() is empty. However, in this case,
// it is not empty because it's been overridden to maintain the fullscreen size
// within a smaller split-root.
final Task topHomeTask = mRootHomeTask.getTopMostTask();
- final int orientation = topHomeTask.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET) {
- return orientation;
+ final ActivityRecord topHomeActivity = topHomeTask.getTopNonFinishingActivity();
+ // If a home activity is in the process of launching and isn't yet visible we
+ // should still respect the stack's preferred orientation to ensure rotation occurs
+ // before the home activity finishes launching.
+ final boolean isHomeActivityLaunching = topHomeActivity != null
+ && topHomeActivity.mVisibleRequested;
+ if (topHomeTask.isVisible() || isHomeActivityLaunching) {
+ final int orientation = topHomeTask.getOrientation();
+ if (orientation != SCREEN_ORIENTATION_UNSET) {
+ return orientation;
+ }
}
}
return SCREEN_ORIENTATION_UNSPECIFIED;
@@ -601,7 +662,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
assignStackOrdering(t);
for (int i = 0; i < mChildren.size(); i++) {
- final ActivityStack s = mChildren.get(i);
+ final Task s = mChildren.get(i);
s.assignChildLayers(t);
}
}
@@ -614,7 +675,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
mTmpHomeStacks.clear();
mTmpNormalStacks.clear();
for (int i = 0; i < mChildren.size(); ++i) {
- final ActivityStack s = mChildren.get(i);
+ final Task s = mChildren.get(i);
if (s.isAlwaysOnTop()) {
mTmpAlwaysOnTopStacks.add(s);
} else if (s.isActivityTypeHome()) {
@@ -626,39 +687,72 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
int layer = 0;
// Place home stacks to the bottom.
- for (int i = 0; i < mTmpHomeStacks.size(); i++) {
- mTmpHomeStacks.get(i).assignLayer(t, layer++);
- }
+ layer = adjustRootTaskLayer(t, mTmpHomeStacks, layer, false /* normalStacks */);
// The home animation layer is between the home stacks and the normal stacks.
final int layerForHomeAnimationLayer = layer++;
- int layerForSplitScreenDividerAnchor = layer++;
- int layerForAnimationLayer = layer++;
- for (int i = 0; i < mTmpNormalStacks.size(); i++) {
- final ActivityStack s = mTmpNormalStacks.get(i);
- s.assignLayer(t, layer++);
- if (s.inSplitScreenWindowingMode()) {
- // The split screen divider anchor is located above the split screen window.
- layerForSplitScreenDividerAnchor = layer++;
- }
- if (s.isTaskAnimating() || s.isAppTransitioning()) {
- // The animation layer is located above the highest animating stack and no
- // higher.
- layerForAnimationLayer = layer++;
- }
- }
+ mTmpLayerForSplitScreenDividerAnchor = layer++;
+ mTmpLayerForAnimationLayer = layer++;
+ layer = adjustRootTaskLayer(t, mTmpNormalStacks, layer, true /* normalStacks */);
+
// The boosted animation layer is between the normal stacks and the always on top
// stacks.
final int layerForBoostedAnimationLayer = layer++;
- for (int i = 0; i < mTmpAlwaysOnTopStacks.size(); i++) {
- mTmpAlwaysOnTopStacks.get(i).assignLayer(t, layer++);
- }
+ adjustRootTaskLayer(t, mTmpAlwaysOnTopStacks, layer, false /* normalStacks */);
t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
- t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
- t.setLayer(mSplitScreenDividerAnchor, layerForSplitScreenDividerAnchor);
+ t.setLayer(mAppAnimationLayer, mTmpLayerForAnimationLayer);
+ t.setLayer(mSplitScreenDividerAnchor, mTmpLayerForSplitScreenDividerAnchor);
t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
}
+ private int adjustNormalStackLayer(Task s, int layer) {
+ if (s.inSplitScreenWindowingMode()) {
+ // The split screen divider anchor is located above the split screen window.
+ mTmpLayerForSplitScreenDividerAnchor = layer++;
+ }
+ if (s.isTaskAnimating() || s.isAppTransitioning()) {
+ // The animation layer is located above the highest animating stack and no
+ // higher.
+ mTmpLayerForAnimationLayer = layer++;
+ }
+ return layer;
+ }
+
+ /**
+ * Adjusts the layer of the stack which belongs to the same group.
+ * Note that there are three stack groups: home stacks, always on top stacks, and normal stacks.
+ *
+ * @param startLayer The beginning layer of this group of stacks.
+ * @param normalStacks Set {@code true} if this group is neither home nor always on top.
+ * @return The adjusted layer value.
+ */
+ private int adjustRootTaskLayer(SurfaceControl.Transaction t, ArrayList<Task> stacks,
+ int startLayer, boolean normalStacks) {
+ mTmpNeedsZBoostIndexes.clear();
+ final int stackSize = stacks.size();
+ for (int i = 0; i < stackSize; i++) {
+ final Task stack = stacks.get(i);
+ if (!stack.needsZBoost()) {
+ stack.assignLayer(t, startLayer++);
+ if (normalStacks) {
+ startLayer = adjustNormalStackLayer(stack, startLayer);
+ }
+ } else {
+ mTmpNeedsZBoostIndexes.add(i);
+ }
+ }
+
+ final int zBoostSize = mTmpNeedsZBoostIndexes.size();
+ for (int i = 0; i < zBoostSize; i++) {
+ final Task stack = stacks.get(mTmpNeedsZBoostIndexes.get(i));
+ stack.assignLayer(t, startLayer++);
+ if (normalStacks) {
+ startLayer = adjustNormalStackLayer(stack, startLayer);
+ }
+ }
+ return startLayer;
+ }
+
@Override
SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
switch (animationLayer) {
@@ -682,15 +776,19 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
super.onParentChanged(newParent, oldParent, () -> {
mAppAnimationLayer = makeChildSurface(null)
.setName("animationLayer")
+ .setCallsite("TaskDisplayArea.onParentChanged")
.build();
mBoostedAppAnimationLayer = makeChildSurface(null)
.setName("boostedAnimationLayer")
+ .setCallsite("TaskDisplayArea.onParentChanged")
.build();
mHomeAppAnimationLayer = makeChildSurface(null)
.setName("homeAnimationLayer")
+ .setCallsite("TaskDisplayArea.onParentChanged")
.build();
mSplitScreenDividerAnchor = makeChildSurface(null)
.setName("splitScreenDividerAnchor")
+ .setCallsite("TaskDisplayArea.onParentChanged")
.build();
getSyncTransaction()
.show(mAppAnimationLayer)
@@ -713,7 +811,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
}
- void onStackRemoved(ActivityStack stack) {
+ void onStackRemoved(Task stack) {
if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId="
+ mDisplayContent.mDisplayId);
@@ -732,95 +830,35 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
}
- void positionStackAt(int position, ActivityStack child, boolean includingParents) {
- positionChildAt(position, child, includingParents);
- mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
- }
-
- void positionStackAtTop(ActivityStack stack, boolean includingParents) {
- positionStackAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
- }
-
- void positionStackAtTop(ActivityStack stack, boolean includingParents,
- String updateLastFocusedStackReason) {
- positionStackAt(stack, getStackCount(), includingParents,
- updateLastFocusedStackReason);
- }
-
- void positionStackAtBottom(ActivityStack stack) {
- positionStackAtBottom(stack, null /* updateLastFocusedStackReason */);
- }
-
- void positionStackAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
- positionStackAt(stack, 0, false /* includingParents */,
- updateLastFocusedStackReason);
- }
-
- void positionStackAt(ActivityStack stack, int position) {
- positionStackAt(stack, position, false /* includingParents */,
- null /* updateLastFocusedStackReason */);
- }
-
- void positionStackAt(ActivityStack stack, int position, boolean includingParents,
- String updateLastFocusedStackReason) {
- // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
- // the position internally, also update the logic here
- final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
- ? getFocusedStack() : null;
- final boolean wasContained = mChildren.contains(stack);
- if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1 && !wasContained) {
- throw new IllegalStateException(
- "positionStackAt: Can only have one task on display=" + this);
- }
-
- // Since positionChildAt() is called during the creation process of pinned stacks,
- // ActivityStack#getStack() can be null.
- positionStackAt(position, stack, includingParents);
-
- if (updateLastFocusedStackReason != null) {
- final ActivityStack currentFocusedStack = getFocusedStack();
- if (currentFocusedStack != prevFocusedStack) {
- mLastFocusedStack = prevFocusedStack;
- EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
- mDisplayContent.mDisplayId,
- currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
- mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
- updateLastFocusedStackReason);
- }
- }
-
- onStackOrderChanged(stack);
- }
-
/**
* Moves/reparents `task` to the back of whatever container the home stack is in. This is for
* when we just want to move a task to "the back" vs. a specific place. The primary use-case
* is to make sure that moved-to-back apps go into secondary split when in split-screen mode.
*/
- void positionTaskBehindHome(ActivityStack task) {
- final ActivityStack home = getOrCreateRootHomeTask();
+ void positionTaskBehindHome(Task task) {
+ final Task home = getOrCreateRootHomeTask();
final WindowContainer homeParent = home.getParent();
final Task homeParentTask = homeParent != null ? homeParent.asTask() : null;
if (homeParentTask == null) {
// reparent throws if parent didn't change...
if (task.getParent() == this) {
- positionStackAtBottom(task);
+ positionChildAt(POSITION_BOTTOM, task, false /*includingParents*/);
} else {
task.reparent(this, false /* onTop */);
}
} else if (homeParentTask == task.getParent()) {
// Apparently reparent early-outs if same stack, so we have to explicitly reorder.
- ((ActivityStack) homeParentTask).positionChildAtBottom(task);
+ homeParentTask.positionChildAtBottom(task);
} else {
- task.reparent((ActivityStack) homeParentTask, false /* toTop */,
+ task.reparent(homeParentTask, false /* toTop */,
Task.REPARENT_LEAVE_STACK_IN_PLACE, false /* animate */,
false /* deferResume */, "positionTaskBehindHome");
}
}
- ActivityStack getStack(int rootTaskId) {
+ Task getStack(int rootTaskId) {
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
+ final Task stack = getStackAt(i);
if (stack.getRootTaskId() == rootTaskId) {
return stack;
}
@@ -833,7 +871,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* if a compatible stack doesn't exist.
* @see #getOrCreateStack(int, int, boolean, Intent, Task)
*/
- ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
+ Task getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
null /* candidateTask */);
}
@@ -846,19 +884,19 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* @see #getStack(int, int)
* @see #createStack(int, int, boolean)
*/
- ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
+ Task getOrCreateStack(int windowingMode, int activityType, boolean onTop,
Intent intent, Task candidateTask) {
// Need to pass in a determined windowing mode to see if a new stack should be created,
// so use its parent's windowing mode if it is undefined.
if (!alwaysCreateStack(
windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : getWindowingMode(),
activityType)) {
- ActivityStack stack = getStack(windowingMode, activityType);
+ Task stack = getStack(windowingMode, activityType);
if (stack != null) {
return stack;
}
} else if (candidateTask != null) {
- final ActivityStack stack = (ActivityStack) candidateTask;
+ final Task stack = candidateTask;
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
Task launchRootTask = updateLaunchRootTask(windowingMode);
@@ -890,7 +928,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* if a compatible stack doesn't exist.
* @see #getOrCreateStack(int, int, boolean)
*/
- ActivityStack getOrCreateStack(@Nullable ActivityRecord r,
+ Task getOrCreateStack(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
boolean onTop) {
// First preference is the windowing mode in the activity options if set.
@@ -909,7 +947,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return mAtmService.mStackSupervisor.getNextTaskIdForUser();
}
- ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
+ Task createStack(int windowingMode, int activityType, boolean onTop) {
return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
false /* createdByOrganizer */);
}
@@ -929,7 +967,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* otherwise.
* @return The newly created stack.
*/
- ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
+ Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
Intent intent, boolean createdByOrganizer) {
if (mDisplayContent.mSingleTaskInstance && getStackCount() > 0) {
// Create stack on default display instead since this display can only contain 1 stack.
@@ -948,7 +986,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
// For now there can be only one stack of a particular non-standard activity type on a
// display. So, get that ignoring whatever windowing mode it is currently in.
- ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ Task stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
if (stack != null) {
throw new IllegalArgumentException("Stack=" + stack + " of activityType="
+ activityType + " already on display=" + this + ". Can't have multiple.");
@@ -1000,7 +1038,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
@VisibleForTesting
- ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
+ Task createStackUnchecked(int windowingMode, int activityType, int stackId,
boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
@@ -1018,12 +1056,12 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
windowingMode = WINDOWING_MODE_UNDEFINED;
}
- final ActivityStack stack = new ActivityStack(mAtmService, stackId, activityType,
+ final Task stack = new Task(mAtmService, stackId, activityType,
info, intent, createdByOrganizer);
if (launchRootTask != null) {
launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
if (onTop) {
- positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
+ positionChildAt(POSITION_TOP, launchRootTask, false /* includingParents */);
}
} else {
addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
@@ -1036,13 +1074,13 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
* focusable and visible stack from the top of stacks in this display.
*/
- ActivityStack getFocusedStack() {
+ Task getFocusedStack() {
if (mPreferredTopFocusableStack != null) {
return mPreferredTopFocusableStack;
}
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
+ final Task stack = getStackAt(i);
if (stack.isFocusableAndVisible()) {
return stack;
}
@@ -1051,13 +1089,13 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return null;
}
- ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
+ Task getNextFocusableStack(Task currentFocus, boolean ignoreCurrent) {
final int currentWindowingMode = currentFocus != null
? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
- ActivityStack candidate = null;
+ Task candidate = null;
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
+ final Task stack = getStackAt(i);
if (ignoreCurrent && stack == currentFocus) {
continue;
}
@@ -1087,7 +1125,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
ActivityRecord getFocusedActivity() {
- final ActivityStack focusedStack = getFocusedStack();
+ final Task focusedStack = getFocusedStack();
if (focusedStack == null) {
return null;
}
@@ -1107,7 +1145,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return resumedActivity;
}
- ActivityStack getLastFocusedStack() {
+ Task getLastFocusedStack() {
return mLastFocusedStack;
}
@@ -1118,7 +1156,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return false;
}
}
- final ActivityStack currentFocusedStack = getFocusedStack();
+ final Task currentFocusedStack = getFocusedStack();
if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
Slog.d(TAG_STACK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
+ mLastFocusedStack + " to=" + currentFocusedStack);
@@ -1140,7 +1178,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming) {
boolean someActivityPaused = false;
for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getStackAt(stackNdx);
+ final Task stack = getStackAt(stackNdx);
final ActivityRecord resumedActivity = stack.getResumedActivity();
if (resumedActivity != null
&& (stack.getVisibility(resuming) != STACK_VISIBILITY_VISIBLE
@@ -1163,7 +1201,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
RootWindowContainer.FindTaskResult result) {
mTmpFindTaskResult.clear();
for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getStackAt(stackNdx);
+ final Task stack = getStackAt(stackNdx);
if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) {
if (DEBUG_TASKS) {
Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
@@ -1203,11 +1241,11 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
// Collect the stacks that are necessary to be removed instead of performing the removal
// by looping mStacks, so that we don't miss any stacks after the stack size changed or
// stacks reordered.
- final ArrayList<ActivityStack> stacks = new ArrayList<>();
+ final ArrayList<Task> stacks = new ArrayList<>();
for (int j = windowingModes.length - 1; j >= 0; --j) {
final int windowingMode = windowingModes[j];
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
+ final Task stack = getStackAt(i);
if (!stack.isActivityTypeStandardOrUndefined()) {
continue;
}
@@ -1231,15 +1269,15 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
// Collect the stacks that are necessary to be removed instead of performing the removal
// by looping mStacks, so that we don't miss any stacks after the stack size changed or
// stacks reordered.
- final ArrayList<ActivityStack> stacks = new ArrayList<>();
+ final ArrayList<Task> stacks = new ArrayList<>();
for (int j = activityTypes.length - 1; j >= 0; --j) {
final int activityType = activityTypes[j];
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
+ final Task stack = getStackAt(i);
// Collect the root tasks that are currently being organized.
if (stack.mCreatedByOrganizer) {
for (int k = stack.getChildCount() - 1; k >= 0; --k) {
- final ActivityStack childStack = (ActivityStack) stack.getChildAt(k);
+ final Task childStack = (Task) stack.getChildAt(k);
if (childStack.getActivityType() == activityType) {
stacks.add(childStack);
}
@@ -1259,15 +1297,15 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
onSplitScreenModeDismissed(null /* toTop */);
}
- void onSplitScreenModeDismissed(ActivityStack toTop) {
+ void onSplitScreenModeDismissed(Task toTop) {
mAtmService.deferWindowLayout();
try {
mLaunchRootTask = null;
moveSplitScreenTasksToFullScreen();
} finally {
- final ActivityStack topFullscreenStack = toTop != null
+ final Task topFullscreenStack = toTop != null
? toTop : getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final ActivityStack homeStack = getOrCreateRootHomeTask();
+ final Task homeStack = getOrCreateRootHomeTask();
if (homeStack != null && ((topFullscreenStack != null && !isTopStack(homeStack))
|| toTop != null)) {
// Whenever split-screen is dismissed we want the home stack directly behind the
@@ -1441,13 +1479,13 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return windowingMode;
}
- boolean isTopStack(ActivityStack stack) {
+ boolean isTopStack(Task stack) {
return stack == getTopStack();
}
- boolean isTopNotPinnedStack(ActivityStack stack) {
+ boolean isTopNotPinnedStack(Task stack) {
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack current = getStackAt(i);
+ final Task current = getStackAt(i);
if (!current.inPinnedWindowingMode()) {
return current == stack;
}
@@ -1470,7 +1508,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
*/
ActivityRecord topRunningActivity(boolean considerKeyguardState) {
ActivityRecord topRunning = null;
- final ActivityStack focusedStack = getFocusedStack();
+ final Task focusedStack = getFocusedStack();
if (focusedStack != null) {
topRunning = focusedStack.topRunningActivity();
}
@@ -1478,7 +1516,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
// Look in other focusable stacks.
if (topRunning == null) {
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
+ final Task stack = getStackAt(i);
// Only consider focusable stacks other than the current focused one.
if (stack == focusedStack || !stack.isTopActivityFocusable()) {
continue;
@@ -1506,12 +1544,12 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return mChildren.size();
}
- protected ActivityStack getStackAt(int index) {
+ protected Task getStackAt(int index) {
return mChildren.get(index);
}
@Nullable
- ActivityStack getOrCreateRootHomeTask() {
+ Task getOrCreateRootHomeTask() {
return getOrCreateRootHomeTask(false /* onTop */);
}
@@ -1522,8 +1560,8 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* be created at the top of the display, else at the bottom.
*/
@Nullable
- ActivityStack getOrCreateRootHomeTask(boolean onTop) {
- ActivityStack homeTask = getRootHomeTask();
+ Task getOrCreateRootHomeTask(boolean onTop) {
+ Task homeTask = getRootHomeTask();
if (homeTask == null && mDisplayContent.supportsSystemDecorations()) {
homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
}
@@ -1539,12 +1577,12 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* Returns the topmost stack on the display that is compatible with the input windowing mode.
* Null is no compatible stack on the display.
*/
- ActivityStack getTopStackInWindowingMode(int windowingMode) {
+ Task getTopStackInWindowingMode(int windowingMode) {
return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
}
void moveHomeStackToFront(String reason) {
- final ActivityStack homeStack = getOrCreateRootHomeTask();
+ final Task homeStack = getOrCreateRootHomeTask();
if (homeStack != null) {
homeStack.moveToFront(reason);
}
@@ -1570,7 +1608,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
@Nullable
ActivityRecord getHomeActivityForUser(int userId) {
- final ActivityStack homeStack = getRootHomeTask();
+ final Task homeStack = getRootHomeTask();
if (homeStack == null) {
return null;
}
@@ -1592,25 +1630,21 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* Generally used in conjunction with {@link #moveStackBehindStack}.
*/
// TODO(b/151575894): Remove special stack movement methods.
- void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
+ void moveStackBehindBottomMostVisibleStack(Task stack) {
if (stack.shouldBeVisible(null)) {
// Skip if the stack is already visible
return;
}
- final boolean isRootTask = stack.isRootTask();
- if (isRootTask) {
- // Move the stack to the bottom to not affect the following visibility checks
- positionStackAtBottom(stack);
- } else {
- stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
- }
+ // Move the stack to the bottom to not affect the following visibility checks
+ stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
// Find the next position where the stack should be placed
+ final boolean isRootTask = stack.isRootTask();
final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
- final ActivityStack s = isRootTask ? getStackAt(stackNdx)
- : (ActivityStack) stack.getParent().getChildAt(stackNdx);
+ final Task s = isRootTask ? getStackAt(stackNdx)
+ : (Task) stack.getParent().getChildAt(stackNdx);
if (s == stack) {
continue;
}
@@ -1620,11 +1654,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
if (s.shouldBeVisible(null) && isValidWindowingMode) {
// Move the provided stack to behind this stack
final int position = Math.max(0, stackNdx - 1);
- if (isRootTask) {
- positionStackAt(stack, position);
- } else {
- stack.getParent().positionChildAt(position, stack, false /*includingParents */);
- }
+ stack.getParent().positionChildAt(position, stack, false /*includingParents */);
break;
}
}
@@ -1635,7 +1665,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* {@param behindStack} is not currently in the display, then then the stack is moved to the
* back. Generally used in conjunction with {@link #moveStackBehindBottomMostVisibleStack}.
*/
- void moveStackBehindStack(ActivityStack stack, ActivityStack behindStack) {
+ void moveStackBehindStack(Task stack, Task behindStack) {
if (behindStack == null || behindStack == stack) {
return;
}
@@ -1654,11 +1684,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
final int insertIndex = stackIndex <= behindStackIndex
? behindStackIndex - 1 : behindStackIndex;
final int position = Math.max(0, insertIndex);
- if (stack.isRootTask()) {
- positionStackAt(stack, position);
- } else {
- parent.positionChildAt(position, stack, false /* includingParents */);
- }
+ parent.positionChildAt(position, stack, false /* includingParents */);
}
boolean hasPinnedTask() {
@@ -1669,19 +1695,19 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* @return the stack currently above the {@param stack}. Can be null if the {@param stack} is
* already top-most.
*/
- static ActivityStack getStackAbove(ActivityStack stack) {
+ static Task getStackAbove(Task stack) {
final WindowContainer wc = stack.getParent();
final int index = wc.mChildren.indexOf(stack) + 1;
- return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null;
+ return (index < wc.mChildren.size()) ? (Task) wc.mChildren.get(index) : null;
}
/** Returns true if the stack in the windowing mode is visible. */
boolean isStackVisible(int windowingMode) {
- final ActivityStack stack = getTopStackInWindowingMode(windowingMode);
+ final Task stack = getTopStackInWindowingMode(windowingMode);
return stack != null && stack.isVisible();
}
- void removeStack(ActivityStack stack) {
+ void removeStack(Task stack) {
removeChild(stack);
}
@@ -1715,7 +1741,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* Notifies of a stack order change
* @param stack The stack which triggered the order change
*/
- void onStackOrderChanged(ActivityStack stack) {
+ void onStackOrderChanged(Task stack) {
for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) {
mStackOrderChangedCallbacks.get(i).onStackOrderChanged(stack);
}
@@ -1730,21 +1756,26 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* Callback for when the order of the stacks in the display changes.
*/
interface OnStackOrderChangedListener {
- void onStackOrderChanged(ActivityStack stack);
+ void onStackOrderChanged(Task stack);
}
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
- for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getStackAt(stackNdx);
- stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
- notifyClients);
+ mAtmService.mStackSupervisor.beginActivityVisibilityUpdate();
+ try {
+ for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final Task stack = getStackAt(stackNdx);
+ stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
+ notifyClients);
+ }
+ } finally {
+ mAtmService.mStackSupervisor.endActivityVisibilityUpdate();
}
}
void prepareFreezingTaskBounds() {
for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getChildAt(stackNdx);
+ final Task stack = getChildAt(stackNdx);
stack.prepareFreezingTaskBounds();
}
}
@@ -1753,12 +1784,12 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* Removes the stacks in the node applying the content removal node from the display.
* @return last reparented stack, or {@code null} if the stacks had to be destroyed.
*/
- ActivityStack remove() {
+ Task remove() {
mPreferredTopFocusableStack = null;
// TODO(b/153090332): Allow setting content removal mode per task display area
final boolean destroyContentOnRemoval = mDisplayContent.shouldDestroyContentOnRemove();
final TaskDisplayArea toDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack lastReparentedStack = null;
+ Task lastReparentedStack = null;
// Stacks could be reparented from the removed display area to other display area. After
// reparenting the last stack of the removed display area, the display area becomes ready to
@@ -1769,10 +1800,10 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
int numStacks = getStackCount();
final boolean splitScreenActivated = toDisplayArea.isSplitScreenModeActivated();
- final ActivityStack rootStack = splitScreenActivated ? toDisplayArea
+ final Task rootStack = splitScreenActivated ? toDisplayArea
.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) : null;
for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
- final ActivityStack stack = getStackAt(stackNdx);
+ final Task stack = getStackAt(stackNdx);
// Always finish non-standard type stacks.
if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
stack.finishAllActivitiesImmediately();
@@ -1811,18 +1842,18 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
pw.println(prefix + "TaskDisplayArea " + getName());
- super.dump(pw, prefix, dumpAll);
+ final String doublePrefix = prefix + " ";
+ super.dump(pw, doublePrefix, dumpAll);
if (mPreferredTopFocusableStack != null) {
- pw.println(prefix + " mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+ pw.println(doublePrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
}
if (mLastFocusedStack != null) {
- pw.println(prefix + " mLastFocusedStack=" + mLastFocusedStack);
+ pw.println(doublePrefix + "mLastFocusedStack=" + mLastFocusedStack);
}
- final String doublePrefix = prefix + " ";
final String triplePrefix = doublePrefix + " ";
pw.println(doublePrefix + "Application tokens in top down Z order:");
for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getChildAt(stackNdx);
+ final Task stack = getChildAt(stackNdx);
pw.println(doublePrefix + "* " + stack);
stack.dump(pw, triplePrefix, dumpAll);
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 11c20b6d9133..9a818ce80872 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -341,8 +341,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
}
- ActivityStack stack = (taskDisplayArea == null && task != null)
- ? task.getStack() : null;
+ Task stack = (taskDisplayArea == null && task != null)
+ ? task.getRootTask() : null;
if (stack != null) {
if (DEBUG) appendLog("display-from-task=" + stack.getDisplayId());
taskDisplayArea = stack.getDisplayArea();
@@ -741,21 +741,19 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
private void adjustBoundsToAvoidConflictInDisplay(@NonNull DisplayContent display,
@NonNull Rect inOutBounds) {
final List<Rect> taskBoundsToCheck = new ArrayList<>();
- int numTaskContainers = display.getTaskDisplayAreaCount();
- for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ display.forAllTaskDisplayAreas(taskDisplayArea -> {
int numStacks = taskDisplayArea.getStackCount();
for (int sNdx = 0; sNdx < numStacks; ++sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (!stack.inFreeformWindowingMode()) {
+ final Task task = taskDisplayArea.getStackAt(sNdx);
+ if (!task.inFreeformWindowingMode()) {
continue;
}
- for (int j = 0; j < stack.getChildCount(); ++j) {
- taskBoundsToCheck.add(stack.getChildAt(j).getBounds());
+ for (int j = 0; j < task.getChildCount(); ++j) {
+ taskBoundsToCheck.add(task.getChildAt(j).getBounds());
}
}
- }
+ }, false /* traverseTopToBottom */);
adjustBoundsToAvoidConflict(display.getBounds(), taskBoundsToCheck, inOutBounds);
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 574f37b87443..04d134c3649d 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -117,7 +117,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
final RunningTaskInfo taskInfo = task.getTaskInfo();
mDeferTaskOrgCallbacksConsumer.accept(() -> {
try {
- SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl());
+ SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(),
+ "TaskOrganizerController.onTaskAppeared");
if (!task.mCreatedByOrganizer && !visible) {
// To prevent flashes, we hide the task prior to sending the leash to the
// task org if the task has previously hidden (ie. when entering PIP)
@@ -602,8 +603,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
throw new IllegalArgumentException("Display " + displayId + " doesn't exist");
}
ArrayList<RunningTaskInfo> out = new ArrayList<>();
- for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+ dc.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final Task task = taskDisplayArea.getStackAt(sNdx);
if (activityTypes != null
@@ -612,7 +612,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
out.add(task.getTaskInfo());
}
- }
+ });
return out;
}
} finally {
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index d343daf46f13..b9a449f558f0 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -86,7 +86,9 @@ class TaskPositioningController {
if (mInputSurface == null) {
mInputSurface = mService.makeSurfaceBuilder(dc.getSession())
.setContainerLayer()
- .setName("Drag and Drop Input Consumer").build();
+ .setName("Drag and Drop Input Consumer")
+ .setCallsite("TaskPositioningController.showInputSurface")
+ .build();
}
final InputWindowHandle h = getDragWindowHandleLocked();
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
index 1424c8d13252..1103bf19b3bf 100644
--- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
+++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
@@ -52,6 +52,7 @@ class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable {
mSurfaceControl = surfaceControlFactory.apply(new SurfaceSession())
.setName("RecentTaskScreenshotSurface")
.setBufferSize(mWidth, mHeight)
+ .setCallsite("TaskScreenshotAnimatable")
.build();
if (buffer != null) {
final Surface surface = new Surface();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 37c3afb3854f..68445f6970fb 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -40,6 +40,7 @@ import android.util.ArraySet;
import android.util.Slog;
import android.view.InsetsSource;
import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl;
import android.view.ThreadedRenderer;
import android.view.WindowInsets;
@@ -480,7 +481,9 @@ class TaskSnapshotController {
task.getTaskDescription().getBackgroundColor(), 255);
final LayoutParams attrs = mainWindow.getAttrs();
final InsetsPolicy insetsPolicy = mainWindow.getDisplayContent().getInsetsPolicy();
- final InsetsState insetsState = insetsPolicy.getInsetsForDispatch(mainWindow);
+ final InsetsState insetsState =
+ new InsetsState(insetsPolicy.getInsetsForDispatch(mainWindow));
+ mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState());
final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrameLw(), insetsState);
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
@@ -600,11 +603,21 @@ class TaskSnapshotController {
return 0;
}
+ static void mergeInsetsSources(InsetsState base, InsetsState other) {
+ for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
+ final InsetsSource source = other.peekSource(type);
+ if (source != null) {
+ base.addSource(source);
+ }
+ }
+ }
+
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
return state.calculateInsets(frame, null /* ignoringVisibilityState */,
false /* isScreenRound */, false /* alwaysConsumeSystemBars */,
- null /* displayCutout */, 0 /* legacySoftInputMode */, 0 /* legacySystemUiFlags */,
- null /* typeSideMap */).getInsets(WindowInsets.Type.systemBars()).toRect();
+ null /* displayCutout */, 0 /* legacySoftInputMode */, 0 /* legacyWindowFlags */,
+ 0 /* legacySystemUiFlags */, null /* typeSideMap */).getInsets(
+ WindowInsets.Type.systemBars()).toRect();
}
void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index ebe587449b25..f3c7a5dcb6d5 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -42,6 +42,7 @@ import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIB
import static com.android.internal.policy.DecorView.getNavigationBarRect;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
import static com.android.server.wm.TaskSnapshotController.getSystemBarInsets;
+import static com.android.server.wm.TaskSnapshotController.mergeInsetsSources;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -243,7 +244,9 @@ class TaskSnapshotSurface implements StartingSurface {
final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent()
.getInsetsPolicy();
- insetsState = insetsPolicy.getInsetsForDispatch(topFullscreenOpaqueWindow);
+ insetsState =
+ new InsetsState(insetsPolicy.getInsetsForDispatch(topFullscreenOpaqueWindow));
+ mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState());
}
try {
final int res = session.addToDisplay(window, window.mSeq, layoutParams,
@@ -375,6 +378,7 @@ class TaskSnapshotSurface implements StartingSurface {
.setBufferSize(buffer.getWidth(), buffer.getHeight())
.setFormat(buffer.getFormat())
.setParent(mSurfaceControl)
+ .setCallsite("TaskSnapshotSurface.drawSizeMismatchSnapshot")
.build();
Surface surface = mService.mSurfaceFactory.get();
surface.copyFrom(mChildSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 132d00ac91c1..c27d0cda22ab 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.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -24,6 +25,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIG
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -44,7 +46,13 @@ class WallpaperWindowToken extends WindowToken {
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
DisplayContent dc, boolean ownerCanManageAppTokens) {
- super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens);
+ this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */);
+ }
+
+ WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
+ DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) {
+ super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID,
+ false /* roundedCornerOverlay */, false /* fromClientToken */, options);
dc.mWallpaperController.addWallpaperToken(this);
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 3d49ebe306e6..7f28ffc5634c 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -121,6 +121,7 @@ class Watermark {
.setName("WatermarkSurface")
.setBufferSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
+ .setCallsite("Watermark")
.build();
t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 100)
.setPosition(ctrl, 0, 0)
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1a6d85547c92..d0785ff9a9ac 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -43,6 +43,7 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER;
+import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
import static com.android.server.wm.WindowContainerProto.ORIENTATION;
import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
import static com.android.server.wm.WindowContainerProto.VISIBLE;
@@ -94,6 +95,7 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.Set;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -110,16 +112,16 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
- /** Animation layer that happens above all animating {@link ActivityStack}s. */
+ /** Animation layer that happens above all animating {@link Task}s. */
static final int ANIMATION_LAYER_STANDARD = 0;
- /** Animation layer that happens above all {@link ActivityStack}s. */
+ /** Animation layer that happens above all {@link Task}s. */
static final int ANIMATION_LAYER_BOOSTED = 1;
/**
* Animation layer that is reserved for {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
* activities and all activities that are being controlled by the recents animation. This
- * layer is generally below all {@link ActivityStack}s.
+ * layer is generally below all {@link Task}s.
*/
static final int ANIMATION_LAYER_HOME = 2;
@@ -188,7 +190,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
/**
* Sources which triggered a surface animation on this container. An animation target can be
* promoted to higher level, for example, from a set of {@link ActivityRecord}s to
- * {@link ActivityStack}. In this case, {@link ActivityRecord}s are set on this variable while
+ * {@link Task}. In this case, {@link ActivityRecord}s are set on this variable while
* the animation is running, and reset after finishing it.
*/
private final ArraySet<WindowContainer> mSurfaceAnimationSources = new ArraySet<>();
@@ -413,7 +415,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
void setInitialSurfaceControlProperties(SurfaceControl.Builder b) {
- setSurfaceControl(b.build());
+ setSurfaceControl(b.setCallsite("WindowContainer.setInitialSurfaceControlProperties").build());
getSyncTransaction().show(mSurfaceControl);
onSurfaceShown(getSyncTransaction());
updateSurfacePosition();
@@ -683,7 +685,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
@CallSuper
void positionChildAt(int position, E child, boolean includingParents) {
-
if (child.getParent() != this) {
throw new IllegalArgumentException("positionChildAt: container=" + child.getName()
+ " is not a child of container=" + getName()
@@ -993,11 +994,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* changes (eg. a transition animation might play out first).
*/
void onChildVisibilityRequested(boolean visible) {
- // If we are changing visibility, then a snapshot isn't necessary and we are no-longer
+ // If we are losing visibility, then a snapshot isn't necessary and we are no-longer
// part of a change transition.
- mSurfaceFreezer.unfreeze(getSyncTransaction());
- if (mDisplayContent != null) {
- mDisplayContent.mChangingContainers.remove(this);
+ if (!visible) {
+ mSurfaceFreezer.unfreeze(getSyncTransaction());
+ if (mDisplayContent != null) {
+ mDisplayContent.mChangingContainers.remove(this);
+ }
}
WindowContainer parent = getParent();
if (parent != null) {
@@ -1044,13 +1047,25 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return mChildren.peekLast();
}
- /** Returns true if there is still a removal being deferred */
- boolean checkCompleteDeferredRemoval() {
+ /**
+ * Removes the containers which were deferred.
+ *
+ * @return {@code true} if there is still a removal being deferred.
+ */
+ boolean handleCompleteDeferredRemoval() {
boolean stillDeferringRemoval = false;
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
- stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
+ stillDeferringRemoval |= wc.handleCompleteDeferredRemoval();
+ if (!hasChild()) {
+ // All child containers of current level could be removed from a removal of
+ // descendant. E.g. if a display is pending to be removed because it contains an
+ // activity with {@link ActivityRecord#mIsExiting} is true, the display may be
+ // removed when completing the removal of the last activity from
+ // {@link ActivityRecord#checkCompleteDeferredRemoval}.
+ return false;
+ }
}
return stillDeferringRemoval;
@@ -1718,6 +1733,145 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
+ * For all {@link TaskDisplayArea} at or below this container call the callback.
+ * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
+ * returns {@code true}.
+ * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
+ * terms of z-order, else from bottom-to-top.
+ * @return {@code true} if the search ended before we reached the end of the hierarchy due to
+ * callback returning {@code true}.
+ */
+ boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
+ boolean traverseTopToBottom) {
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ if (mChildren.get(i).forAllTaskDisplayAreas(callback, traverseTopToBottom)) {
+ return true;
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return false;
+ }
+
+ /**
+ * For all {@link TaskDisplayArea} at or below this container call the callback. Traverses from
+ * top to bottom in terms of z-order.
+ * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
+ * returns {@code true}.
+ * @return {@code true} if the search ended before we reached the end of the hierarchy due to
+ * callback returning {@code true}.
+ */
+ boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback) {
+ return forAllTaskDisplayAreas(callback, true /* traverseTopToBottom */);
+ }
+
+ /**
+ * For all {@link TaskDisplayArea} at or below this container call the callback.
+ * @param callback Applies on each {@link TaskDisplayArea} found.
+ * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
+ * terms of z-order, else from bottom-to-top.
+ */
+ void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) {
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ mChildren.get(i).forAllTaskDisplayAreas(callback, traverseTopToBottom);
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ }
+
+ /**
+ * For all {@link TaskDisplayArea} at or below this container call the callback. Traverses from
+ * top to bottom in terms of z-order.
+ * @param callback Applies on each {@link TaskDisplayArea} found.
+ */
+ void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback) {
+ forAllTaskDisplayAreas(callback, true /* traverseTopToBottom */);
+ }
+
+ /**
+ * Performs a reduction on all {@link TaskDisplayArea} at or below this container, using the
+ * provided initial value and an accumulation function, and returns the reduced value.
+ * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result
+ * from the previous call.
+ * @param initValue The initial value to pass to the accumulating function with the first
+ * {@link TaskDisplayArea}.
+ * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
+ * terms of z-order, else from bottom-to-top.
+ * @return the accumulative result.
+ */
+ @Nullable
+ <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator,
+ @Nullable R initValue, boolean traverseTopToBottom) {
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ R result = initValue;
+ while (i >= 0 && i < childCount) {
+ result = (R) mChildren.get(i)
+ .reduceOnAllTaskDisplayAreas(accumulator, result, traverseTopToBottom);
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return result;
+ }
+
+ /**
+ * Performs a reduction on all {@link TaskDisplayArea} at or below this container, using the
+ * provided initial value and an accumulation function, and returns the reduced value. Traverses
+ * from top to bottom in terms of z-order.
+ * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result
+ * from the previous call.
+ * @param initValue The initial value to pass to the accumulating function with the first
+ * {@link TaskDisplayArea}.
+ * @return the accumulative result.
+ */
+ @Nullable
+ <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator,
+ @Nullable R initValue) {
+ return reduceOnAllTaskDisplayAreas(accumulator, initValue, true /* traverseTopToBottom */);
+ }
+
+ /**
+ * Finds the first non {@code null} return value from calling the callback on all
+ * {@link TaskDisplayArea} at or below this container.
+ * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
+ * returns non {@code null}.
+ * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
+ * terms of z-order, else from bottom-to-top.
+ * @return the first returned object that is not {@code null}. Returns {@code null} if not
+ * found.
+ */
+ @Nullable
+ <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
+ boolean traverseTopToBottom) {
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ R result = (R) mChildren.get(i)
+ .getItemFromTaskDisplayAreas(callback, traverseTopToBottom);
+ if (result != null) {
+ return result;
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return null;
+ }
+
+ /**
+ * Finds the first non {@code null} return value from calling the callback on all
+ * {@link TaskDisplayArea} at or below this container. Traverses from top to bottom in terms of
+ * z-order.
+ * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
+ * returns non {@code null}.
+ * @return the first returned object that is not {@code null}. Returns {@code null} if not
+ * found.
+ */
+ @Nullable
+ <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback) {
+ return getItemFromTaskDisplayAreas(callback, true /* traverseTopToBottom */);
+ }
+
+ /**
* Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
* the input container in terms of z-order.
*/
@@ -1928,6 +2082,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
super.dumpDebug(proto, CONFIGURATION_CONTAINER, logLevel);
proto.write(ORIENTATION, mOrientation);
proto.write(VISIBLE, isVisible);
+ writeIdentifierToProto(proto, IDENTIFIER);
if (mSurfaceAnimator.isAnimating()) {
mSurfaceAnimator.dumpDebug(proto, SURFACE_ANIMATOR);
}
@@ -2428,6 +2583,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
doAnimationFinished(type, anim);
mWmService.onAnimationFinished();
+ mNeedsZBoost = false;
}
/**
@@ -2542,6 +2698,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
pw.print(prefix); pw.println("ContainerAnimator:");
mSurfaceAnimator.dump(pw, prefix + " ");
}
+ if (mLastOrientationSource != null) {
+ pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
+ }
}
final void updateSurfacePosition() {
@@ -2638,6 +2797,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return null;
}
+ /** Cheap way of doing cast and instanceof. */
+ DisplayArea asDisplayArea() {
+ return null;
+ }
+
/**
* @return {@code true} if window container is manage by a
* {@link android.window.WindowOrganizer}
diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
index 0fb198908906..b75f886520e6 100644
--- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
+++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
@@ -103,6 +103,7 @@ class WindowContainerThumbnail implements Animatable {
.setFormat(PixelFormat.TRANSLUCENT)
.setMetadata(METADATA_WINDOW_TYPE, mWindowContainer.getWindowingMode())
.setMetadata(METADATA_OWNER_UID, Process.myUid())
+ .setCallsite("WindowContainerThumbnail")
.build();
ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 97186b4e9cda..d96b6457f9db 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -57,7 +57,7 @@ public class WindowFrames {
public final Rect mParentFrame = new Rect();
/**
- * The entire screen area of the {@link ActivityStack} this window is in. Usually equal to the
+ * The entire screen area of the {@link Task} this window is in. Usually equal to the
* screen area of the device.
*
* TODO(b/111611553): The name is unclear and most likely should be swapped with
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index c605e3e1ea60..315014c1b248 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -528,8 +528,9 @@ public abstract class WindowManagerInternal {
* Hide IME using imeTargetWindow when requested.
*
* @param imeTargetWindowToken token of the (IME target) window on which IME should be hidden.
+ * @param displayId the id of the display the IME is on.
*/
- public abstract void hideIme(IBinder imeTargetWindowToken);
+ public abstract void hideIme(IBinder imeTargetWindowToken, int displayId);
/**
* Tell window manager about a package that should not be running with high refresh rate
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fbbb2b2ab75c..2dd25c969d0d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -91,6 +91,7 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_BOOT;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
@@ -1477,9 +1478,14 @@ public class WindowManagerService extends IWindowManager.Stub
rootType, attrs.token, attrs.packageName)) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
- final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
- token = new WindowToken(this, binder, type, false, displayContent,
- session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
+ if (hasParent) {
+ // Use existing parent window token for child windows.
+ token = parentWindow.mToken;
+ } else {
+ final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
+ token = new WindowToken(this, binder, type, false, displayContent,
+ session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
+ }
} else if (rootType >= FIRST_APPLICATION_WINDOW
&& rootType <= LAST_APPLICATION_WINDOW) {
activity = token.asActivityRecord();
@@ -1944,7 +1950,7 @@ public class WindowManagerService extends IWindowManager.Stub
// re-factor.
activity.firstWindowDrawn = false;
activity.clearAllDrawn();
- final ActivityStack stack = activity.getStack();
+ final Task stack = activity.getStack();
if (stack != null) {
stack.mExitingActivities.remove(activity);
}
@@ -2164,6 +2170,10 @@ public class WindowManagerService extends IWindowManager.Stub
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
}
+ if (!Arrays.equals(win.mAttrs.providesInsetsTypes, attrs.providesInsetsTypes)) {
+ throw new IllegalArgumentException(
+ "Insets types can not be changed after the window is added.");
+ }
// Odd choice but less odd than embedding in copyFrom()
if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
@@ -2669,10 +2679,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
// TODO(window-container): Clean up dead tokens
if (type == TYPE_WALLPAPER) {
- new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
+ new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens,
+ options);
} else {
new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
- callingUid, false /* roundedCornerOverlay */, fromClientToken);
+ callingUid, false /* roundedCornerOverlay */, fromClientToken, options);
}
}
} finally {
@@ -2857,7 +2868,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
void getStackBounds(int windowingMode, int activityType, Rect bounds) {
- final ActivityStack stack = mRoot.getStack(windowingMode, activityType);
+ final Task stack = mRoot.getStack(windowingMode, activityType);
if (stack != null) {
stack.getBounds(bounds);
return;
@@ -4581,7 +4592,7 @@ public class WindowManagerService extends IWindowManager.Stub
return mRoot.getTopFocusedDisplayContent().mCurrentFocus;
}
- ActivityStack getImeFocusStackLocked() {
+ Task getImeFocusStackLocked() {
// Don't use mCurrentFocus.getStack() because it returns home stack for system windows.
// Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE
// and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME target so they're moved
@@ -4590,7 +4601,7 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent topFocusedDisplay = mRoot.getTopFocusedDisplayContent();
final ActivityRecord focusedApp = topFocusedDisplay.mFocusedApp;
return (focusedApp != null && focusedApp.getTask() != null)
- ? focusedApp.getTask().getStack() : null;
+ ? focusedApp.getTask().getRootTask() : null;
}
public boolean detectSafeMode() {
@@ -5155,8 +5166,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
case WINDOW_STATE_BLAST_SYNC_TIMEOUT: {
synchronized (mGlobalLock) {
- final WindowState ws = (WindowState) msg.obj;
- ws.finishDrawing(null);
+ final WindowState ws = (WindowState) msg.obj;
+ ws.immediatelyNotifyBlastSync();
}
break;
}
@@ -5619,17 +5630,28 @@ public class WindowManagerService extends IWindowManager.Stub
}
final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
- final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig;
- final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0;
- if (waitingForConfig || mAppsFreezingScreen > 0
+ final int numOpeningApps;
+ final boolean waitingForConfig;
+ final boolean waitingForRemoteRotation;
+ if (displayContent != null) {
+ numOpeningApps = displayContent.mOpeningApps.size();
+ waitingForConfig = displayContent.mWaitingForConfig;
+ waitingForRemoteRotation =
+ displayContent.getDisplayRotation().isWaitingForRemoteRotation();
+ } else {
+ waitingForConfig = waitingForRemoteRotation = false;
+ numOpeningApps = 0;
+ }
+ if (waitingForConfig || waitingForRemoteRotation || mAppsFreezingScreen > 0
|| mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
|| mClientFreezingScreen || numOpeningApps > 0) {
- ProtoLog.d(WM_DEBUG_ORIENTATION,
- "stopFreezingDisplayLocked: Returning mWaitingForConfig=%b, "
- + "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, "
- + "mClientFreezingScreen=%b, mOpeningApps.size()=%d",
- waitingForConfig, mAppsFreezingScreen, mWindowsFreezingScreen,
- mClientFreezingScreen, numOpeningApps);
+ ProtoLog.d(WM_DEBUG_ORIENTATION, "stopFreezingDisplayLocked: Returning "
+ + "waitingForConfig=%b, waitingForRemoteRotation=%b, "
+ + "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, "
+ + "mClientFreezingScreen=%b, mOpeningApps.size()=%d",
+ waitingForConfig, waitingForRemoteRotation,
+ mAppsFreezingScreen, mWindowsFreezingScreen,
+ mClientFreezingScreen, numOpeningApps);
return;
}
@@ -5640,7 +5662,6 @@ public class WindowManagerService extends IWindowManager.Stub
// We must make a local copy of the displayId as it can be potentially overwritten later on
// in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
// of update rotation, but we reference the frozen display after that call in this method.
- final int displayId = mFrozenDisplayId;
mFrozenDisplayId = INVALID_DISPLAY;
mDisplayFrozen = false;
mInputManagerCallback.thawInputDispatchingLw();
@@ -5829,27 +5850,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- @Override
- public void setForceShowSystemBars(boolean show) {
- boolean isAutomotive = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- if (!isAutomotive) {
- throw new UnsupportedOperationException("Force showing system bars is only supported"
- + "for Automotive use cases.");
- }
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller does not hold permission "
- + android.Manifest.permission.STATUS_BAR);
- }
- synchronized (mGlobalLock) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::setForceShowSystemBars, PooledLambda.__(), show);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
- }
- }
-
public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
!= PackageManager.PERMISSION_GRANTED) {
@@ -7608,35 +7608,38 @@ public class WindowManagerService extends IWindowManager.Stub
if (imeTarget == null) {
return;
}
- imeTarget = imeTarget.getImeControlTarget().getWindow();
+ final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget();
+ imeTarget = controlTarget.getWindow();
// If InsetsControlTarget doesn't have a window, its using remoteControlTarget which
// is controlled by default display
final DisplayContent dc = imeTarget != null
? imeTarget.getDisplayContent() : getDefaultDisplayContentLocked();
dc.getInsetsStateController().getImeSourceProvider()
- .scheduleShowImePostLayout(imeTarget);
+ .scheduleShowImePostLayout(controlTarget);
}
}
@Override
- public void hideIme(IBinder imeTargetWindowToken) {
+ public void hideIme(IBinder imeTargetWindowToken, int displayId) {
synchronized (mGlobalLock) {
WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
- if (imeTarget == null) {
- // The target window no longer exists.
- return;
+ ProtoLog.d(WM_DEBUG_IME, "hideIme target: %s ", imeTarget);
+ DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (imeTarget != null) {
+ imeTarget = imeTarget.getImeControlTarget().getWindow();
+ if (imeTarget != null) {
+ dc = imeTarget.getDisplayContent();
+ }
+ // If there was a pending IME show(), reset it as IME has been
+ // requested to be hidden.
+ dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
}
- imeTarget = imeTarget.getImeControlTarget().getWindow();
- final DisplayContent dc = imeTarget != null
- ? imeTarget.getDisplayContent() : getDefaultDisplayContentLocked();
- // If there was a pending IME show(), reset it as IME has been
- // requested to be hidden.
- dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
- if (dc.mInputMethodControlTarget == null) {
- return;
+ if (dc != null && dc.mInputMethodControlTarget != null) {
+ ProtoLog.d(WM_DEBUG_IME, "hideIme Control target: %s ",
+ dc.mInputMethodControlTarget);
+ dc.mInputMethodControlTarget.hideInsets(
+ WindowInsets.Type.ime(), true /* fromIme */);
}
- dc.mInputMethodControlTarget.hideInsets(
- WindowInsets.Type.ime(), true /* fromIme */);
}
}
@@ -8246,7 +8249,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
final SurfaceControl mirror = SurfaceControl.mirrorSurface(displaySc);
- outSurfaceControl.copyFrom(mirror);
+ outSurfaceControl.copyFrom(mirror, "WMS.mirrorDisplay");
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 3dddd233f4d8..548345e94fd0 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -290,6 +290,22 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return effects;
}
+ private int applyDisplayAreaChanges(WindowContainer container,
+ WindowContainerTransaction.Change c) {
+ final int[] effects = new int[1];
+
+ container.forAllTasks(task -> {
+ Task tr = (Task) task;
+ if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
+ if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
+ effects[0] |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
+ });
+
+ return effects[0];
+ }
+
private int sanitizeAndApplyHierarchyOp(WindowContainer container,
WindowContainerTransaction.HierarchyOp hop) {
final Task task = container.asTask();
@@ -301,55 +317,45 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
Slog.w(TAG, "Container is no longer attached: " + task);
return 0;
}
- final ActivityStack as = (ActivityStack) task;
+ final Task as = task;
if (hop.isReparent()) {
final boolean isNonOrganizedRootableTask =
(task.isRootTask() && !task.mCreatedByOrganizer)
|| task.getParent().asTask().mCreatedByOrganizer;
if (isNonOrganizedRootableTask) {
- Task newParent = hop.getNewParent() == null ? null
- : WindowContainer.fromBinder(hop.getNewParent()).asTask();
+ WindowContainer newParent = hop.getNewParent() == null
+ ? dc.getDefaultTaskDisplayArea()
+ : WindowContainer.fromBinder(hop.getNewParent());
if (task.getParent() != newParent) {
- if (newParent == null) {
- // Re-parent task to display as a root task.
- as.reparent(dc.getDefaultTaskDisplayArea(), hop.getToTop());
+ if (newParent instanceof TaskDisplayArea) {
+ // For now, reparenting to displayarea is different from other reparents...
+ as.reparent((TaskDisplayArea) newParent, hop.getToTop());
} else if (newParent.inMultiWindowMode() && !task.isResizeable()
&& task.isLeafTask()) {
Slog.w(TAG, "Can't support task that doesn't support multi-window mode in"
+ " multi-window mode... newParent=" + newParent + " task=" + task);
return 0;
} else {
- task.reparent((ActivityStack) newParent,
+ task.reparent((Task) newParent,
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
}
} else {
- final ActivityStack rootTask =
- (ActivityStack) (newParent != null ? newParent : task.getRootTask());
- if (hop.getToTop()) {
- as.getDisplayArea().positionStackAtTop(rootTask,
- false /* includingParents */);
- } else {
- as.getDisplayArea().positionStackAtBottom(rootTask);
- }
+ final Task rootTask = (Task) (
+ (newParent != null && !(newParent instanceof TaskDisplayArea))
+ ? newParent : task.getRootTask());
+ as.getDisplayArea().positionChildAt(
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, rootTask,
+ false /* includingParents */);
}
} else {
- throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
+ throw new RuntimeException("Reparenting leaf Tasks is not supported now. " + task);
}
} else {
- // Ugh, of course ActivityStack has its own special reorder logic...
- if (task.isRootTask()) {
- if (hop.getToTop()) {
- as.getDisplayArea().positionStackAtTop(as, false /* includingParents */);
- } else {
- as.getDisplayArea().positionStackAtBottom(as);
- }
- } else {
- task.getParent().positionChildAt(
- hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
- task, false /* includingParents */);
- }
+ task.getParent().positionChildAt(
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+ task, false /* includingParents */);
}
return TRANSACT_EFFECTS_LIFECYCLE;
}
@@ -366,7 +372,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
int effects = applyChanges(wc, c);
- if (wc instanceof Task) {
+ if (wc instanceof DisplayArea) {
+ effects |= applyDisplayAreaChanges(wc, c);
+ } else if (wc instanceof Task) {
effects |= applyTaskChanges(wc.asTask(), c);
}
@@ -375,10 +383,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
int windowMask, Configuration config) {
- if ((container instanceof ActivityStack)
+ if ((container instanceof Task)
&& ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
&& ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
- final ActivityStack stack = (ActivityStack) container;
+ final Task stack = (Task) container;
if (stack.inPinnedWindowingMode()) {
stack.resize(config.windowConfiguration.getBounds(),
PRESERVE_WINDOWS, true /* deferResume */);
@@ -428,6 +436,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
try {
callback.onTransactionReady(mSyncId, mergedTransaction);
} catch (RemoteException e) {
+ // If there's an exception when trying to send the mergedTransaction to the client, we
+ // should immediately apply it here so the transactions aren't lost.
+ mergedTransaction.apply();
}
mTransactionCallbacksByPendingSyncId.remove(mSyncId);
@@ -456,6 +467,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
.setBufferSize(bounds.width(), bounds.height())
.setFormat(PixelFormat.TRANSLUCENT)
.setParent(wc.getParentSurfaceControl())
+ .setCallsite("WindowOrganizerController.takeScreenshot")
.build();
Surface surface = new Surface();
@@ -463,7 +475,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
surface.attachAndQueueBufferWithColorSpace(buffer.getHardwareBuffer(), null);
surface.release();
- outSurfaceControl.copyFrom(screenshot);
+ outSurfaceControl.copyFrom(screenshot, "WindowOrganizerController.takeScreenshot");
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 29cf1776df9c..da9c7f3ea1b5 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -22,13 +22,6 @@ import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.am.ActivityManagerService.MY_PID;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
@@ -40,6 +33,13 @@ import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.Task.ActivityState.DESTROYED;
+import static com.android.server.wm.Task.ActivityState.DESTROYING;
+import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.Task.ActivityState.PAUSING;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.Task.ActivityState.STARTED;
+import static com.android.server.wm.Task.ActivityState.STOPPING;
import android.Manifest;
import android.annotation.NonNull;
@@ -160,7 +160,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private volatile boolean mPerceptible;
// Set to true when process was launched with a wrapper attached
private volatile boolean mUsingWrapper;
- // Set to true if this process is currently temporarily whitelisted to start activities even if
+ // Set to true if this process is currently temporarily allowed to start activities even if
// it's not in the foreground
private volatile boolean mAllowBackgroundActivityStarts;
// Set of UIDs of clients currently bound to this process
@@ -454,7 +454,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
boolean areBackgroundActivityStartsAllowed() {
- // allow if the whitelisting flag was explicitly set
+ // allow if the flag was explicitly set
if (mAllowBackgroundActivityStarts) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid
@@ -690,7 +690,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
if (canUpdate) {
// Make sure the previous top activity in the process no longer be resumed.
if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
- final ActivityStack stack = mPreQTopResumedActivity.getRootTask();
+ final Task stack = mPreQTopResumedActivity.getRootTask();
if (stack != null) {
stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
activity);
@@ -924,7 +924,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
// "best" state, the order would be visible, pausing, stopping...
- ActivityStack.ActivityState best = DESTROYED;
+ Task.ActivityState best = DESTROYED;
boolean finishing = true;
boolean visible = false;
synchronized (mAtm.mGlobalLockWithoutBoost) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6a871f9b0240..f3c5092a42be 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -27,6 +27,7 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.graphics.GraphicsProtos.dumpPointProto;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.SurfaceControl.Transaction;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
@@ -62,7 +63,6 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
@@ -85,8 +85,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
@@ -161,7 +161,6 @@ import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_
import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
-import static com.android.server.wm.WindowStateProto.IDENTIFIER;
import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
@@ -344,7 +343,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private boolean mDragResizing;
private boolean mDragResizingChangeReported = true;
private int mResizeMode;
- private boolean mResizeForBlastSyncReported;
+ private boolean mRedrawForSyncReported;
/**
* Special mode that is intended only for the rounded corner overlay: during rotation
@@ -761,6 +760,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
oldRotation = mPendingSeamlessRotate.getOldRotation();
}
+ // Skip performing seamless rotation when the controlled insets is IME with visible state.
+ if (mControllableInsetProvider != null
+ && mControllableInsetProvider.getSource().getType() == ITYPE_IME) {
+ return;
+ }
+
if (mForceSeamlesslyRotate || requested) {
if (mControllableInsetProvider != null) {
mControllableInsetProvider.startSeamlessRotation();
@@ -947,17 +952,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mInputWindowHandle.trustedOverlay =
(mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
&& mOwnerCanAddInternalSystemWindow;
- mInputWindowHandle.trustedOverlay |=
- mAttrs.type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
- || mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG
- || mAttrs.type == TYPE_MAGNIFICATION_OVERLAY || mAttrs.type == TYPE_STATUS_BAR
- || mAttrs.type == TYPE_NOTIFICATION_SHADE
- || mAttrs.type == TYPE_NAVIGATION_BAR
- || mAttrs.type == TYPE_NAVIGATION_BAR_PANEL
- || mAttrs.type == TYPE_SECURE_SYSTEM_OVERLAY
- || mAttrs.type == TYPE_DOCK_DIVIDER
- || mAttrs.type == TYPE_ACCESSIBILITY_OVERLAY
- || mAttrs.type == TYPE_INPUT_CONSUMER;
+ mInputWindowHandle.trustedOverlay |= InputMonitor.isTrustedOverlay(mAttrs.type);
// Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged.
@@ -1424,7 +1419,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|| configChanged
|| dragResizingChanged
|| mReportOrientationChanged
- || requestResizeForBlastSync()) {
+ || shouldSendRedrawForSync()) {
ProtoLog.v(WM_DEBUG_RESIZE,
"Resize reasons for w=%s: %s surfaceResized=%b configChanged=%b "
+ "dragResizingChanged=%b reportOrientationChanged=%b",
@@ -1559,10 +1554,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mActivityRecord != null ? mActivityRecord.getTask() : null;
}
- @Nullable ActivityStack getRootTask() {
+ @Nullable Task getRootTask() {
final Task task = getTask();
if (task != null) {
- return (ActivityStack) task.getRootTask();
+ return task.getRootTask();
}
// Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
// associate them with some stack to enable dimming.
@@ -1604,7 +1599,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
bounds.setEmpty();
mTmpRect.setEmpty();
if (intersectWithStackBounds) {
- final ActivityStack stack = task.getStack();
+ final Task stack = task.getRootTask();
if (stack != null) {
stack.getDimBounds(mTmpRect);
} else {
@@ -1615,7 +1610,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// the secondary split, it means this is "minimized" and thus must prevent
// overlapping with home.
// TODO(b/158242495): get rid of this when drag/drop can use surface bounds.
- final ActivityStack rootSecondary =
+ final Task rootSecondary =
task.getDisplayArea().getRootSplitScreenSecondaryTask();
if (rootSecondary.isActivityTypeHome() || rootSecondary.isActivityTypeRecents()) {
final WindowContainer topTask = rootSecondary.getTopChild();
@@ -2100,7 +2095,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean isObscuringDisplay() {
Task task = getTask();
- if (task != null && task.getStack() != null && !task.getStack().fillsParent()) {
+ if (task != null && task.getRootTask() != null && !task.getRootTask().fillsParent()) {
return false;
}
return isOpaqueDrawn() && fillsDisplay();
@@ -2184,6 +2179,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (isInputMethodTarget()) {
dc.computeImeTarget(true /* updateImeTarget */);
}
+ if (dc.mInputMethodInputTarget == this) {
+ dc.setInputMethodInputTarget(null);
+ }
final int type = mAttrs.type;
if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
@@ -2214,7 +2212,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
- finishDrawing(null);
+ immediatelyNotifyBlastSync();
}
private void removeIfPossible(boolean keepVisibleDeadWindow) {
@@ -2394,41 +2392,43 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
+ if (mAttrs.type == TYPE_SCREENSHOT) {
+ // Disallow screenshot windows from being IME targets
+ return false;
+ }
+
final boolean windowsAreFocusable = mActivityRecord == null || mActivityRecord.windowsAreFocusable();
if (!windowsAreFocusable) {
// This window can't be an IME target if the app's windows should not be focusable.
return false;
}
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (stack != null && !stack.isFocusable()) {
// Ignore when the stack shouldn't receive input event.
// (i.e. the minimized stack in split screen mode.)
return false;
}
- if (PixelFormat.formatHasAlpha(mAttrs.format)) {
- // Support legacy use cases where transparent windows can still be ime target with
- // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
- // Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to
- // manually synchronize app content to IME animation b/144619551.
- // TODO(b/145812508): remove this once new focus management is complete b/141738570
+ if (mAttrs.type == TYPE_APPLICATION_STARTING) {
+ // Ignore mayUseInputMethod for starting window for now.
+ // TODO(b/159911356): Remove this special casing (originally added in commit e75d872).
+ } else {
+ // TODO(b/145812508): Clean this up in S, may depend on b/141738570
+ // The current logic lets windows become the "ime target" even though they are
+ // not-focusable and can thus never actually start input.
+ // Ideally, this would reject windows where mayUseInputMethod() == false, but this
+ // also impacts Z-ordering of and delivery of IME insets to child windows, which means
+ // that simply disallowing non-focusable windows would break apps.
+ // See b/159438771, b/144619551.
+
final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
- final int type = mAttrs.type;
// Can only be an IME target if both FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM are
// set or both are cleared...and not a starting window.
- if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)
- && type != TYPE_APPLICATION_STARTING) {
+ if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)) {
return false;
}
- } else if (!WindowManager.LayoutParams.mayUseInputMethod(mAttrs.flags)
- || mAttrs.type == TYPE_APPLICATION_STARTING) {
- // Can be an IME target only if:
- // 1. FLAG_NOT_FOCUSABLE is not set
- // 2. FLAG_ALT_FOCUSABLE_IM is not set
- // 3. not a starting window.
- return false;
}
if (DEBUG_INPUT_METHOD) {
@@ -2718,7 +2718,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
Settings.Global.THEATER_MODE_ON, 0) == 0;
boolean canTurnScreenOn = mActivityRecord == null || mActivityRecord.currentLaunchCanTurnScreenOn();
- if (allowTheaterMode && canTurnScreenOn && !mPowerManagerWrapper.isInteractive()) {
+ if (allowTheaterMode && canTurnScreenOn
+ && (mWmService.mAtmInternal.isDreaming()
+ || !mPowerManagerWrapper.isInteractive())) {
if (DEBUG_VISIBILITY || DEBUG_POWER) {
Slog.v(TAG, "Relayout window turning screen on: " + this);
}
@@ -2902,7 +2904,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
- return mActivityRecord.getTask().getStack().shouldIgnoreInput()
+ return mActivityRecord.getTask().getRootTask().shouldIgnoreInput()
|| !mActivityRecord.mVisibleRequested
|| isRecentsAnimationConsumingAppInput();
}
@@ -3472,7 +3474,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
- final ActivityStack stack = task.getStack();
+ final Task stack = task.getRootTask();
if (stack == null || inFreeformWindowingMode()) {
handle.setTouchableRegionCrop(null);
return;
@@ -3487,7 +3489,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
- final ActivityStack stack = task.getStack();
+ final Task stack = task.getRootTask();
if (stack == null || stack.mCreatedByOrganizer) {
return;
}
@@ -3585,7 +3587,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mReportOrientationChanged = false;
mDragResizingChangeReported = true;
mWinAnimator.mSurfaceResized = false;
- mResizeForBlastSyncReported = true;
mWindowFrames.resetInsetsChanged();
final Rect frame = mWindowFrames.mCompatFrame;
@@ -3593,11 +3594,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
final Rect stableInsets = mWindowFrames.mLastStableInsets;
final MergedConfiguration mergedConfiguration = mLastReportedConfiguration;
- final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING || useBLASTSync();
- final boolean forceRelayout = reportOrientation || isDragResizeChanged();
+ final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING || useBLASTSync() || !mRedrawForSyncReported;
+ final boolean forceRelayout = reportOrientation || isDragResizeChanged() || !mRedrawForSyncReported;
final int displayId = getDisplayId();
final DisplayCutout displayCutout = getWmDisplayCutout().getDisplayCutout();
+ mRedrawForSyncReported = true;
+
try {
mClient.resized(frame, contentInsets, visibleInsets, stableInsets, reportDraw,
mergedConfiguration, getBackdropFrame(frame), forceRelayout,
@@ -3730,7 +3733,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
private int getRootTaskId() {
- final ActivityStack stack = getRootTask();
+ final Task stack = getRootTask();
if (stack == null) {
return INVALID_TASK_ID;
}
@@ -3812,6 +3815,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|| mActivityRecord.letterboxNotIntersectsOrFullyContains(rect);
}
+ public boolean isLetterboxedOverlappingWith(Rect rect) {
+ return mActivityRecord != null && mActivityRecord.isLetterboxOverlappingWith(rect);
+ }
+
boolean isDragResizeChanged() {
return mDragResizing != computeDragResizing();
}
@@ -3912,7 +3919,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final long token = proto.start(fieldId);
super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
- writeIdentifierToProto(proto, IDENTIFIER);
proto.write(DISPLAY_ID, getDisplayId());
proto.write(STACK_ID, getRootTaskId());
mAttrs.dumpDebug(proto, ATTRIBUTES);
@@ -5410,7 +5416,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
- private void transformFrameToSurfacePosition(int left, int top, Point outPoint) {
+ void transformFrameToSurfacePosition(int left, int top, Point outPoint) {
outPoint.set(left, top);
// If changed, also adjust getTransformationMatrix
@@ -5430,7 +5436,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outPoint.offset(-parentBounds.left, -parentBounds.top);
}
- ActivityStack stack = getRootTask();
+ Task stack = getRootTask();
// If we have stack outsets, that means the top-left
// will be outset, and we need to inset ourselves
@@ -5830,7 +5836,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// client will not render when visibility is GONE. Therefore, call finishDrawing here to
// prevent system server from blocking on a window that will not draw.
if (viewVisibility == View.GONE && mUsingBLASTSyncTransaction) {
- finishDrawing(null);
+ immediatelyNotifyBlastSync();
}
}
@@ -5845,7 +5851,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (!willSync) {
return false;
}
- mResizeForBlastSyncReported = false;
+ requestRedrawForSync();
mLocalSyncId = mBLASTSyncEngine.startSyncSet(this);
addChildrenToSyncSet(mLocalSyncId);
@@ -5868,7 +5874,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
}
- mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
if (postDrawTransaction != null) {
mBLASTSyncTransaction.merge(postDrawTransaction);
}
@@ -5877,8 +5882,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mWinAnimator.finishDrawingLocked(null);
}
- @VisibleForTesting
- void notifyBlastSyncTransaction() {
+ private void notifyBlastSyncTransaction() {
+ mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
+
if (!mNotifyBlastOnSurfacePlacement || mWaitingListener == null) {
mNotifyBlastOnSurfacePlacement = false;
return;
@@ -5901,7 +5907,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mNotifyBlastOnSurfacePlacement = false;
}
- private boolean requestResizeForBlastSync() {
- return useBLASTSync() && !mResizeForBlastSyncReported;
+ void immediatelyNotifyBlastSync() {
+ finishDrawing(null);
+ notifyBlastSyncTransaction();
+ }
+
+ /**
+ * When using the two WindowOrganizer sync-primitives (BoundsChangeTransaction, BLASTSync)
+ * it can be a little difficult to predict whether your change will actually trigger redrawing
+ * on the client side. To ease the burden on shell developers, we force send MSG_RESIZED
+ * for Windows involved in these Syncs
+ */
+ private boolean shouldSendRedrawForSync() {
+ final Task task = getTask();
+ if (task != null && task.getMainWindowSizeChangeTransaction() != null)
+ return !mRedrawForSyncReported;
+ return useBLASTSync() && !mRedrawForSyncReported;
+ }
+
+ void requestRedrawForSync() {
+ mRedrawForSyncReported = false;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 508d2d477067..77fee851889e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -924,15 +924,15 @@ class WindowStateAnimator {
int posX = 0;
int posY = 0;
- task.getStack().getDimBounds(mTmpStackBounds);
+ task.getRootTask().getDimBounds(mTmpStackBounds);
boolean allowStretching = false;
- task.getStack().getFinalAnimationSourceHintBounds(mTmpSourceBounds);
+ task.getRootTask().getFinalAnimationSourceHintBounds(mTmpSourceBounds);
// If we don't have source bounds, we can attempt to use the content insets
// if we have content insets.
if (mTmpSourceBounds.isEmpty() && (mWin.mLastRelayoutContentInsets.width() > 0
|| mWin.mLastRelayoutContentInsets.height() > 0)) {
- mTmpSourceBounds.set(task.getStack().mPreAnimationBounds);
+ mTmpSourceBounds.set(task.getRootTask().mPreAnimationBounds);
mTmpSourceBounds.inset(mWin.mLastRelayoutContentInsets);
allowStretching = true;
}
@@ -946,7 +946,7 @@ class WindowStateAnimator {
if (!mTmpSourceBounds.isEmpty()) {
// Get the final target stack bounds, if we are not animating, this is just the
// current stack bounds
- task.getStack().getFinalAnimationBounds(mTmpAnimatingBounds);
+ task.getRootTask().getFinalAnimationBounds(mTmpAnimatingBounds);
// Calculate the current progress and interpolate the difference between the target
// and source bounds
@@ -1042,7 +1042,7 @@ class WindowStateAnimator {
mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(),
mWin.getFrameNumber());
} else {
- final ActivityStack stack = mWin.getRootTask();
+ final Task stack = mWin.getRootTask();
mTmpPos.x = 0;
mTmpPos.y = 0;
if (stack != null) {
@@ -1576,7 +1576,7 @@ class WindowStateAnimator {
*/
boolean isForceScaled() {
final Task task = mWin.getTask();
- if (task != null && task.getStack().isForceScaled()) {
+ if (task != null && task.getRootTask().isForceScaled()) {
return true;
}
return mForceScaleUntilResize;
@@ -1592,12 +1592,6 @@ class WindowStateAnimator {
if (mSurfaceController != null) {
mSurfaceController.detachChildren();
}
- // If the children are detached, it means the app is exiting. We don't want to tear the
- // content down too early, otherwise we could end up with a flicker. By preserving the
- // current surface, we ensure the content remains on screen until the window is completely
- // removed. It also ensures that the old surface is cleaned up when started again since it
- // forces mSurfaceController to be set to null.
- preserveSurfaceLocked();
}
void setOffsetPositionForStackResize(boolean offsetPositionForStackResize) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index b2bfcdc8a900..b89cdd32e132 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -116,7 +116,8 @@ class WindowSurfaceController {
.setFormat(format)
.setFlags(flags)
.setMetadata(METADATA_WINDOW_TYPE, windowType)
- .setMetadata(METADATA_OWNER_UID, ownerUid);
+ .setMetadata(METADATA_OWNER_UID, ownerUid)
+ .setCallsite("WindowSurfaceController");
final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
@@ -132,6 +133,7 @@ class WindowSurfaceController {
.setName(name + "(BLAST)")
.setHidden(false)
.setBLASTLayer()
+ .setCallsite("WindowSurfaceController")
.build();
}
@@ -493,12 +495,12 @@ class WindowSurfaceController {
}
void getSurfaceControl(SurfaceControl outSurfaceControl) {
- outSurfaceControl.copyFrom(mSurfaceControl);
+ outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
}
void getBLASTSurfaceControl(SurfaceControl outSurfaceControl) {
if (mBLASTSurfaceControl != null) {
- outSurfaceControl.copyFrom(mBLASTSurfaceControl);
+ outSurfaceControl.copyFrom(mBLASTSurfaceControl, "WindowSurfaceController.getBLASTSurfaceControl");
}
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 86aacf308068..2c1bb3ec51eb 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -40,10 +40,12 @@ import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW;
import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.app.IWindowToken;
import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
@@ -78,6 +80,13 @@ class WindowToken extends WindowContainer<WindowState> {
// The type of window this token is for, as per WindowManager.LayoutParams.
final int windowType;
+ /**
+ * Options that will be used to determine which {@link RootDisplayArea} this window should be
+ * attached to.
+ */
+ @Nullable
+ final Bundle mOptions;
+
/** {@code true} if this holds the rounded corner overlay */
final boolean mRoundedCornerOverlay;
@@ -233,9 +242,17 @@ class WindowToken extends WindowContainer<WindowState> {
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
boolean roundedCornerOverlay, boolean fromClientToken) {
+ this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, ownerUid,
+ roundedCornerOverlay, fromClientToken, null /* options */);
+ }
+
+ WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
+ DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
+ boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
super(service);
token = _token;
windowType = type;
+ mOptions = options;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mOwnerUid = ownerUid;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 5f1895abd008..0e1b2f25c7af 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -14,7 +14,6 @@ cc_library_static {
],
srcs: [
- ":lib_alarmManagerService_native",
"BroadcastRadio/JavaRef.cpp",
"BroadcastRadio/NativeCallbackThread.cpp",
"BroadcastRadio/BroadcastRadioService.cpp",
@@ -72,17 +71,6 @@ cc_library_static {
header_libs: [
"bionic_libc_platform_headers",
],
-
- product_variables: {
- arc: {
- exclude_srcs: [
- "com_android_server_alarm_AlarmManagerService.cpp",
- ],
- srcs: [
- ":arctimersrcs",
- ],
- }
- }
}
cc_defaults {
@@ -198,10 +186,3 @@ filegroup {
"com_android_server_net_NetworkStatsFactory.cpp",
],
}
-
-filegroup {
- name: "lib_alarmManagerService_native",
- srcs: [
- "com_android_server_alarm_AlarmManagerService.cpp",
- ],
-}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 75ec22486021..10523a2adbb2 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -194,16 +194,16 @@ protected:
public:
NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);
- inline sp<InputManager> getInputManager() const { return mInputManager; }
+ inline sp<InputManagerInterface> getInputManager() const { return mInputManager; }
void dump(std::string& dump);
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
- status_t registerInputMonitor(JNIEnv* env, const sp<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor);
- status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
+ status_t registerInputChannel(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel);
+ status_t registerInputMonitor(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor);
+ status_t unregisterInputChannel(JNIEnv* env, const InputChannel& inputChannel);
status_t pilferPointers(const sp<IBinder>& token);
void displayRemoved(JNIEnv* env, int32_t displayId);
@@ -225,7 +225,7 @@ public:
/* --- InputReaderPolicyInterface implementation --- */
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
- virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
+ virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId);
virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices);
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier);
@@ -236,29 +236,26 @@ public:
/* --- InputDispatcherPolicyInterface implementation --- */
- virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
- uint32_t policyFlags) override;
- virtual void notifyConfigurationChanged(nsecs_t when);
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) override;
- virtual void notifyInputChannelBroken(const sp<IBinder>& token);
- virtual void notifyFocusChanged(const sp<IBinder>& oldToken,
- const sp<IBinder>& newToken) override;
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
- uint32_t& policyFlags) override;
- virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
- uint32_t& policyFlags) override;
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
- const KeyEvent* keyEvent,
- uint32_t policyFlags) override;
- virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
- uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
- int32_t injectorUid) override;
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
+ void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override;
+ void notifyConfigurationChanged(nsecs_t when) override;
+ std::chrono::nanoseconds notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token,
+ const std::string& reason) override;
+ void notifyInputChannelBroken(const sp<IBinder>& token) override;
+ void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
+ bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
+ void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
+ void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) override;
+ void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+ uint32_t& policyFlags) override;
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent* keyEvent,
+ uint32_t policyFlags) override;
+ bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
+ uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
+ void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
+ bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override;
+ void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
/* --- PointerControllerPolicyInterface implementation --- */
@@ -270,7 +267,7 @@ public:
virtual int32_t getCustomPointerIconId();
private:
- sp<InputManager> mInputManager;
+ sp<InputManagerInterface> mInputManager;
jobject mServiceObj;
sp<Looper> mLooper;
@@ -299,7 +296,7 @@ private:
sp<SpriteController> spriteController;
// Pointer controller singleton, created and destroyed as needed.
- wp<PointerController> pointerController;
+ std::weak_ptr<PointerController> pointerController;
// Input devices to be disabled
std::set<int32_t> disabledInputDevices;
@@ -342,9 +339,9 @@ NativeInputManager::NativeInputManager(jobject contextObj,
}
mInteractive = true;
- mInputManager = new InputManager(this, this);
- defaultServiceManager()->addService(String16("inputflinger"),
- mInputManager, false);
+ InputManager* im = new InputManager(this, this);
+ mInputManager = im;
+ defaultServiceManager()->addService(String16("inputflinger"), im);
}
NativeInputManager::~NativeInputManager() {
@@ -424,21 +421,22 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
-status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel) {
+status_t NativeInputManager::registerInputChannel(
+ JNIEnv* /* env */, const std::shared_ptr<InputChannel>& inputChannel) {
ATRACE_CALL();
return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}
status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel, int32_t displayId, bool isGestureMonitor) {
+ const std::shared_ptr<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor) {
ATRACE_CALL();
return mInputManager->getDispatcher()->registerInputMonitor(
inputChannel, displayId, isGestureMonitor);
}
status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel) {
+ const InputChannel& inputChannel) {
ATRACE_CALL();
return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);
}
@@ -544,15 +542,16 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon
} // release lock
}
-sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) {
+std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerController(
+ int32_t /* deviceId */) {
ATRACE_CALL();
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller == nullptr) {
ensureSpriteControllerLocked();
- controller = new PointerController(this, mLooper, mLocked.spriteController);
+ controller = PointerController::create(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
updateInactivityTimeoutLocked();
}
@@ -694,8 +693,9 @@ static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
return handle->getInputApplicationHandleObjLocalRef(env);
}
-nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) {
+std::chrono::nanoseconds NativeInputManager::notifyAnr(
+ const sp<InputApplicationHandle>& inputApplicationHandle, const sp<IBinder>& token,
+ const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyANR");
#endif
@@ -718,7 +718,7 @@ nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApp
} else {
assert(newTimeout >= 0);
}
- return newTimeout;
+ return std::chrono::nanoseconds(newTimeout);
}
void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) {
@@ -803,15 +803,14 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
}
void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller == nullptr) {
return;
}
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
- controller->setInactivityTimeout(lightsOut
- ? PointerController::INACTIVITY_TIMEOUT_SHORT
- : PointerController::INACTIVITY_TIMEOUT_NORMAL);
+ controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT
+ : PointerController::InactivityTimeout::NORMAL);
}
void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -891,7 +890,7 @@ void NativeInputManager::reloadCalibration() {
void NativeInputManager::setPointerIconType(int32_t iconId) {
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller != nullptr) {
controller->updatePointerIcon(iconId);
}
@@ -899,7 +898,7 @@ void NativeInputManager::setPointerIconType(int32_t iconId) {
void NativeInputManager::reloadPointerIcons() {
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller != nullptr) {
controller->reloadPointerResources();
}
@@ -907,7 +906,7 @@ void NativeInputManager::reloadPointerIcons() {
void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller != nullptr) {
controller->setCustomPointerIcon(icon);
}
@@ -1253,7 +1252,7 @@ int32_t NativeInputManager::getCustomPointerIconId() {
}
void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
- mInputManager->setMotionClassifierEnabled(enabled);
+ mInputManager->getClassifier()->setMotionClassifierEnabled(enabled);
}
// ----------------------------------------------------------------------------
@@ -1340,21 +1339,22 @@ static void throwInputChannelNotInitialized(JNIEnv* env) {
"inputChannel is not initialized");
}
-static void handleInputChannelDisposed(JNIEnv* env,
- jobject /* inputChannelObj */, const sp<InputChannel>& inputChannel, void* data) {
+static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
+ const std::shared_ptr<InputChannel>& inputChannel,
+ void* data) {
NativeInputManager* im = static_cast<NativeInputManager*>(data);
ALOGW("Input channel object '%s' was disposed without first being unregistered with "
"the input manager!", inputChannel->getName().c_str());
- im->unregisterInputChannel(env, inputChannel);
+ im->unregisterInputChannel(env, *inputChannel);
}
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
@@ -1377,8 +1377,8 @@ static void nativeRegisterInputMonitor(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj, jint displayId, jboolean isGestureMonitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
@@ -1403,8 +1403,8 @@ static void nativeUnregisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
@@ -1412,7 +1412,7 @@ static void nativeUnregisterInputChannel(JNIEnv* env, jclass /* clazz */,
android_view_InputChannel_setDisposeCallback(env, inputChannelObj, nullptr, nullptr);
- status_t status = im->unregisterInputChannel(env, inputChannel);
+ status_t status = im->unregisterInputChannel(env, *inputChannel);
if (status && status != BAD_VALUE) { // ignore already unregistered channel
std::string message;
message += StringPrintf("Failed to unregister input channel. status=%d", status);
@@ -1617,9 +1617,8 @@ static void nativeReloadCalibration(JNIEnv* env, jclass clazz, jlong ptr) {
im->reloadCalibration();
}
-static void nativeVibrate(JNIEnv* env,
- jclass /* clazz */, jlong ptr, jint deviceId, jlongArray patternObj,
- jint repeat, jint token) {
+static void nativeVibrate(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jlongArray patternObj, jintArray amplitudesObj, jint repeat, jint token) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
size_t patternSize = env->GetArrayLength(patternObj);
@@ -1632,14 +1631,19 @@ static void nativeVibrate(JNIEnv* env,
jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
patternObj, nullptr));
- nsecs_t pattern[patternSize];
+ jint* amplitudes = static_cast<jint*>(env->GetPrimitiveArrayCritical(amplitudesObj, nullptr));
+
+ std::vector<VibrationElement> pattern(patternSize);
for (size_t i = 0; i < patternSize; i++) {
- pattern[i] = max(jlong(0), min(patternMillis[i],
- (jlong)(MAX_VIBRATE_PATTERN_DELAY_NSECS / 1000000LL))) * 1000000LL;
+ jlong duration =
+ max(min(patternMillis[i], (jlong)MAX_VIBRATE_PATTERN_DELAY_MSECS), (jlong)0);
+ pattern[i].duration = std::chrono::milliseconds(duration);
+ pattern[i].channels = {amplitudes[i]};
}
env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);
+ env->ReleasePrimitiveArrayCritical(amplitudesObj, amplitudes, JNI_ABORT);
- im->getInputManager()->getReader()->vibrate(deviceId, pattern, patternSize, repeat, token);
+ im->getInputManager()->getReader()->vibrate(deviceId, pattern, repeat, token);
}
static void nativeCancelVibrate(JNIEnv* /* env */,
@@ -1791,7 +1795,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches},
{"nativeSetInteractive", "(JZ)V", (void*)nativeSetInteractive},
{"nativeReloadCalibration", "(J)V", (void*)nativeReloadCalibration},
- {"nativeVibrate", "(JI[JII)V", (void*)nativeVibrate},
+ {"nativeVibrate", "(JI[J[III)V", (void*)nativeVibrate},
{"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate},
{"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts},
{"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases},
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index d11c4bcb9872..91f70729d5b6 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -47,13 +47,13 @@
#include "com_android_server_power_PowerManagerService.h"
+using android::String8;
using android::hardware::power::Boost;
using android::hardware::power::Mode;
-using android::String8;
+using android::system::suspend::ISuspendControlService;
using android::system::suspend::V1_0::ISystemSuspend;
using android::system::suspend::V1_0::IWakeLock;
using android::system::suspend::V1_0::WakeLockType;
-using android::system::suspend::ISuspendControlService;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
using IPowerAidl = android::hardware::power::IPower;
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 39e7249eec33..6f24e3b580b7 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -24,7 +24,6 @@
#include "BroadcastRadio/Tuner.h"
namespace android {
-int register_android_server_alarm_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryStatsService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputManager(JNIEnv* env);
@@ -86,7 +85,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_SerialService(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
- register_android_server_alarm_AlarmManagerService(env);
register_android_server_UsbDeviceManager(env);
register_android_server_UsbMidiDevice(env);
register_android_server_UsbAlsaJackDetector(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c6b93d6ca4f4..7ec819f13e96 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3570,6 +3570,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
+ /**
+ * Persist modified values to disk by calling {@link #saveSettingsLocked} for each
+ * affected user ID.
+ */
+ @GuardedBy("getLockObject()")
+ private void saveSettingsForUsersLocked(Set<Integer> affectedUserIds) {
+ for (int userId : affectedUserIds) {
+ saveSettingsLocked(userId);
+ }
+ }
+
private void saveSettingsLocked(int userHandle) {
DevicePolicyData policy = getUserData(userHandle);
JournaledFile journal = makeJournaledFile(userHandle);
@@ -4785,13 +4796,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Updates a flag that tells us whether the user's password currently satisfies the
- * requirements set by all of the user's active admins. The flag is updated both in memory
- * and persisted to disk by calling {@link #saveSettingsLocked}, for the value of the flag
- * be the correct one upon boot.
- * This should be called whenever the password or the admin policies have changed.
+ * requirements set by all of the user's active admins.
+ * This should be called whenever the password or the admin policies have changed. The caller
+ * is responsible for calling {@link #saveSettingsLocked} to persist the change.
+ *
+ * @return the set of user IDs that have been affected
*/
@GuardedBy("getLockObject()")
- private void updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+ private Set<Integer> updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
final int credentialOwner = getCredentialOwner(userHandle, parent);
DevicePolicyData policy = getUserData(credentialOwner);
PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
@@ -4801,9 +4814,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
metrics, userHandle, parent);
if (newCheckpoint != policy.mPasswordValidAtLastCheckpoint) {
policy.mPasswordValidAtLastCheckpoint = newCheckpoint;
- saveSettingsLocked(credentialOwner);
+ affectedUserIds.add(credentialOwner);
}
}
+ return affectedUserIds;
}
/**
@@ -6175,7 +6189,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void removeCaApprovalsIfNeeded(int userId) {
+ private Set<Integer> removeCaApprovalsIfNeeded(int userId) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
for (UserInfo userInfo : mUserManager.getProfiles(userId)) {
boolean isSecure = mLockPatternUtils.isSecure(userInfo.id);
if (userInfo.isManagedProfile()){
@@ -6184,11 +6199,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!isSecure) {
synchronized (getLockObject()) {
getUserData(userInfo.id).mAcceptedCaCertificates.clear();
- saveSettingsLocked(userInfo.id);
+ affectedUserIds.add(userInfo.id);
}
mCertificateMonitor.onCertificateApprovalsChanged(userId);
}
}
+ return affectedUserIds;
}
@Override
@@ -7458,42 +7474,45 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
DevicePolicyData policy = getUserData(userId);
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
synchronized (getLockObject()) {
policy.mFailedPasswordAttempts = 0;
- updatePasswordValidityCheckpointLocked(userId, /* parent */ false);
- saveSettingsLocked(userId);
- updatePasswordExpirationsLocked(userId);
+ affectedUserIds.add(userId);
+ affectedUserIds.addAll(updatePasswordValidityCheckpointLocked(
+ userId, /* parent */ false));
+ affectedUserIds.addAll(updatePasswordExpirationsLocked(userId));
setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
// Send a broadcast to each profile using this password as its primary unlock.
sendAdminCommandForLockscreenPoliciesLocked(
DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
+
+ affectedUserIds.addAll(removeCaApprovalsIfNeeded(userId));
+ saveSettingsForUsersLocked(affectedUserIds);
}
- removeCaApprovalsIfNeeded(userId);
}
/**
* Called any time the device password is updated. Resets all password expiration clocks.
+ *
+ * @return the set of user IDs that have been affected
*/
- private void updatePasswordExpirationsLocked(int userHandle) {
- ArraySet<Integer> affectedUserIds = new ArraySet<Integer>();
+ private Set<Integer> updatePasswordExpirationsLocked(int userHandle) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(
userHandle, /* parent */ false);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
+ for (int i = 0; i < admins.size(); i++) {
ActiveAdmin admin = admins.get(i);
if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
affectedUserIds.add(admin.getUserHandle().getIdentifier());
long timeout = admin.passwordExpirationTimeout;
- long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
- admin.passwordExpirationDate = expiration;
+ admin.passwordExpirationDate =
+ timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
}
}
- for (int affectedUserId : affectedUserIds) {
- saveSettingsLocked(affectedUserId);
- }
+ return affectedUserIds;
}
@Override
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 3450c3ae9fb3..a5f0d045948c 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1422,6 +1422,11 @@ static long elapsedMcs(Duration start, Duration end) {
}
// Extract lib files from zip, create new files in incfs and write data to them
+// Lib files should be placed next to the APK file in the following matter:
+// Example:
+// /path/to/base.apk
+// /path/to/lib/arm/first.so
+// /path/to/lib/arm/second.so
bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
std::string_view libDirRelativePath,
std::string_view abi, bool extractNativeLibs) {
@@ -1433,9 +1438,13 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
return false;
}
+ const auto targetLibPathRelativeToStorage =
+ path::join(path::dirname(normalizePathToStorage(*ifs, storage, apkFullPath)),
+ libDirRelativePath);
+
// First prepare target directories if they don't exist yet
- if (auto res = makeDirs(*ifs, storage, libDirRelativePath, 0755)) {
- LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath
+ if (auto res = makeDirs(*ifs, storage, targetLibPathRelativeToStorage, 0755)) {
+ LOG(ERROR) << "Failed to prepare target lib directory " << targetLibPathRelativeToStorage
<< " errno: " << res;
return false;
}
@@ -1486,7 +1495,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
auto startFileTs = Clock::now();
const auto libName = path::basename(fileName);
- auto targetLibPath = path::join(libDirRelativePath, libName);
+ auto targetLibPath = path::join(targetLibPathRelativeToStorage, libName);
const auto targetLibPathAbsolute = normalizePathToStorage(*ifs, storage, targetLibPath);
// If the extract file already exists, skip
if (access(targetLibPathAbsolute.c_str(), F_OK) == 0) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d9a46022ad05..a2e310a27fe0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -80,6 +80,7 @@ import android.util.Pair;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureManager;
+import com.android.i18n.timezone.ZoneInfoDb;
import com.android.internal.R;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BinderInternal;
@@ -184,8 +185,6 @@ import dalvik.system.VMRuntime;
import com.google.android.startop.iorap.IorapForwardingService;
-import libcore.timezone.ZoneInfoDb;
-
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
@@ -619,12 +618,6 @@ public final class SystemServer {
}
}
- // Diagnostic to ensure that the system is in a base healthy state. Done here as a common
- // non-zygote process.
- if (!VMRuntime.hasBootImageSpaces()) {
- Slog.wtf(TAG, "Runtime is not running with a boot image!");
- }
-
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 25ab5d36169e..1ae2aec90ba3 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -43,6 +43,7 @@ android_robolectric_test {
// Include the testing libraries
libs: [
"platform-test-annotations",
+ "services.backup",
"testng",
],
static_libs: [
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index a1bfcdf4bdfa..4a73efe25fdb 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -38,6 +38,8 @@ import static org.testng.Assert.expectThrows;
import android.annotation.UserIdInt;
import android.app.Application;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
@@ -871,7 +873,8 @@ public class BackupManagerServiceRoboTest {
SecurityException.class,
() ->
backupManagerService.requestBackup(
- mUserTwoId, packages, observer, monitor, 0));
+ mUserTwoId, packages, observer, monitor, 0,
+ OperationType.BACKUP));
}
/**
@@ -889,9 +892,11 @@ public class BackupManagerServiceRoboTest {
IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
- backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0,
+ OperationType.BACKUP);
- verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0,
+ OperationType.BACKUP);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@@ -904,9 +909,11 @@ public class BackupManagerServiceRoboTest {
IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
+ backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0,
+ OperationType.BACKUP);
- verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0,
+ OperationType.BACKUP);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@@ -919,9 +926,11 @@ public class BackupManagerServiceRoboTest {
IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0,
+ OperationType.BACKUP);
- verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
+ verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0,
+ OperationType.BACKUP);
}
/**
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index dfe75ed50cd4..06d51a4be920 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -61,7 +61,7 @@ import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.testing.shadows.ShadowAppBackupUtils;
+import com.android.server.testing.shadows.ShadowBackupEligibilityRules;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowBinder;
import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
@@ -99,7 +99,7 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
- ShadowAppBackupUtils.class,
+ ShadowBackupEligibilityRules.class,
ShadowApplicationPackageManager.class,
ShadowSystemServiceRegistry.class
})
@@ -159,7 +159,7 @@ public class UserBackupManagerServiceTest {
@After
public void tearDown() throws Exception {
mBackupThread.quit();
- ShadowAppBackupUtils.reset();
+ ShadowBackupEligibilityRules.reset();
ShadowApplicationPackageManager.reset();
}
@@ -236,7 +236,7 @@ public class UserBackupManagerServiceTest {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
registerPackages(PACKAGE_1);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+ ShadowBackupEligibilityRules.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
@@ -255,7 +255,7 @@ public class UserBackupManagerServiceTest {
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
setUpCurrentTransport(mTransportManager, mTransport);
registerPackages(PACKAGE_1);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+ ShadowBackupEligibilityRules.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
expectThrows(
@@ -273,7 +273,7 @@ public class UserBackupManagerServiceTest {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
registerPackages(PACKAGE_1, PACKAGE_2);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+ ShadowBackupEligibilityRules.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
String[] filtered =
@@ -801,7 +801,7 @@ public class UserBackupManagerServiceTest {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
for (String packageName : packages) {
registerPackages(packageName);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
+ ShadowBackupEligibilityRules.setAppRunningAndEligibleForBackupWithTransport(packageName);
}
setUpCurrentTransport(mTransportManager, mTransport);
}
@@ -962,7 +962,7 @@ public class UserBackupManagerServiceTest {
@Config(shadows = ShadowKeyValueBackupTask.class)
public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
setUpForRequestBackup(PACKAGE_1);
- ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
+ ShadowBackupEligibilityRules.setAppGetsFullBackup(PACKAGE_1);
UserBackupManagerService backupManagerService =
createBackupManagerServiceForRequestBackup();
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index b5c9375fcc0d..6184c4ed7f1a 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -115,6 +115,7 @@ import com.android.server.backup.testing.TestUtils.ThrowingRunnable;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.testing.shadows.FrameworkShadowLooper;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowBackupDataInput;
@@ -176,6 +177,7 @@ public class KeyValueBackupTaskTest {
private static final String BACKUP_AGENT_SHARED_PREFS_SYNCHRONIZER_CLASS =
"android.app.backup.BackupAgent$SharedPrefsSynchronizer";
private static final int USER_ID = 10;
+ private static final int OPERATION_TYPE = BackupManager.OperationType.BACKUP;
@Mock private TransportManager mTransportManager;
@Mock private DataChangedJournal mOldJournal;
@@ -183,6 +185,7 @@ public class KeyValueBackupTaskTest {
@Mock private IBackupManagerMonitor mMonitor;
@Mock private OnTaskFinishedListener mListener;
@Mock private PackageManagerInternal mPackageManagerInternal;
+
private UserBackupManagerService mBackupManagerService;
private TransportData mTransport;
private ShadowLooper mShadowBackupLooper;
@@ -198,6 +201,7 @@ public class KeyValueBackupTaskTest {
private Looper mMainLooper;
private FrameworkShadowLooper mShadowMainLooper;
private Context mContext;
+ private BackupEligibilityRules mBackupEligibilityRules;
@Before
public void setUp() throws Exception {
@@ -253,6 +257,8 @@ public class KeyValueBackupTaskTest {
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+ mBackupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+ LocalServices.getService(PackageManagerInternal.class), USER_ID, OPERATION_TYPE);
}
@After
@@ -479,7 +485,8 @@ public class KeyValueBackupTaskTest {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(forward(pmAgent)).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1);
runTask(task);
@@ -492,7 +499,8 @@ public class KeyValueBackupTaskTest {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(forward(pmAgent)).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
KeyValueBackupTask task =
createKeyValueBackupTask(transportMock, true, PACKAGE_1, PM_PACKAGE);
@@ -506,7 +514,8 @@ public class KeyValueBackupTaskTest {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(forward(pmAgent)).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, false, PACKAGE_1);
runTask(task);
@@ -742,7 +751,7 @@ public class KeyValueBackupTaskTest {
verify(mBackupManagerService).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
- verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ verify(mObserver).backupFinished(SUCCESS);
assertBackupPendingFor(PACKAGE_1);
}
@@ -775,7 +784,7 @@ public class KeyValueBackupTaskTest {
verify(mBackupManagerService).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
- verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ verify(mObserver).backupFinished(SUCCESS);
assertBackupPendingFor(PACKAGE_1);
}
@@ -792,7 +801,7 @@ public class KeyValueBackupTaskTest {
verify(mBackupManagerService).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
- verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ verify(mObserver).backupFinished(SUCCESS);
assertBackupPendingFor(PACKAGE_1);
}
@@ -810,7 +819,7 @@ public class KeyValueBackupTaskTest {
verify(mBackupManagerService).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
- verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ verify(mObserver).backupFinished(SUCCESS);
assertBackupPendingFor(PACKAGE_1);
}
@@ -1316,7 +1325,8 @@ public class KeyValueBackupTaskTest {
argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
.then(copyBackupDataTo(backupDataPath));
BackupAgent pmAgent = spy(createPmAgent());
- doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(forward(pmAgent)).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
agentOnBackupDo(
pmAgent,
(oldState, dataOutput, newState) -> {
@@ -1380,7 +1390,8 @@ public class KeyValueBackupTaskTest {
setUpAgent(PACKAGE_1);
when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
BackupAgent pmAgent = spy(createPmAgent());
- doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(forward(pmAgent)).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
agentOnBackupDo(
pmAgent,
(oldState, dataOutput, newState) -> {
@@ -1404,7 +1415,8 @@ public class KeyValueBackupTaskTest {
setUpAgent(PACKAGE_1);
when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
BackupAgent pmAgent = spy(createPmAgent());
- doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(forward(pmAgent)).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
agentOnBackupDo(
pmAgent,
(oldState, dataOutput, newState) -> {
@@ -1669,7 +1681,7 @@ public class KeyValueBackupTaskTest {
verify(mReporter).onPackageBackupTransportFailure(PACKAGE_1.packageName);
verify(mReporter).onTransportNotInitialized(mTransport.transportName);
- verify(mReporter).onBackupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+ verify(mReporter).onBackupFinished(ERROR_TRANSPORT_ABORTED);
}
@Test
@@ -1686,7 +1698,7 @@ public class KeyValueBackupTaskTest {
verify(mReporter).onPackageBackupTransportFailure(PM_PACKAGE.packageName);
verify(mReporter).onTransportNotInitialized(mTransport.transportName);
- verify(mReporter).onBackupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+ verify(mReporter).onBackupFinished(ERROR_TRANSPORT_ABORTED);
}
@Test
@@ -1983,7 +1995,8 @@ public class KeyValueBackupTaskTest {
public void testRunTask_whenPmAgentFails_reportsCorrectly() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ when(mBackupManagerService.makeMetadataAgentWithEligibilityRules(
+ mBackupEligibilityRules)).thenReturn(pmAgent);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
runTask(task);
@@ -2001,7 +2014,8 @@ public class KeyValueBackupTaskTest {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
- doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(pmAgent).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
runTask(task);
@@ -2014,7 +2028,8 @@ public class KeyValueBackupTaskTest {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
- doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(pmAgent).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
runTask(task);
@@ -2027,7 +2042,8 @@ public class KeyValueBackupTaskTest {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
- doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(pmAgent).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
runTask(task);
@@ -2040,7 +2056,8 @@ public class KeyValueBackupTaskTest {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(forward(pmAgent)).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
agentOnBackupDo(
pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
@@ -2055,7 +2072,8 @@ public class KeyValueBackupTaskTest {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
+ doReturn(forward(pmAgent)).when(mBackupManagerService)
+ .makeMetadataAgentWithEligibilityRules(mBackupEligibilityRules);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
agentOnBackupDo(
pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
@@ -2652,14 +2670,16 @@ public class KeyValueBackupTaskTest {
mListener,
emptyList(),
/* userInitiated */ false,
- nonIncremental);
+ nonIncremental,
+ mBackupEligibilityRules);
mBackupManager.setUp(mBackupHandler, task);
return task;
}
private PackageManagerBackupAgent createPmAgent() {
PackageManagerBackupAgent pmAgent =
- new PackageManagerBackupAgent(mApplication.getPackageManager(), USER_ID);
+ new PackageManagerBackupAgent(mApplication.getPackageManager(), USER_ID,
+ mBackupEligibilityRules);
pmAgent.attach(mApplication);
pmAgent.onCreate();
return pmAgent;
@@ -2671,7 +2691,8 @@ public class KeyValueBackupTaskTest {
*/
private PackageManagerBackupAgent createThrowingPmAgent(RuntimeException exception) {
PackageManagerBackupAgent pmAgent =
- new ThrowingPackageManagerBackupAgent(mApplication.getPackageManager(), exception);
+ new ThrowingPackageManagerBackupAgent(mApplication.getPackageManager(), exception,
+ mBackupEligibilityRules);
pmAgent.attach(mApplication);
pmAgent.onCreate();
return pmAgent;
@@ -2985,8 +3006,9 @@ public class KeyValueBackupTaskTest {
private final RuntimeException mException;
ThrowingPackageManagerBackupAgent(
- PackageManager packageManager, RuntimeException exception) {
- super(packageManager, USER_ID);
+ PackageManager packageManager, RuntimeException exception,
+ BackupEligibilityRules backupEligibilityRules) {
+ super(packageManager, USER_ID, backupEligibilityRules);
mException = exception;
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupEligibilityRules.java
index 33b8aa73d293..566b0e151402 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupEligibilityRules.java
@@ -22,7 +22,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupEligibilityRules;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -31,8 +31,8 @@ import org.robolectric.annotation.Resetter;
import java.util.HashSet;
import java.util.Set;
-@Implements(AppBackupUtils.class)
-public class ShadowAppBackupUtils {
+@Implements(BackupEligibilityRules.class)
+public class ShadowBackupEligibilityRules {
private static final Set<String> sAppsRunningAndEligibleForBackupWithTransport =
new HashSet<>();
private static final Set<String> sAppsEligibleForBackup = new HashSet<>();
@@ -53,21 +53,19 @@ public class ShadowAppBackupUtils {
}
@Implementation
- protected static boolean appIsRunningAndEligibleForBackupWithTransport(
+ protected boolean appIsRunningAndEligibleForBackupWithTransport(
@Nullable TransportClient transportClient,
- String packageName,
- PackageManager pm,
- int userId) {
+ String packageName) {
return sAppsRunningAndEligibleForBackupWithTransport.contains(packageName);
}
@Implementation
- protected static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) {
+ protected boolean appIsEligibleForBackup(ApplicationInfo app) {
return sAppsEligibleForBackup.contains(app.packageName);
}
@Implementation
- protected static boolean appGetsFullBackup(PackageInfo packageInfo) {
+ protected boolean appGetsFullBackup(PackageInfo packageInfo) {
return sAppsGetFullBackup.contains(packageInfo.packageName);
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowCloseGuard.java b/services/robotests/src/com/android/server/testing/shadows/ShadowCloseGuard.java
index c9984bf07059..4055dfc08f90 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowCloseGuard.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowCloseGuard.java
@@ -44,5 +44,10 @@ public class ShadowCloseGuard {
public void report(String message, Throwable allocationSite) {
mReports += 1;
}
+
+ @Override
+ public void report(String message) {
+ mReports += 1;
+ }
}
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
index ac5d2da9c3cf..fd51df7ab1f9 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
@@ -24,6 +24,7 @@ import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.keyvalue.KeyValueBackupReporter;
import com.android.server.backup.keyvalue.KeyValueBackupTask;
import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.utils.BackupEligibilityRules;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -63,7 +64,8 @@ public class ShadowKeyValueBackupTask {
OnTaskFinishedListener listener,
List<String> pendingFullBackups,
boolean userInitiated,
- boolean nonIncremental) {
+ boolean nonIncremental,
+ BackupEligibilityRules backupEligibilityRules) {
mListener = listener;
mQueue = queue;
mPendingFullBackups = pendingFullBackups;
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 8daef5fad032..5161070398d7 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -25,6 +25,7 @@ import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.utils.BackupEligibilityRules;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -67,7 +68,8 @@ public class ShadowPerformUnifiedRestoreTask {
int pmToken,
boolean isFullSystemRestore,
@Nullable String[] filterSet,
- OnTaskFinishedListener listener) {
+ OnTaskFinishedListener listener,
+ BackupEligibilityRules backupEligibilityRules) {
mBackupManagerService = backupManagerService;
mPackage = targetPackage;
mIsFullSystemRestore = isFullSystemRestore;
diff --git a/services/tests/PackageManagerServiceTests/OWNERS b/services/tests/PackageManagerServiceTests/OWNERS
new file mode 100644
index 000000000000..182dfe8fca9e
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/OWNERS
@@ -0,0 +1,3 @@
+chiuwinson@google.com
+patb@google.com
+toddke@google.com
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
index 490f96d8f426..234fcf19062d 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -22,10 +22,16 @@ import java.io.File
import java.io.FileOutputStream
internal fun SystemPreparer.pushApk(file: String, partition: Partition) =
- pushResourceFile(file, HostUtils.makePathForApk(file, partition))
-
-internal fun SystemPreparer.deleteApk(file: String, partition: Partition) =
- deleteFile(partition.baseFolder.resolve(file.removeSuffix(".apk")).toString())
+ pushResourceFile(file, HostUtils.makePathForApk(file, partition).toString())
+
+internal fun SystemPreparer.deleteApkFolders(
+ partition: Partition,
+ vararg javaResourceNames: String
+) = apply {
+ javaResourceNames.forEach {
+ deleteFile(partition.baseAppFolder.resolve(it.removeSuffix(".apk")).toString())
+ }
+}
internal object HostUtils {
@@ -40,10 +46,9 @@ internal object HostUtils {
makePathForApk(File(fileName), partition)
fun makePathForApk(file: File, partition: Partition) =
- partition.baseFolder
+ partition.baseAppFolder
.resolve(file.nameWithoutExtension)
.resolve(file.name)
- .toString()
fun copyResourceToHostFile(javaResourceName: String, file: File): File {
javaClass.classLoader!!.getResource(javaResourceName).openStream().use { input ->
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
index 98e045d0a203..39b40d8d3195 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
@@ -45,23 +45,24 @@ class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
private val tempFolder = TemporaryFolder()
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
- SystemPreparer.RebootStrategy.START_STOP, deviceRebootRule) { this.device }
+ SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
@get:Rule
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
+ private val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT)
@Before
@After
- fun uninstallDataPackage() {
+ fun removeApk() {
device.uninstallPackage(TEST_PKG_NAME)
+ device.deleteFile(filePath.parent.toString())
+ device.reboot()
}
@Test
fun verify() {
// First, push a system app to the device and then update it so there's a data variant
- val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT)
-
- preparer.pushResourceFile(VERSION_ONE, filePath)
+ preparer.pushResourceFile(VERSION_ONE, filePath.toString())
.reboot()
val versionTwoFile = HostUtils.copyResourceToHostFile(VERSION_TWO, tempFolder.newFile())
@@ -69,8 +70,8 @@ class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
assertThat(device.installPackage(versionTwoFile, true)).isNull()
// Then push a bad update to the system, overwriting the existing file as if an OTA occurred
- preparer.deleteFile(filePath)
- .pushResourceFile(VERSION_THREE_INVALID, filePath)
+ preparer.deleteFile(filePath.toString())
+ .pushResourceFile(VERSION_THREE_INVALID, filePath.toString())
.reboot()
// This will remove the package from the device, which is expected
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
index 90494c591363..fb0348c3146b 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
@@ -20,6 +20,8 @@ import com.android.internal.util.test.SystemPreparer
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
import org.junit.ClassRule
import org.junit.Rule
import org.junit.Test
@@ -43,11 +45,18 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
private val tempFolder = TemporaryFolder()
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
- SystemPreparer.RebootStrategy.START_STOP, deviceRebootRule) { this.device }
+ SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
@get:Rule
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
+ @Before
+ @After
+ fun deleteApkFolders() {
+ preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE,
+ NEW_PKG)
+ }
+
@Test
fun lowerVersion() {
runForApk(VERSION_ONE)
@@ -71,28 +80,28 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
preparer.pushApk(apk, Partition.SYSTEM)
.reboot()
- device.getAppPackageInfo(TEST_PKG_NAME).run {
- assertThat(codePath).contains(apk.removeSuffix(".apk"))
- }
+ assertCodePath(apk)
// Ensure data is preserved by writing to the original dataDir
val file = tempFolder.newFile().apply { writeText("Test") }
device.pushFile(file, "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt")
- preparer.deleteApk(apk, Partition.SYSTEM)
+ preparer.deleteApkFolders(Partition.SYSTEM, apk)
.pushApk(NEW_PKG, Partition.SYSTEM)
.reboot()
- device.getAppPackageInfo(TEST_PKG_NAME)
- .run {
- assertThat(this.toString()).isNotEmpty()
- assertThat(codePath)
- .contains(NEW_PKG.removeSuffix(".apk"))
- }
+ assertCodePath(NEW_PKG)
// And then reading the data contents back
assertThat(device.pullFileContents(
"${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt"))
.isEqualTo("Test")
}
+
+ private fun assertCodePath(apk: String) {
+ // dumpsys package and therefore device.getAppPackageInfo doesn't work here for some reason,
+ // so parse the package dump directly to see if the path matches.
+ assertThat(device.executeShellCommand("pm dump $TEST_PKG_NAME"))
+ .contains(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString())
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
index 35192a73ceda..654c11c5bf81 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
@@ -20,7 +20,7 @@ import java.nio.file.Path
import java.nio.file.Paths
// Unfortunately no easy way to access PMS SystemPartitions, so mock them here
-internal enum class Partition(val baseFolder: Path) {
+internal enum class Partition(val baseAppFolder: Path) {
SYSTEM("/system/app"),
VENDOR("/vendor/app"),
PRODUCT("/product/app"),
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index f0758dd3ef32..8ccaeddc8355 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -18,7 +18,7 @@ package com.android.server;
import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
-import static com.android.server.AppStateTracker.TARGET_OP;
+import static com.android.server.AppStateTrackerImpl.TARGET_OP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -57,6 +57,7 @@ import android.os.PowerSaveState;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.provider.Settings.Global;
import android.test.mock.MockContentResolver;
import android.util.ArraySet;
@@ -67,7 +68,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
-import com.android.server.AppStateTracker.Listener;
+import com.android.server.AppStateTrackerImpl.Listener;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
@@ -90,15 +91,16 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
- * Tests for {@link AppStateTracker}
+ * Tests for {@link AppStateTrackerImpl}
*
* Run with: atest com.android.server.AppStateTrackerTest
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AppStateTrackerTest {
- private class AppStateTrackerTestable extends AppStateTracker {
+ private class AppStateTrackerTestable extends AppStateTrackerImpl {
AppStateTrackerTestable() {
super(mMockContext, Looper.getMainLooper());
}
@@ -130,6 +132,7 @@ public class AppStateTrackerTest {
@Override
AppStandbyInternal injectAppStandbyInternal() {
+ when(mMockAppStandbyInternal.isAppIdleEnabled()).thenReturn(true);
return mMockAppStandbyInternal;
}
@@ -141,7 +144,9 @@ public class AppStateTrackerTest {
}
@Override
- boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; };
+ boolean isSmallBatteryDevice() {
+ return mIsSmallBatteryDevice;
+ }
}
private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
@@ -201,6 +206,13 @@ public class AppStateTrackerTest {
mMainHandler = new Handler(Looper.getMainLooper());
}
+ /**
+ * Enqueues a message and waits for it to complete. This ensures that any messages posted until
+ * now have been executed.
+ *
+ * Note that these messages may have enqueued more messages, which may or may not have executed
+ * when this method returns.
+ */
private void waitUntilMainHandlerDrain() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
mMainHandler.post(() -> {
@@ -362,6 +374,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidActive(UID_1);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
@@ -370,6 +383,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidGone(UID_1, /*disable=*/ false);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
@@ -378,12 +392,14 @@ public class AppStateTrackerTest {
mIUidObserver.onUidActive(UID_1);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
mIUidObserver.onUidIdle(UID_1, /*disable=*/ false);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
@@ -499,6 +515,8 @@ public class AppStateTrackerTest {
mIUidObserver.onUidActive(UID_1);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
+
assertTrue(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
@@ -517,6 +535,8 @@ public class AppStateTrackerTest {
ActivityManager.PROCESS_CAPABILITY_NONE);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
+
assertTrue(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
@@ -535,6 +555,8 @@ public class AppStateTrackerTest {
ActivityManager.PROCESS_CAPABILITY_NONE);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
+
assertTrue(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
@@ -546,6 +568,8 @@ public class AppStateTrackerTest {
mIUidObserver.onUidGone(UID_1, true);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
+
assertFalse(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
@@ -557,6 +581,8 @@ public class AppStateTrackerTest {
mIUidObserver.onUidIdle(UID_2, true);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
+
assertFalse(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
@@ -570,6 +596,8 @@ public class AppStateTrackerTest {
ActivityManager.PROCESS_CAPABILITY_NONE);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
+
assertFalse(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
@@ -583,6 +611,8 @@ public class AppStateTrackerTest {
ActivityManager.PROCESS_CAPABILITY_NONE);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
+
assertFalse(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
@@ -692,7 +722,7 @@ public class AppStateTrackerTest {
AppOpsManager.MODE_IGNORED,
Collections.emptyMap()));
entries.add(new OpEntry(
- AppStateTracker.TARGET_OP,
+ AppStateTrackerImpl.TARGET_OP,
AppOpsManager.MODE_IGNORED,
Collections.emptyMap()));
@@ -701,7 +731,7 @@ public class AppStateTrackerTest {
//--------------------------------------------------
entries = new ArrayList<>();
entries.add(new OpEntry(
- AppStateTracker.TARGET_OP,
+ AppStateTrackerImpl.TARGET_OP,
AppOpsManager.MODE_IGNORED,
Collections.emptyMap()));
@@ -710,7 +740,7 @@ public class AppStateTrackerTest {
//--------------------------------------------------
entries = new ArrayList<>();
entries.add(new OpEntry(
- AppStateTracker.TARGET_OP,
+ AppStateTrackerImpl.TARGET_OP,
AppOpsManager.MODE_ALLOWED,
Collections.emptyMap()));
@@ -719,7 +749,7 @@ public class AppStateTrackerTest {
//--------------------------------------------------
entries = new ArrayList<>();
entries.add(new OpEntry(
- AppStateTracker.TARGET_OP,
+ AppStateTrackerImpl.TARGET_OP,
AppOpsManager.MODE_IGNORED,
Collections.emptyMap()));
entries.add(new OpEntry(
@@ -1007,6 +1037,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidActive(UID_10_1);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
@@ -1019,6 +1050,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidGone(UID_10_1, true);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
@@ -1031,6 +1063,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidActive(UID_10_1);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
@@ -1043,6 +1076,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidIdle(UID_10_1, true);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
@@ -1069,6 +1103,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidActive(UID_10_1);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
@@ -1081,6 +1116,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidGone(UID_10_1, true);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
@@ -1093,6 +1129,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidActive(UID_10_1);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
@@ -1105,6 +1142,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidIdle(UID_10_1, true);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
@@ -1124,6 +1162,7 @@ public class AppStateTrackerTest {
mIUidObserver.onUidActive(UID_10_1);
waitUntilMainHandlerDrain();
+ waitUntilMainHandlerDrain();
setAppOps(UID_2, PACKAGE_2, true);
setAppOps(UID_10_2, PACKAGE_2, true);
@@ -1227,7 +1266,7 @@ public class AppStateTrackerTest {
private void checkAnyAppIdUnwhitelisted(int[] prevArray, int[] newArray, boolean expected) {
assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
- expected, AppStateTracker.isAnyAppIdUnwhitelisted(prevArray, newArray));
+ expected, AppStateTrackerImpl.isAnyAppIdUnwhitelisted(prevArray, newArray));
// Also test isAnyAppIdUnwhitelistedSlow.
assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
@@ -1259,7 +1298,7 @@ public class AppStateTrackerTest {
final int[] array2 = makeRandomArray();
final boolean expected = isAnyAppIdUnwhitelistedSlow(array1, array2);
- final boolean actual = AppStateTracker.isAnyAppIdUnwhitelisted(array1, array2);
+ final boolean actual = AppStateTrackerImpl.isAnyAppIdUnwhitelisted(array1, array2);
assertEquals("Input: " + Arrays.toString(array1) + " " + Arrays.toString(array2),
expected, actual);
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 9251031eb5cd..6cd083e11754 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -164,7 +164,7 @@ public class DeviceIdleControllerTest {
}
@Override
- AppStateTracker getAppStateTracker(Context ctx, Looper loop) {
+ AppStateTrackerImpl getAppStateTracker(Context ctx, Looper loop) {
return mAppStateTracker;
}
@@ -260,7 +260,7 @@ public class DeviceIdleControllerTest {
}
}
- private class AppStateTrackerForTest extends AppStateTracker {
+ private class AppStateTrackerForTest extends AppStateTrackerImpl {
AppStateTrackerForTest(Context ctx, Looper looper) {
super(ctx, looper);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 6c9b61822c0b..77232dc4644c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -89,6 +89,7 @@ import com.android.dx.mockito.inline.extended.MockedVoidMethod;
import com.android.internal.annotations.GuardedBy;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppStateTracker;
+import com.android.server.AppStateTrackerImpl;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.usage.AppStandbyInternal;
@@ -131,7 +132,7 @@ public class AlarmManagerServiceTest {
@Mock
private ActivityManagerInternal mActivityManagerInternal;
@Mock
- private AppStateTracker mAppStateTracker;
+ private AppStateTrackerImpl mAppStateTracker;
@Mock
private AlarmManagerService.ClockReceiver mClockReceiver;
@Mock
@@ -769,8 +770,8 @@ public class AlarmManagerServiceTest {
@Test
public void testAlarmRestrictedInBatterSaver() throws Exception {
- final ArgumentCaptor<AppStateTracker.Listener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(AppStateTracker.Listener.class);
+ final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -795,10 +796,10 @@ public class AlarmManagerServiceTest {
@Test
public void alarmsRemovedOnAppStartModeDisabled() {
- final ArgumentCaptor<AppStateTracker.Listener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(AppStateTracker.Listener.class);
+ final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
- final AppStateTracker.Listener listener = listenerArgumentCaptor.getValue();
+ final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue();
final PendingIntent alarmPi1 = getNewMockPendingIntent();
final PendingIntent alarmPi2 = getNewMockPendingIntent();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 23381ffd4eaa..8db09b4f156c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -192,6 +192,7 @@ public class ApplicationExitInfoTest {
final int app1Uid = 10123;
final int app1Pid1 = 12345;
final int app1Pid2 = 12346;
+ final int app1sPid1 = 13456;
final int app1DefiningUid = 23456;
final int app1ConnectiongGroup = 10;
final int app1UidUser2 = 1010123;
@@ -202,8 +203,12 @@ public class ApplicationExitInfoTest {
final long app1Rss2 = 45679;
final long app1Pss3 = 34569;
final long app1Rss3 = 45680;
+ final long app1sPss1 = 56789;
+ final long app1sRss1 = 67890;
final String app1ProcessName = "com.android.test.stub1:process";
final String app1PackageName = "com.android.test.stub1";
+ final String app1sProcessName = "com.android.test.stub_shared:process";
+ final String app1sPackageName = "com.android.test.stub_shared";
final byte[] app1Cookie1 = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
(byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08};
final byte[] app1Cookie2 = {(byte) 0x08, (byte) 0x07, (byte) 0x06, (byte) 0x05,
@@ -262,6 +267,29 @@ public class ApplicationExitInfoTest {
app1Cookie1.length));
assertEquals(info.getTraceInputStream(), null);
+ // Now create a process record from a different package but shared UID.
+ sleep(1);
+ final long now1s = System.currentTimeMillis();
+ app = makeProcessRecord(
+ app1sPid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ 0, // connectionGroup
+ PROCESS_STATE_BOUND_TOP, // procstate
+ app1sPss1, // pss
+ app1sRss1, // rss
+ app1sProcessName, // processName
+ app1sPackageName); // packageName
+ doReturn(new Pair<Long, Object>(now1s, Integer.valueOf(0)))
+ .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
+ .remove(anyInt(), anyInt());
+ doReturn(null)
+ .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
+ .remove(anyInt(), anyInt());
+ noteAppKill(app, ApplicationExitInfo.REASON_USER_REQUESTED,
+ ApplicationExitInfo.SUBREASON_UNKNOWN, null);
+
// Case 2: create another app1 process record with a different pid
sleep(1);
final long now2 = System.currentTimeMillis();
@@ -290,8 +318,8 @@ public class ApplicationExitInfoTest {
list.clear();
// Get all the records for app1Uid
- mAppExitInfoTracker.getExitInfo(app1PackageName, app1Uid, 0, 0, list);
- assertEquals(2, list.size());
+ mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
+ assertEquals(3, list.size());
info = list.get(0);
@@ -315,7 +343,26 @@ public class ApplicationExitInfoTest {
assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie2,
app1Cookie2.length));
+
info = list.get(1);
+ verifyApplicationExitInfo(
+ info, // info
+ now1s, // timestamp
+ app1sPid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ app1sProcessName, // processName
+ 0, // connectionGroup
+ ApplicationExitInfo.REASON_USER_REQUESTED, // reason
+ null, // subReason
+ null, // status
+ app1sPss1, // pss
+ app1sRss1, // rss
+ IMPORTANCE_FOREGROUND, // importance
+ null); // description
+
+ info = list.get(2);
assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1,
app1Cookie1.length));
@@ -808,7 +855,7 @@ public class ApplicationExitInfoTest {
list.clear();
mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
- assertEquals(2, list.size());
+ assertEquals(3, list.size());
info = list.get(0);
@@ -831,6 +878,24 @@ public class ApplicationExitInfoTest {
null); // description
info = list.get(1);
+ verifyApplicationExitInfo(
+ info, // info
+ now1s, // timestamp
+ app1sPid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ app1sProcessName, // processName
+ 0, // connectionGroup
+ ApplicationExitInfo.REASON_USER_REQUESTED, // reason
+ null, // subReason
+ null, // status
+ app1sPss1, // pss
+ app1sRss1, // rss
+ IMPORTANCE_FOREGROUND, // importance
+ null); // description
+
+ info = list.get(2);
exitCode = 5;
verifyApplicationExitInfo(
info, // info
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index cdafd32cbbb5..2a267c413c31 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -63,7 +63,6 @@ import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.answer;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
@@ -85,6 +84,7 @@ import android.os.Build;
import android.os.IBinder;
import android.os.PowerManagerInternal;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -131,6 +131,7 @@ public class MockingOomAdjusterTests {
private static final int MOCKAPP5_UID = MOCKAPP_UID + 4;
private static final String MOCKAPP5_PROCESSNAME = "test #5";
private static final String MOCKAPP5_PACKAGENAME = "com.android.test.test5";
+ private static final int MOCKAPP2_UID_OTHER = MOCKAPP2_UID + UserHandle.PER_USER_RANGE;
private static Context sContext;
private static PackageManagerInternal sPackageManagerInternal;
private static ActivityManagerService sService;
@@ -169,9 +170,10 @@ public class MockingOomAdjusterTests {
mock(SparseArray.class));
setFieldValue(ActivityManagerService.class, sService, "mOomAdjProfiler",
mock(OomAdjProfiler.class));
+ setFieldValue(ActivityManagerService.class, sService, "mUserController",
+ mock(UserController.class));
doReturn(new ActivityManagerService.ProcessChangeItem()).when(sService)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
- doReturn(true).when(sService).containsTopUiOrRunningRemoteAnimOrEmptyLocked(any());
sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList,
mock(ActiveUids.class));
sService.mOomAdjuster.mAdjSeq = 10000;
@@ -268,21 +270,6 @@ public class MockingOomAdjusterTests {
@SuppressWarnings("GuardedBy")
@Test
- public void testUpdateOomAdj_DoOne_TopApp_PreemptedByTopUi() {
- ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
- MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(app).when(sService).getTopAppLocked();
- doReturn(false).when(sService).containsTopUiOrRunningRemoteAnimOrEmptyLocked(eq(app));
- sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
- sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doReturn(null).when(sService).getTopAppLocked();
-
- assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT);
- }
-
- @SuppressWarnings("GuardedBy")
- @Test
public void testUpdateOomAdj_DoOne_RunningInstrumentation() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
@@ -1638,6 +1625,117 @@ public class MockingOomAdjusterTests {
assertEquals(SERVICE_ADJ, app.setAdj);
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testUpdateOomAdj_DoAll_Service_KeepWarmingList() {
+ final ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID_OTHER,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ final int userOwner = 0;
+ final int userOther = 1;
+ final int cachedAdj1 = CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+ doReturn(userOwner).when(sService.mUserController).getCurrentUserId();
+
+ final ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ lru.clear();
+ lru.add(app2);
+ lru.add(app);
+
+ final ComponentName cn = ComponentName.unflattenFromString(
+ MOCKAPP_PACKAGENAME + "/.TestService");
+ final ComponentName cn2 = ComponentName.unflattenFromString(
+ MOCKAPP2_PACKAGENAME + "/.TestService");
+ final long now = SystemClock.uptimeMillis();
+
+ sService.mConstants.KEEP_WARMING_SERVICES.clear();
+ final ServiceInfo si = mock(ServiceInfo.class);
+ si.applicationInfo = mock(ApplicationInfo.class);
+ ServiceRecord s = spy(new ServiceRecord(sService, null, cn, cn, null, 0, null,
+ si, false, null));
+ doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
+ s.startRequested = true;
+ s.lastActivity = now;
+
+ app.setCached(false);
+ app.startService(s);
+ app.hasShownUi = true;
+
+ final ServiceInfo si2 = mock(ServiceInfo.class);
+ si2.applicationInfo = mock(ApplicationInfo.class);
+ si2.applicationInfo.uid = MOCKAPP2_UID_OTHER;
+ ServiceRecord s2 = spy(new ServiceRecord(sService, null, cn2, cn2, null, 0, null,
+ si2, false, null));
+ doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s2).getConnections();
+ s2.startRequested = true;
+ s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+
+ app2.setCached(false);
+ app2.startService(s2);
+ app2.hasShownUi = false;
+
+ sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
+ assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj2, "cch-started-services");
+
+ app.setProcState = PROCESS_STATE_NONEXISTENT;
+ app.adjType = null;
+ app.setAdj = UNKNOWN_ADJ;
+ app.hasShownUi = false;
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+
+ app.setCached(false);
+ app.setProcState = PROCESS_STATE_NONEXISTENT;
+ app.adjType = null;
+ app.setAdj = UNKNOWN_ADJ;
+ s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+
+ app.stopService(s);
+ app.setProcState = PROCESS_STATE_NONEXISTENT;
+ app.adjType = null;
+ app.setAdj = UNKNOWN_ADJ;
+ app.hasShownUi = true;
+ sService.mConstants.KEEP_WARMING_SERVICES.add(cn);
+ sService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
+ s = spy(new ServiceRecord(sService, null, cn, cn, null, 0, null,
+ si, false, null));
+ doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
+ s.startRequested = true;
+ s.lastActivity = now;
+
+ app.startService(s);
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+ assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+
+ app.setCached(true);
+ app.setProcState = PROCESS_STATE_NONEXISTENT;
+ app.adjType = null;
+ app.setAdj = UNKNOWN_ADJ;
+ app.hasShownUi = false;
+ s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+ assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+
+ doReturn(userOther).when(sService.mUserController).getCurrentUserId();
+ sService.mOomAdjuster.handleUserSwitchedLocked();
+
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+ assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+ assertProcStates(app2, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+ }
+
private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName,
String packageName, boolean hasShownUi) {
long now = SystemClock.uptimeMillis();
@@ -1764,4 +1862,12 @@ public class MockingOomAdjusterTests {
assertEquals(expectedAdj, app.setAdj);
assertEquals(expectedSchedGroup, app.setSchedGroup);
}
+
+ private void assertProcStates(ProcessRecord app, boolean expectedCached,
+ int expectedProcState, int expectedAdj, String expectedAdjType) {
+ assertEquals(expectedCached, app.isCached());
+ assertEquals(expectedProcState, app.setProcState);
+ assertEquals(expectedAdj, app.setAdj);
+ assertEquals(expectedAdjType, app.adjType);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 7446289cd498..924ad7f3f5a1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -174,10 +174,10 @@ public class BlobStoreManagerServiceTest {
mService.handlePackageRemoved(TEST_PKG1, TEST_UID1);
// Verify sessions are removed
- verify(sessionFile1).delete();
- verify(sessionFile2, never()).delete();
- verify(sessionFile3, never()).delete();
- verify(sessionFile4).delete();
+ verify(session1).destroy();
+ verify(session2, never()).destroy();
+ verify(session3, never()).destroy();
+ verify(session4).destroy();
assertThat(mUserSessions.size()).isEqualTo(2);
assertThat(mUserSessions.get(sessionId1)).isNull();
@@ -193,9 +193,9 @@ public class BlobStoreManagerServiceTest {
verify(blobMetadata3).removeCommitter(TEST_PKG1, TEST_UID1);
verify(blobMetadata3).removeLeasee(TEST_PKG1, TEST_UID1);
- verify(blobFile1, never()).delete();
- verify(blobFile2).delete();
- verify(blobFile3).delete();
+ verify(blobMetadata1, never()).destroy();
+ verify(blobMetadata2).destroy();
+ verify(blobMetadata3).destroy();
assertThat(mUserBlobs.size()).isEqualTo(1);
assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
@@ -272,9 +272,9 @@ public class BlobStoreManagerServiceTest {
mService.handleIdleMaintenanceLocked();
// Verify stale sessions are removed
- verify(sessionFile1).delete();
- verify(sessionFile2, never()).delete();
- verify(sessionFile3).delete();
+ verify(session1).destroy();
+ verify(session2, never()).destroy();
+ verify(session3).destroy();
assertThat(mUserSessions.size()).isEqualTo(1);
assertThat(mUserSessions.get(sessionId2)).isNotNull();
@@ -317,9 +317,9 @@ public class BlobStoreManagerServiceTest {
mService.handleIdleMaintenanceLocked();
// Verify stale blobs are removed
- verify(blobFile1).delete();
- verify(blobFile2, never()).delete();
- verify(blobFile3).delete();
+ verify(blobMetadata1).destroy();
+ verify(blobMetadata2, never()).destroy();
+ verify(blobMetadata3).destroy();
assertThat(mUserBlobs.size()).isEqualTo(1);
assertThat(mUserBlobs.get(blobHandle2)).isNotNull();
@@ -377,11 +377,11 @@ public class BlobStoreManagerServiceTest {
}
private BlobMetadata createBlobMetadataMock(long blobId, File blobFile,
- BlobHandle blobHandle, boolean hasLeases) {
+ BlobHandle blobHandle, boolean hasValidLeases) {
final BlobMetadata blobMetadata = mock(BlobMetadata.class);
doReturn(blobId).when(blobMetadata).getBlobId();
doReturn(blobFile).when(blobMetadata).getBlobFile();
- doReturn(hasLeases).when(blobMetadata).hasLeases();
+ doReturn(hasValidLeases).when(blobMetadata).hasValidLeases();
doReturn(blobHandle).when(blobMetadata).getBlobHandle();
doCallRealMethod().when(blobMetadata).shouldBeDeleted(anyBoolean());
doReturn(true).when(blobMetadata).hasLeaseWaitTimeElapsedForAll();
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 93402681271a..9d2393b4a8f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -188,10 +188,10 @@ public class LocalDisplayAdapterTest {
}
@Test
- public void testAfterDisplayChange_ModesAreUpdated() throws Exception {
- SurfaceControl.DisplayConfig displayInfo = createFakeDisplayConfig(1920, 1080, 60f);
+ public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception {
+ SurfaceControl.DisplayConfig displayConfig = createFakeDisplayConfig(1920, 1080, 60f);
SurfaceControl.DisplayConfig[] configs =
- new SurfaceControl.DisplayConfig[]{displayInfo};
+ new SurfaceControl.DisplayConfig[]{displayConfig};
FakeDisplay display = new FakeDisplay(PORT_A, configs, 0);
setUpDisplay(display);
updateAvailableDisplays();
@@ -205,20 +205,20 @@ public class LocalDisplayAdapterTest {
0).getDisplayDeviceInfoLocked();
assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
- assertModeIsSupported(displayDeviceInfo.supportedModes, displayInfo);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
- assertThat(defaultMode.matches(displayInfo.width, displayInfo.height,
- displayInfo.refreshRate)).isTrue();
+ assertThat(defaultMode.matches(displayConfig.width, displayConfig.height,
+ displayConfig.refreshRate)).isTrue();
Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
- assertThat(activeMode.matches(displayInfo.width, displayInfo.height,
- displayInfo.refreshRate)).isTrue();
+ assertThat(activeMode.matches(displayConfig.width, displayConfig.height,
+ displayConfig.refreshRate)).isTrue();
// Change the display
SurfaceControl.DisplayConfig addedDisplayInfo = createFakeDisplayConfig(3840, 2160,
60f);
- configs = new SurfaceControl.DisplayConfig[]{displayInfo, addedDisplayInfo};
+ configs = new SurfaceControl.DisplayConfig[]{displayConfig, addedDisplayInfo};
display.configs = configs;
display.activeConfig = 1;
setUpDisplay(display);
@@ -236,7 +236,7 @@ public class LocalDisplayAdapterTest {
displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
- assertModeIsSupported(displayDeviceInfo.supportedModes, displayInfo);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo);
activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
@@ -248,6 +248,80 @@ public class LocalDisplayAdapterTest {
addedDisplayInfo.refreshRate)).isTrue();
}
+ @Test
+ public void testAfterDisplayChange_HdrCapabilitiesAreUpdated() throws Exception {
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ Display.HdrCapabilities initialHdrCapabilities = new Display.HdrCapabilities(new int[0],
+ 1000, 1000, 0);
+ display.hdrCapabilities = initialHdrCapabilities;
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays).isEmpty();
+
+ DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
+ 0).getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.hdrCapabilities).isEqualTo(initialHdrCapabilities);
+
+ // Change the display
+ Display.HdrCapabilities changedHdrCapabilities = new Display.HdrCapabilities(
+ new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0);
+ display.hdrCapabilities = changedHdrCapabilities;
+ setUpDisplay(display);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+
+ DisplayDevice displayDevice = mListener.changedDisplays.get(0);
+ displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+ displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.hdrCapabilities).isEqualTo(changedHdrCapabilities);
+ }
+
+ @Test
+ public void testAfterDisplayChange_ColorModesAreUpdated() throws Exception {
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ final int[] initialColorModes = new int[]{Display.COLOR_MODE_BT709};
+ display.colorModes = initialColorModes;
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays).isEmpty();
+
+ DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0)
+ .getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.colorMode).isEqualTo(Display.COLOR_MODE_BT709);
+ assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(initialColorModes);
+
+ // Change the display
+ final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT};
+ display.colorModes = changedColorModes;
+ setUpDisplay(display);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+
+ DisplayDevice displayDevice = mListener.changedDisplays.get(0);
+ displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+ displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.colorMode).isEqualTo(Display.COLOR_MODE_DEFAULT);
+ assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(changedColorModes);
+ }
+
private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort,
float expectedXdpi,
float expectedYDpi,
@@ -279,6 +353,9 @@ public class LocalDisplayAdapterTest {
public final SurfaceControl.DisplayInfo info;
public SurfaceControl.DisplayConfig[] configs;
public int activeConfig;
+ public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
+ public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
+ 1000, 1000, 0);
private FakeDisplay(int port) {
this.address = createDisplayAddress(port);
@@ -306,8 +383,10 @@ public class LocalDisplayAdapterTest {
() -> SurfaceControl.getDisplayConfigs(display.token));
doReturn(display.activeConfig).when(() -> SurfaceControl.getActiveConfig(display.token));
doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
- doReturn(new int[] { 0 }).when(
+ doReturn(display.colorModes).when(
() -> SurfaceControl.getDisplayColorModes(display.token));
+ doReturn(display.hdrCapabilities).when(
+ () -> SurfaceControl.getHdrCapabilities(display.token));
doReturn(new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f))
.when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 730f303f036a..043ca9e02873 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -39,6 +39,7 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -54,8 +55,10 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import com.android.server.AppStateTracker;
+import com.android.server.AppStateTrackerImpl;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
+import com.android.server.SystemServiceManager;
import com.android.server.job.controllers.JobStatus;
import com.android.server.usage.AppStandbyInternal;
@@ -82,6 +85,7 @@ public class JobSchedulerServiceTest {
private class TestJobSchedulerService extends JobSchedulerService {
TestJobSchedulerService(Context context) {
super(context);
+ mAppStateTracker = mock(AppStateTrackerImpl.class);
}
@Override
@@ -109,7 +113,7 @@ public class JobSchedulerServiceTest {
.when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
when(mContext.getString(anyInt())).thenReturn("some_test_string");
// Called in BackgroundJobsController constructor.
- doReturn(mock(AppStateTracker.class))
+ doReturn(mock(AppStateTrackerImpl.class))
.when(() -> LocalServices.getService(AppStateTracker.class));
// Called in BatteryController constructor.
doReturn(mock(BatteryManagerInternal.class))
@@ -136,6 +140,9 @@ public class JobSchedulerServiceTest {
} catch (RemoteException e) {
fail("registerUidObserver threw exception: " + e.getMessage());
}
+ // Called by QuotaTracker
+ doReturn(mock(SystemServiceManager.class))
+ .when(() -> LocalServices.getService(SystemServiceManager.class));
JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
JobSchedulerService.sElapsedRealtimeClock =
@@ -750,4 +757,90 @@ public class JobSchedulerServiceTest {
maybeQueueFunctor.postProcess();
assertEquals(3, mService.mPendingJobs.size());
}
+
+ /** Tests that jobs scheduled by the app itself are counted towards scheduling limits. */
+ @Test
+ public void testScheduleLimiting_RegularSchedule_Blocked() {
+ mService.mConstants.ENABLE_API_QUOTAS = true;
+ mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300;
+ mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000;
+ mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false;
+ mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true;
+ mService.updateQuotaTracker();
+
+ final JobInfo job = createJobInfo().setPersisted(true).build();
+ for (int i = 0; i < 500; ++i) {
+ final int expected =
+ i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE;
+ assertEquals("Got unexpected result for schedule #" + (i + 1),
+ expected,
+ mService.scheduleAsPackage(job, null, 10123, null, 0, ""));
+ }
+ }
+
+ /**
+ * Tests that jobs scheduled by the app itself succeed even if the app is above the scheduling
+ * limit.
+ */
+ @Test
+ public void testScheduleLimiting_RegularSchedule_Allowed() {
+ mService.mConstants.ENABLE_API_QUOTAS = true;
+ mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300;
+ mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000;
+ mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false;
+ mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
+ mService.updateQuotaTracker();
+
+ final JobInfo job = createJobInfo().setPersisted(true).build();
+ for (int i = 0; i < 500; ++i) {
+ assertEquals("Got unexpected result for schedule #" + (i + 1),
+ JobScheduler.RESULT_SUCCESS,
+ mService.scheduleAsPackage(job, null, 10123, null, 0, ""));
+ }
+ }
+
+ /**
+ * Tests that jobs scheduled through a proxy (eg. system server) don't count towards scheduling
+ * limits.
+ */
+ @Test
+ public void testScheduleLimiting_Proxy() {
+ mService.mConstants.ENABLE_API_QUOTAS = true;
+ mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300;
+ mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000;
+ mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false;
+ mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true;
+ mService.updateQuotaTracker();
+
+ final JobInfo job = createJobInfo().setPersisted(true).build();
+ for (int i = 0; i < 500; ++i) {
+ assertEquals("Got unexpected result for schedule #" + (i + 1),
+ JobScheduler.RESULT_SUCCESS,
+ mService.scheduleAsPackage(job, null, 10123, "proxied.package", 0, ""));
+ }
+ }
+
+ /**
+ * Tests that jobs scheduled by an app for itself as if through a proxy are counted towards
+ * scheduling limits.
+ */
+ @Test
+ public void testScheduleLimiting_SelfProxy() {
+ mService.mConstants.ENABLE_API_QUOTAS = true;
+ mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300;
+ mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000;
+ mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false;
+ mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true;
+ mService.updateQuotaTracker();
+
+ final JobInfo job = createJobInfo().setPersisted(true).build();
+ for (int i = 0; i < 500; ++i) {
+ final int expected =
+ i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE;
+ assertEquals("Got unexpected result for schedule #" + (i + 1),
+ expected,
+ mService.scheduleAsPackage(job, null, 10123, job.getService().getPackageName(),
+ 0, ""));
+ }
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
index 5d041b7c5757..3614763fecab 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
@@ -71,7 +71,6 @@ public class TimeControllerTest {
private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
private static final int SOURCE_USER_ID = 0;
- private TimeController.TcConstants mConstants;
private TimeController mTimeController;
private MockitoSession mMockingSession;
@@ -111,7 +110,6 @@ public class TimeControllerTest {
// Initialize real objects.
mTimeController = new TimeController(mJobSchedulerService);
- mConstants = mTimeController.getTcConstants();
spyOn(mTimeController);
}
@@ -530,46 +528,6 @@ public class TimeControllerTest {
}
@Test
- public void testJobDelayWakeupAlarmToggling() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-
- JobStatus job = createJobStatus(
- "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
- createJob().setMinimumLatency(HOUR_IN_MILLIS));
-
- doReturn(true).when(mTimeController)
- .wouldBeReadyWithConstraintLocked(eq(job), anyInt());
-
- // Starting off with using a wakeup alarm.
- mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
- InOrder inOrder = inOrder(mAlarmManager);
-
- mTimeController.maybeStartTrackingJobLocked(job, null);
- inOrder.verify(mAlarmManager, times(1))
- .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
- anyLong(),
- eq(TAG_DELAY), any(), any(), any());
-
- // Use a non wakeup alarm.
- mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
-
- mTimeController.maybeStartTrackingJobLocked(job, null);
- inOrder.verify(mAlarmManager, times(1))
- .set(eq(AlarmManager.ELAPSED_REALTIME), eq(now + HOUR_IN_MILLIS), anyLong(),
- anyLong(), eq(TAG_DELAY),
- any(), any(), any());
-
- // Back off, use a wakeup alarm.
- mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
-
- mTimeController.maybeStartTrackingJobLocked(job, null);
- inOrder.verify(mAlarmManager, times(1))
- .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
- anyLong(),
- eq(TAG_DELAY), any(), any(), any());
- }
-
- @Test
public void testCheckExpiredDeadlinesAndResetAlarm_AllReady() {
doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java
index 6bfe566197af..2026363044fd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
import android.location.Criteria;
import android.location.Location;
+import android.location.util.identity.CallerIdentity;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -68,8 +69,8 @@ public class MockableLocationProviderTest {
true,
true,
Criteria.POWER_LOW,
- Criteria.ACCURACY_FINE)
- ));
+ Criteria.ACCURACY_FINE),
+ CallerIdentity.forTest(0, 1, "testpackage", "test")));
mProvider = new MockableLocationProvider(lock, mListener);
mProvider.setRealProvider(mRealProvider);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
index da794da7f9c9..e947e89c4f94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
@@ -57,7 +57,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
if (!myAppOp.mAllowed) {
return false;
@@ -68,20 +68,20 @@ public class FakeAppOpsHelper extends AppOpsHelper {
}
@Override
- protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+ public void finishOp(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
Preconditions.checkState(myAppOp.mStarted);
myAppOp.mStarted = false;
}
@Override
- protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
return myAppOp.mAllowed;
}
@Override
- protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
if (!noteOpNoThrow(appOp, callerIdentity)) {
throw new SecurityException(
"noteOp not allowed for op " + appOp + " and caller " + callerIdentity);
@@ -91,7 +91,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
if (!myAppOp.mAllowed) {
return false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
new file mode 100644
index 000000000000..e7d7e310d1e4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.location.util.identity.CallerIdentity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Version of LocationPermissionsHelper for testing. All permissions are granted unless notified
+ * otherwise.
+ */
+public class FakeLocationPermissionsHelper extends LocationPermissionsHelper {
+
+ private final HashMap<String, Set<String>> mRevokedPermissions;
+
+ public FakeLocationPermissionsHelper(AppOpsHelper appOps) {
+ super(appOps);
+ mRevokedPermissions = new HashMap<>();
+ }
+
+ public void grantPermission(String packageName, String permission) {
+ getRevokedPermissionsList(packageName).remove(permission);
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ public void revokePermission(String packageName, String permission) {
+ getRevokedPermissionsList(packageName).add(permission);
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ protected boolean hasPermission(String permission, CallerIdentity identity) {
+ return !getRevokedPermissionsList(identity.getPackageName()).contains(permission);
+ }
+
+ private Set<String> getRevokedPermissionsList(String packageName) {
+ return mRevokedPermissions.computeIfAbsent(packageName, p -> new HashSet<>());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
new file mode 100644
index 000000000000..3ead5d4f214d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.os.IPowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+
+/**
+ * Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
+ * change".
+ */
+public class FakeLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper {
+
+ @LocationPowerSaveMode
+ private int mLocationPowerSaveMode;
+
+ public FakeLocationPowerSaveModeHelper() {
+ mLocationPowerSaveMode = IPowerManager.LOCATION_MODE_NO_CHANGE;
+ }
+
+ public void setLocationPowerSaveMode(int locationPowerSaveMode) {
+ if (locationPowerSaveMode == mLocationPowerSaveMode) {
+ return;
+ }
+
+ mLocationPowerSaveMode = locationPowerSaveMode;
+ notifyLocationPowerSaveModeChanged(locationPowerSaveMode);
+ }
+
+ @LocationPowerSaveMode
+ @Override
+ public int getLocationPowerSaveMode() {
+ return mLocationPowerSaveMode;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
new file mode 100644
index 000000000000..df697fa1a03c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+/**
+ * Version of ScreenInteractiveHelper for testing. Screen is initialized as interactive (on).
+ */
+public class FakeScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+ private boolean mIsInteractive;
+
+ public FakeScreenInteractiveHelper() {
+ mIsInteractive = true;
+ }
+
+ public void setScreenInteractive(boolean interactive) {
+ if (interactive == mIsInteractive) {
+ return;
+ }
+
+ mIsInteractive = interactive;
+ notifyScreenInteractiveChanged(interactive);
+ }
+
+ public boolean isInteractive() {
+ return mIsInteractive;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
index 726b1b82b699..1d0523f18008 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
@@ -90,7 +90,7 @@ public class FakeSettingsHelper extends SettingsHelper {
@Override
public boolean isLocationEnabled(int userId) {
- return mLocationEnabledSetting.getValue(Boolean.class);
+ return mLocationEnabledSetting.getValue(userId, Boolean.class);
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
index 336e28c879ba..f5978da416be 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
@@ -16,7 +16,6 @@
package com.android.server.location.util;
-import android.os.Process;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.SparseArray;
@@ -27,19 +26,21 @@ import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
/**
- * Version of UserInfoHelper for testing. The user this code is running under is set as the current
- * user by default, with no profiles.
+ * Version of UserInfoHelper for testing. By default there is one user that starts in a running
+ * state with a userId of 0;
*/
public class FakeUserInfoHelper extends UserInfoHelper {
+ public static final int DEFAULT_USERID = 0;
+
private final IntArray mRunningUserIds;
private final SparseArray<IntArray> mProfiles;
private int mCurrentUserId;
public FakeUserInfoHelper() {
- mCurrentUserId = Process.myUserHandle().getIdentifier();
- mRunningUserIds = IntArray.wrap(new int[]{mCurrentUserId});
+ mCurrentUserId = DEFAULT_USERID;
+ mRunningUserIds = IntArray.wrap(new int[]{DEFAULT_USERID});
mProfiles = new SparseArray<>();
}
@@ -67,6 +68,10 @@ public class FakeUserInfoHelper extends UserInfoHelper {
dispatchOnUserStopped(userId);
}
+ public void setCurrentUserId(int parentUser) {
+ setCurrentUserIds(parentUser, new int[]{parentUser});
+ }
+
public void setCurrentUserIds(int parentUser, int[] currentProfileUserIds) {
Preconditions.checkArgument(ArrayUtils.contains(currentProfileUserIds, parentUser));
int oldUserId = mCurrentUserId;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
index e6f625217965..4165b6ee111f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
@@ -16,7 +16,11 @@
package com.android.server.location.util;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -46,9 +50,7 @@ public class LocationAttributionHelperTest {
public void setUp() {
initMocks(this);
- when(mAppOpsHelper.startLocationMonitoring(any(CallerIdentity.class))).thenReturn(true);
- when(mAppOpsHelper.startHighPowerLocationMonitoring(any(CallerIdentity.class))).thenReturn(
- true);
+ when(mAppOpsHelper.startOpNoThrow(anyInt(), any(CallerIdentity.class))).thenReturn(true);
mHelper = new LocationAttributionHelper(mAppOpsHelper);
}
@@ -63,30 +65,30 @@ public class LocationAttributionHelperTest {
Object key4 = new Object();
mHelper.reportLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
}
@Test
@@ -99,29 +101,29 @@ public class LocationAttributionHelperTest {
Object key4 = new Object();
mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
index 1bcfcbfc7f10..093aa2e0e771 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
@@ -20,12 +20,8 @@ import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_MOCK_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -34,15 +30,15 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
+import static org.testng.Assert.assertThrows;
import android.app.AppOpsManager;
import android.content.Context;
-import android.content.Intent;
-import android.location.LocationManager;
import android.location.util.identity.CallerIdentity;
import android.platform.test.annotations.Presubmit;
@@ -52,7 +48,6 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.util.ArrayList;
@@ -68,7 +63,7 @@ public class SystemAppOpsHelperTest {
@Mock private Context mContext;
@Mock private AppOpsManager mAppOps;
- private List<AppOpsManager.OnOpChangedInternalListener> mListeners = new ArrayList<>();
+ private List<AppOpsManager.OnOpChangedListener> mListeners = new ArrayList<>();
private SystemAppOpsHelper mHelper;
@@ -82,15 +77,15 @@ public class SystemAppOpsHelperTest {
eq(AppOpsManager.OP_COARSE_LOCATION),
isNull(),
eq(AppOpsManager.WATCH_FOREGROUND_CHANGES),
- any(AppOpsManager.OnOpChangedInternalListener.class));
+ any(AppOpsManager.OnOpChangedListener.class));
mHelper = new SystemAppOpsHelper(mContext);
mHelper.onSystemReady();
}
private void sendAppOp(String packageName) {
- for (AppOpsManager.OnOpChangedInternalListener listener : mListeners) {
- listener.onOpChanged(AppOpsManager.OP_COARSE_LOCATION, packageName);
+ for (AppOpsManager.OnOpChangedListener listener : mListeners) {
+ listener.onOpChanged(AppOpsManager.OPSTR_COARSE_LOCATION, packageName);
}
}
@@ -108,41 +103,41 @@ public class SystemAppOpsHelperTest {
}
@Test
- public void testCheckLocationAccess() {
+ public void testCheckOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isTrue();
+ assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isFalse();
+ assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+ assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+ assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
}
@Test
- public void testNoteLocationAccess() {
+ public void testNoteOpNoThrow() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isTrue();
+ assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isFalse();
+ assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
@@ -150,84 +145,55 @@ public class SystemAppOpsHelperTest {
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+ assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+ assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
}
@Test
- public void testStartLocationMonitoring() {
+ public void testStartOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startLocationMonitoring(identity)).isTrue();
+ assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startLocationMonitoring(identity)).isFalse();
+ assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isFalse();
}
@Test
- public void testStartHighPowerLocationMonitoring() {
+ public void testFinishOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
- doReturn(MODE_ALLOWED).when(
- mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
- eq("mypackage"),
- eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue();
-
- ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcast(intentCaptor.capture());
- assertThat(intentCaptor.getValue().getAction()).isEqualTo(
- LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
-
- doReturn(MODE_IGNORED).when(
- mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
- eq("mypackage"),
- eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isFalse();
- }
-
- @Test
- public void testStopLocationMonitoring() {
- CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
- mHelper.stopLocationMonitoring(identity);
+ mHelper.finishOp(OP_MONITOR_LOCATION, identity);
verify(mAppOps).finishOp(OP_MONITOR_LOCATION, 1000, "mypackage", "myfeature");
}
@Test
- public void testStopHighPowerLocationMonitoring() {
- CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
- mHelper.stopHighPowerLocationMonitoring(identity);
- verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature");
-
- ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcast(intentCaptor.capture());
- assertThat(intentCaptor.getValue().getAction()).isEqualTo(
- LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
- }
-
- @Test
- public void testNoteMockLocationAccess() {
+ public void testNoteOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
nullable(String.class));
- assertThat(mHelper.noteMockLocationAccess(identity)).isTrue();
+ assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
nullable(String.class));
- assertThat(mHelper.noteMockLocationAccess(identity)).isFalse();
+ assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isFalse();
+
+
+ doThrow(new SecurityException()).when(
+ mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
+ nullable(String.class));
+ assertThrows(SecurityException.class, () -> mHelper.noteOp(OP_MOCK_LOCATION, identity));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
new file mode 100644
index 000000000000..2acb70c4b83d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.location.util;
+
+import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
+import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.location.util.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemLocationPowerSaveModeHelperTest {
+
+ private static final long TIMEOUT_MS = 5000;
+ private static final long FAILURE_TIMEOUT_MS = 200;
+
+ @Mock
+ private PowerManagerInternal mPowerManagerInternal;
+
+ private List<Consumer<PowerSaveState>> mListeners = new ArrayList<>();
+
+ private SystemLocationPowerSaveModeHelper mHelper;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+
+ LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternal);
+
+ doAnswer(invocation -> mListeners.add(invocation.getArgument(1))).when(
+ mPowerManagerInternal).registerLowPowerModeObserver(anyInt(), any(Consumer.class));
+
+ PowerManager powerManager = mock(PowerManager.class);
+ doReturn(LOCATION_MODE_NO_CHANGE).when(powerManager).getLocationPowerSaveMode();
+ Context context = mock(Context.class);
+ doReturn(powerManager).when(context).getSystemService(PowerManager.class);
+
+ mHelper = new SystemLocationPowerSaveModeHelper(context);
+ mHelper.onSystemReady();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ }
+
+ private void sendPowerSaveState(PowerSaveState powerSaveState) {
+ for (Consumer<PowerSaveState> listener : mListeners) {
+ listener.accept(powerSaveState);
+ }
+ }
+
+ @Test
+ public void testListener() {
+ LocationPowerSaveModeChangedListener listener = mock(
+ LocationPowerSaveModeChangedListener.class);
+ mHelper.addListener(listener);
+
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+ false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_FOREGROUND_ONLY);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_FOREGROUND_ONLY);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+ true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_NO_CHANGE);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
index c22dc104f438..1867be0b9f3b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
@@ -16,22 +16,32 @@
package com.android.server.location.util;
+import com.android.server.location.LocationRequestStatistics;
+
public class TestInjector implements Injector {
private final FakeUserInfoHelper mUserInfoHelper;
private final FakeAppOpsHelper mAppOpsHelper;
+ private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
private final FakeSettingsHelper mSettingsHelper;
private final FakeAppForegroundHelper mAppForegroundHelper;
- private final LocationUsageLogger mLocationUsageLogger;
+ private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+ private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
+ private final LocationUsageLogger mLocationUsageLogger;
+ private final LocationRequestStatistics mLocationRequestStatistics;
public TestInjector() {
mUserInfoHelper = new FakeUserInfoHelper();
mAppOpsHelper = new FakeAppOpsHelper();
+ mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
mSettingsHelper = new FakeSettingsHelper();
mAppForegroundHelper = new FakeAppForegroundHelper();
- mLocationUsageLogger = new LocationUsageLogger();
+ mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper();
+ mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+ mLocationUsageLogger = new LocationUsageLogger();
+ mLocationRequestStatistics = new LocationRequestStatistics();
}
@Override
@@ -45,6 +55,11 @@ public class TestInjector implements Injector {
}
@Override
+ public FakeLocationPermissionsHelper getLocationPermissionsHelper() {
+ return mLocationPermissionsHelper;
+ }
+
+ @Override
public FakeSettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@@ -55,12 +70,27 @@ public class TestInjector implements Injector {
}
@Override
- public LocationUsageLogger getLocationUsageLogger() {
- return mLocationUsageLogger;
+ public FakeLocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+ return mLocationPowerSaveModeHelper;
+ }
+
+ @Override
+ public FakeScreenInteractiveHelper getScreenInteractiveHelper() {
+ return mScreenInteractiveHelper;
}
@Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
+
+ @Override
+ public LocationUsageLogger getLocationUsageLogger() {
+ return mLocationUsageLogger;
+ }
+
+ @Override
+ public LocationRequestStatistics getLocationRequestStatistics() {
+ return mLocationRequestStatistics;
+ }
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ac2ec58fae2a..7fc6bbd70000 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -59,6 +59,7 @@ android_test {
libs: [
"android.hardware.power-java",
"android.hardware.tv.cec-V1.0-java",
+ "android.hardware.vibrator-java",
"android.hidl.manager-V1.0-java",
"android.test.mock",
"android.test.base",
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 6915220d3fb7..90e1cfcd305a 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -81,6 +81,9 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
<uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE"/>
+ <uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 4fbc587c8a87..6f37ff5ef329 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -153,6 +153,20 @@ public class GestureLauncherServiceTest {
}
@Test
+ public void testIsPanicButtonGestureEnabled_settingDisabled() {
+ withPanicGestureEnabledSettingValue(false);
+ assertFalse(mGestureLauncherService.isPanicButtonGestureEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ public void testIsPanicButtonGestureEnabled_settingEnabled() {
+ withPanicGestureEnabledSettingValue(true);
+ assertTrue(mGestureLauncherService.isPanicButtonGestureEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
public void testHandleCameraLaunchGesture_userSetupComplete() {
withUserSetupCompleteValue(true);
@@ -882,6 +896,14 @@ public class GestureLauncherServiceTest {
UserHandle.USER_CURRENT);
}
+ private void withPanicGestureEnabledSettingValue(boolean enable) {
+ Settings.Secure.putIntForUser(
+ mContentResolver,
+ Settings.Secure.PANIC_GESTURE_ENABLED,
+ enable ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ }
+
private void withUserSetupCompleteValue(boolean userSetupComplete) {
int userSetupCompleteValue = userSetupComplete ? 1 : 0;
Settings.Secure.putIntForUser(
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
new file mode 100644
index 000000000000..c6922536f61a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.intThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManagerInternal;
+import android.hardware.vibrator.IVibrator;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IVibratorStateListener;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+
+/**
+ * Tests for {@link VibratorService}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:VibratorServiceTest
+ */
+@Presubmit
+public class VibratorServiceTest {
+
+ private static final int UID = Process.ROOT_UID;
+ private static final String PACKAGE_NAME = "package";
+ private static final VibrationAttributes ALARM_ATTRS =
+ new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
+ private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_TOUCH).build();
+ private static final VibrationAttributes NOTIFICATION_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_NOTIFICATION).build();
+ private static final VibrationAttributes RINGTONE_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_RINGTONE).build();
+
+ @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock private PackageManagerInternal mPackageManagerInternalMock;
+ @Mock private PowerManagerInternal mPowerManagerInternalMock;
+ @Mock private PowerSaveState mPowerSaveStateMock;
+ @Mock private Vibrator mVibratorMock;
+ @Mock private VibratorService.NativeWrapper mNativeWrapperMock;
+ @Mock private IVibratorStateListener mVibratorStateListenerMock;
+ @Mock private IBinder mVibratorStateListenerBinderMock;
+
+ private TestLooper mTestLooper;
+ private ContextWrapper mContextSpy;
+
+ @Before
+ public void setUp() throws Exception {
+ mTestLooper = new TestLooper();
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+
+ MockContentResolver contentResolver = new MockContentResolver(mContextSpy);
+ contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+
+ when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+ when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);
+ when(mVibratorMock.getDefaultHapticFeedbackIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ when(mVibratorMock.getDefaultNotificationVibrationIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ when(mVibratorMock.getDefaultRingVibrationIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock);
+ when(mPackageManagerInternalMock.getSystemUiServiceComponent())
+ .thenReturn(new ComponentName("", ""));
+ when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION))
+ .thenReturn(mPowerSaveStateMock);
+
+ addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock);
+ addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ private VibratorService createService() {
+ return new VibratorService(mContextSpy,
+ new VibratorService.Injector() {
+ @Override
+ VibratorService.NativeWrapper getNativeWrapper() {
+ return mNativeWrapperMock;
+ }
+
+ @Override
+ Handler createHandler(Looper looper) {
+ return new Handler(mTestLooper.getLooper());
+ }
+
+ @Override
+ void addService(String name, IBinder service) {
+ // ignore
+ }
+ });
+ }
+
+ @Test
+ public void createService_initializesNativeService() {
+ createService();
+ verify(mNativeWrapperMock).vibratorInit();
+ verify(mNativeWrapperMock).vibratorOff();
+ }
+
+ @Test
+ public void hasVibrator_withVibratorHalPresent_returnsTrue() {
+ when(mNativeWrapperMock.vibratorExists()).thenReturn(true);
+ assertTrue(createService().hasVibrator());
+ }
+
+ @Test
+ public void hasVibrator_withNoVibratorHalPresent_returnsFalse() {
+ when(mNativeWrapperMock.vibratorExists()).thenReturn(false);
+ assertFalse(createService().hasVibrator());
+ }
+
+ @Test
+ public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() {
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+ assertTrue(createService().hasAmplitudeControl());
+ }
+
+ @Test
+ public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() {
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(false);
+ assertFalse(createService().hasAmplitudeControl());
+ }
+
+ @Test
+ public void areEffectsSupported_withNullResultFromNative_returnsSupportUnknown() {
+ when(mNativeWrapperMock.vibratorGetSupportedEffects()).thenReturn(null);
+ assertArrayEquals(new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN},
+ createService().areEffectsSupported(new int[]{VibrationEffect.EFFECT_CLICK}));
+ }
+
+ @Test
+ public void areEffectsSupported_withSomeEffectsSupported_returnsSupportYesAndNoForEffects() {
+ int[] effects = new int[]{VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK};
+
+ when(mNativeWrapperMock.vibratorGetSupportedEffects())
+ .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK});
+ assertArrayEquals(
+ new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
+ Vibrator.VIBRATION_EFFECT_SUPPORT_NO},
+ createService().areEffectsSupported(effects));
+ }
+
+ @Test
+ public void arePrimitivesSupported_withoutComposeCapability_returnsAlwaysFalse() {
+ assertArrayEquals(new boolean[]{false, false},
+ createService().arePrimitivesSupported(new int[]{
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_TICK
+ }));
+ }
+
+ @Test
+ public void arePrimitivesSupported_withComposeCapability_returnsAlwaysTrue() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ assertArrayEquals(new boolean[]{true, true},
+ createService().arePrimitivesSupported(new int[]{
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE
+ }));
+ }
+
+ @Test
+ public void setAlwaysOnEffect_withCapabilityAndValidEffect_enablesAlwaysOnEffect() {
+ mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
+
+ assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS));
+ verify(mNativeWrapperMock).vibratorAlwaysOnEnable(
+ eq(1L), eq((long) VibrationEffect.EFFECT_CLICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG));
+ }
+
+ @Test
+ public void setAlwaysOnEffect_withNonPrebakedEffect_ignoresEffect() {
+ mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
+
+ assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1,
+ VibrationEffect.createOneShot(100, 255), ALARM_ATTRS));
+ verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong());
+ verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong());
+ }
+
+ @Test
+ public void setAlwaysOnEffect_withNullEffect_disablesAlwaysOnEffect() {
+ mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
+
+ assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, null, ALARM_ATTRS));
+ verify(mNativeWrapperMock).vibratorAlwaysOnDisable(eq(1L));
+ }
+
+ @Test
+ public void setAlwaysOnEffect_withoutCapability_ignoresEffect() {
+ assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1,
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS));
+ verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong());
+ verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong());
+ }
+
+ @Test
+ public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() {
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ vibrate(service, VibrationEffect.createOneShot(100, 128));
+ assertTrue(service.isVibrating());
+
+ verify(mNativeWrapperMock).vibratorOff();
+ verify(mNativeWrapperMock).vibratorOn(eq(100L));
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128));
+ }
+
+ @Test
+ public void vibrate_withOneShotAndNoAmplitudeControl_turnsVibratorOnAndIgnoresAmplitude() {
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ vibrate(service, VibrationEffect.createOneShot(100, 128));
+ assertTrue(service.isVibrating());
+
+ verify(mNativeWrapperMock).vibratorOff();
+ verify(mNativeWrapperMock).vibratorOn(eq(100L));
+ verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt());
+ }
+
+ @Test
+ public void vibrate_withPrebaked_performsEffect() {
+ when(mNativeWrapperMock.vibratorGetSupportedEffects())
+ .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK});
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+
+ verify(mNativeWrapperMock).vibratorOff();
+ verify(mNativeWrapperMock).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_CLICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG),
+ any(VibratorService.Vibration.class), eq(false));
+ }
+
+ @Test
+ public void vibrate_withComposed_performsEffect() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
+ .compose();
+ vibrate(service, effect);
+
+ ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor =
+ ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class);
+
+ verify(mNativeWrapperMock).vibratorOff();
+ verify(mNativeWrapperMock).vibratorPerformComposedEffect(
+ primitivesCaptor.capture(), any(VibratorService.Vibration.class));
+
+ // Check all primitive effect fields are passed down to the HAL.
+ assertEquals(1, primitivesCaptor.getValue().length);
+ VibrationEffect.Composition.PrimitiveEffect primitive = primitivesCaptor.getValue()[0];
+ assertEquals(VibrationEffect.Composition.PRIMITIVE_CLICK, primitive.id);
+ assertEquals(0.5f, primitive.scale, /* delta= */ 1e-2);
+ assertEquals(10, primitive.delay);
+ }
+
+ @Test
+ public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime()
+ throws Exception {
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ new long[] { 10, 10, 10 }, new int[] { 100, 200, 50 }, -1);
+ vibrate(service, effect);
+
+ verify(mNativeWrapperMock).vibratorOff();
+
+ Thread.sleep(5);
+ verify(mNativeWrapperMock).vibratorOn(eq(30L));
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
+
+ Thread.sleep(10);
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200));
+
+ Thread.sleep(10);
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50));
+ }
+
+ @Test
+ public void vibrate_withCallback_finishesVibrationWhenCallbackTriggered() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ doAnswer(invocation -> {
+ ((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
+ return null;
+ }).when(mNativeWrapperMock).vibratorPerformComposedEffect(
+ any(), any(VibratorService.Vibration.class));
+
+ // Use vibration with delay so there is time for the callback to be triggered.
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
+ .compose();
+ vibrate(service, effect);
+
+ // Vibration canceled once before perform and once by native callback.
+ verify(mNativeWrapperMock, times(2)).vibratorOff();
+ verify(mNativeWrapperMock).vibratorPerformComposedEffect(
+ any(VibrationEffect.Composition.PrimitiveEffect[].class),
+ any(VibratorService.Vibration.class));
+ }
+
+ @Test
+ public void vibrate_whenBinderDies_cancelsVibration() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ doAnswer(invocation -> {
+ ((VibratorService.Vibration) invocation.getArgument(1)).binderDied();
+ return null;
+ }).when(mNativeWrapperMock).vibratorPerformComposedEffect(
+ any(), any(VibratorService.Vibration.class));
+
+ // Use vibration with delay so there is time for the callback to be triggered.
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
+ .compose();
+ vibrate(service, effect);
+
+ // Vibration canceled once before perform and once by native binder death.
+ verify(mNativeWrapperMock, times(2)).vibratorOff();
+ verify(mNativeWrapperMock).vibratorPerformComposedEffect(
+ any(VibrationEffect.Composition.PrimitiveEffect[].class),
+ any(VibratorService.Vibration.class));
+ }
+
+ @Test
+ public void cancelVibrate_withDeviceVibrating_callsVibratorOff() {
+ VibratorService service = createService();
+ vibrate(service, VibrationEffect.createOneShot(100, 128));
+ assertTrue(service.isVibrating());
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ service.cancelVibrate(service);
+ assertFalse(service.isVibrating());
+ verify(mNativeWrapperMock).vibratorOff();
+ }
+
+ @Test
+ public void cancelVibrate_withDeviceNotVibrating_ignoresCall() {
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ service.cancelVibrate(service);
+ assertFalse(service.isVibrating());
+ verify(mNativeWrapperMock, never()).vibratorOff();
+ }
+
+ @Test
+ public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
+ VibratorService service = createService();
+
+ service.registerVibratorStateListener(mVibratorStateListenerMock);
+ verify(mVibratorStateListenerMock).onVibrating(false);
+
+ vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE));
+ verify(mVibratorStateListenerMock).onVibrating(true);
+
+ // Run the scheduled callback to finish one-shot vibration.
+ mTestLooper.moveTimeForward(10);
+ mTestLooper.dispatchAll();
+ verify(mVibratorStateListenerMock, times(2)).onVibrating(false);
+ }
+
+ @Test
+ public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
+ VibratorService service = createService();
+
+ service.registerVibratorStateListener(mVibratorStateListenerMock);
+ verify(mVibratorStateListenerMock).onVibrating(false);
+
+ vibrate(service, VibrationEffect.createOneShot(5, VibrationEffect.DEFAULT_AMPLITUDE));
+ verify(mVibratorStateListenerMock).onVibrating(true);
+
+ service.unregisterVibratorStateListener(mVibratorStateListenerMock);
+ Mockito.clearInvocations(mVibratorStateListenerMock);
+
+ vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE));
+ verifyNoMoreInteractions(mVibratorStateListenerMock);
+ }
+
+ @Test
+ public void scale_withPrebaked_userIntensitySettingAsEffectStrength() {
+ // Alarm vibration is always VIBRATION_INTENSITY_HIGH.
+ setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+ VibratorService service = createService();
+ service.systemReady();
+
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
+ ALARM_ATTRS);
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK),
+ NOTIFICATION_ATTRS);
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK),
+ HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK),
+ RINGTONE_ATTRS);
+
+ verify(mNativeWrapperMock).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_CLICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(), anyBoolean());
+ verify(mNativeWrapperMock).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_TICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any(), anyBoolean());
+ verify(mNativeWrapperMock).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any(), anyBoolean());
+ verify(mNativeWrapperMock, never()).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any(), anyBoolean());
+ }
+
+ @Test
+ public void scale_withOneShotAndWaveform_usesScaleLevelOnAmplitude() throws Exception {
+ when(mVibratorMock.getDefaultNotificationVibrationIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_HIGH);
+ setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+ VibratorService service = createService();
+ service.systemReady();
+
+ vibrate(service, VibrationEffect.createOneShot(20, 100), ALARM_ATTRS);
+ vibrate(service, VibrationEffect.createOneShot(20, 100), NOTIFICATION_ATTRS);
+ vibrate(service, VibrationEffect.createOneShot(20, 255), RINGTONE_ATTRS);
+ vibrate(service, VibrationEffect.createWaveform(new long[] { 10 }, new int[] { 100 }, -1),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ // Waveform effect runs on a separate thread.
+ Thread.sleep(5);
+
+ // Alarm vibration is never scaled.
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
+ // Notification vibrations will be scaled with SCALE_VERY_HIGH.
+ verify(mNativeWrapperMock).vibratorSetAmplitude(intThat(amplitude -> amplitude > 150));
+ // Haptic feedback vibrations will be scaled with SCALE_LOW.
+ verify(mNativeWrapperMock).vibratorSetAmplitude(
+ intThat(amplitude -> amplitude < 100 && amplitude > 50));
+ // Ringtone vibration is off.
+ verify(mNativeWrapperMock, never()).vibratorSetAmplitude(eq(255));
+ }
+
+ @Test
+ public void scale_withComposed_usesScaleLevelOnPrimitiveScaleValues() {
+ when(mVibratorMock.getDefaultNotificationVibrationIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_HIGH);
+ setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorService service = createService();
+ service.systemReady();
+
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .compose();
+ ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor =
+ ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class);
+
+ vibrate(service, effect, ALARM_ATTRS);
+ vibrate(service, effect, NOTIFICATION_ATTRS);
+ vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, effect, RINGTONE_ATTRS);
+
+ // Ringtone vibration is off, so only the other 3 are propagated to native.
+ verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect(
+ primitivesCaptor.capture(), any());
+
+ List<VibrationEffect.Composition.PrimitiveEffect[]> values =
+ primitivesCaptor.getAllValues();
+
+ // Alarm vibration is never scaled.
+ assertEquals(1f, values.get(0)[0].scale, /* delta= */ 1e-2);
+ assertEquals(0.5f, values.get(0)[1].scale, /* delta= */ 1e-2);
+
+ // Notification vibrations will be scaled with SCALE_VERY_HIGH.
+ assertEquals(1f, values.get(1)[0].scale, /* delta= */ 1e-2);
+ assertTrue(0.7 < values.get(1)[1].scale);
+
+ // Haptic feedback vibrations will be scaled with SCALE_LOW.
+ assertTrue(0.5 < values.get(2)[0].scale);
+ assertTrue(0.5 > values.get(2)[1].scale);
+ }
+
+ private void vibrate(VibratorService service, VibrationEffect effect) {
+ vibrate(service, effect, ALARM_ATTRS);
+ }
+
+ private void vibrate(VibratorService service, VibrationEffect effect,
+ VibrationAttributes attributes) {
+ service.vibrate(UID, PACKAGE_NAME, effect, attributes, "some reason", service);
+ }
+
+ private void mockVibratorCapabilities(int capabilities) {
+ when(mNativeWrapperMock.vibratorGetCapabilities()).thenReturn((long) capabilities);
+ }
+
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ private void setVibrationIntensityUserSetting(String settingName, int value) {
+ Settings.System.putIntForUser(
+ mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 0445bff8fd0d..cfa2086793a4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -51,6 +51,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -90,6 +91,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
@@ -166,7 +168,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Mock private IAccessibilityInteractionConnection mMockIA11yInteractionConnection;
@Mock private IAccessibilityInteractionConnectionCallback mMockCallback;
@Mock private FingerprintGestureDispatcher mMockFingerprintGestureDispatcher;
- @Mock private MagnificationController mMockMagnificationController;
+ @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock private RemoteCallback.OnResultListener mMockListener;
@Before
@@ -177,8 +179,8 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher);
when(mMockSystemSupport.getFingerprintGestureDispatcher())
.thenReturn(mMockFingerprintGestureDispatcher);
- when(mMockSystemSupport.getMagnificationController())
- .thenReturn(mMockMagnificationController);
+ when(mMockSystemSupport.getFullScreenMagnificationController())
+ .thenReturn(mMockFullScreenMagnificationController);
PowerManager powerManager =
new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler);
@@ -271,6 +273,25 @@ public class AbstractAccessibilityServiceConnectionTest {
}
@Test
+ public void setServiceInfo_ChangePackageNames_updateSuccess() {
+ assertTrue(mServiceConnection.mPackageNames.isEmpty());
+
+ final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
+ updateServiceInfo(serviceInfo, 0, 0, A11Y_SERVICE_FLAG,
+ new String[] {PACKAGE_NAME1, PACKAGE_NAME2},
+ 1000);
+
+ mServiceConnection.setServiceInfo(serviceInfo);
+ assertEquals(serviceInfo.packageNames.length, mServiceConnection.mPackageNames.size());
+ assertTrue(mServiceConnection.mPackageNames.containsAll(
+ Arrays.asList(mServiceConnection.getServiceInfo().packageNames)));
+
+ updateServiceInfo(serviceInfo, 0, 0, A11Y_SERVICE_FLAG, null, 1000);
+ mServiceConnection.setServiceInfo(serviceInfo);
+ assertTrue(mServiceConnection.mPackageNames.isEmpty());
+ }
+
+ @Test
public void canReceiveEvents_hasEventType_returnTrue() {
final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
updateServiceInfo(serviceInfo,
@@ -533,7 +554,7 @@ public class AbstractAccessibilityServiceConnectionTest {
public void getMagnificationScale() {
final int displayId = 1;
final float scale = 2.0f;
- when(mMockMagnificationController.getScale(displayId)).thenReturn(scale);
+ when(mMockFullScreenMagnificationController.getScale(displayId)).thenReturn(scale);
final float result = mServiceConnection.getMagnificationScale(displayId);
assertThat(result, is(scale));
@@ -543,7 +564,7 @@ public class AbstractAccessibilityServiceConnectionTest {
public void getMagnificationScale_serviceNotBelongCurrentUser_returnNoScale() {
final int displayId = 1;
final float scale = 2.0f;
- when(mMockMagnificationController.getScale(displayId)).thenReturn(scale);
+ when(mMockFullScreenMagnificationController.getScale(displayId)).thenReturn(scale);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final float result = mServiceConnection.getMagnificationScale(displayId);
@@ -557,13 +578,14 @@ public class AbstractAccessibilityServiceConnectionTest {
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
- }).when(mMockMagnificationController).getMagnificationRegion(eq(displayId), any());
- when(mMockMagnificationController.isRegistered(displayId)).thenReturn(false);
+ }).when(mMockFullScreenMagnificationController).getMagnificationRegion(eq(displayId),
+ any());
+ when(mMockFullScreenMagnificationController.isRegistered(displayId)).thenReturn(false);
final Region result = mServiceConnection.getMagnificationRegion(displayId);
assertThat(result, is(region));
- verify(mMockMagnificationController).register(displayId);
- verify(mMockMagnificationController).unregister(displayId);
+ verify(mMockFullScreenMagnificationController).register(displayId);
+ verify(mMockFullScreenMagnificationController).unregister(displayId);
}
@Test
@@ -573,7 +595,8 @@ public class AbstractAccessibilityServiceConnectionTest {
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
- }).when(mMockMagnificationController).getMagnificationRegion(eq(displayId), any());
+ }).when(mMockFullScreenMagnificationController).getMagnificationRegion(eq(displayId),
+ any());
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final Region result = mServiceConnection.getMagnificationRegion(displayId);
@@ -584,20 +607,20 @@ public class AbstractAccessibilityServiceConnectionTest {
public void getMagnificationCenterX_notRegistered_shouldRegisterThenUnregister() {
final int displayId = 1;
final float centerX = 480.0f;
- when(mMockMagnificationController.getCenterX(displayId)).thenReturn(centerX);
- when(mMockMagnificationController.isRegistered(displayId)).thenReturn(false);
+ when(mMockFullScreenMagnificationController.getCenterX(displayId)).thenReturn(centerX);
+ when(mMockFullScreenMagnificationController.isRegistered(displayId)).thenReturn(false);
final float result = mServiceConnection.getMagnificationCenterX(displayId);
assertThat(result, is(centerX));
- verify(mMockMagnificationController).register(displayId);
- verify(mMockMagnificationController).unregister(displayId);
+ verify(mMockFullScreenMagnificationController).register(displayId);
+ verify(mMockFullScreenMagnificationController).unregister(displayId);
}
@Test
public void getMagnificationCenterX_serviceNotBelongCurrentUser_returnZero() {
final int displayId = 1;
final float centerX = 480.0f;
- when(mMockMagnificationController.getCenterX(displayId)).thenReturn(centerX);
+ when(mMockFullScreenMagnificationController.getCenterX(displayId)).thenReturn(centerX);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final float result = mServiceConnection.getMagnificationCenterX(displayId);
@@ -608,20 +631,20 @@ public class AbstractAccessibilityServiceConnectionTest {
public void getMagnificationCenterY_notRegistered_shouldRegisterThenUnregister() {
final int displayId = 1;
final float centerY = 640.0f;
- when(mMockMagnificationController.getCenterY(displayId)).thenReturn(centerY);
- when(mMockMagnificationController.isRegistered(displayId)).thenReturn(false);
+ when(mMockFullScreenMagnificationController.getCenterY(displayId)).thenReturn(centerY);
+ when(mMockFullScreenMagnificationController.isRegistered(displayId)).thenReturn(false);
final float result = mServiceConnection.getMagnificationCenterY(displayId);
assertThat(result, is(centerY));
- verify(mMockMagnificationController).register(displayId);
- verify(mMockMagnificationController).unregister(displayId);
+ verify(mMockFullScreenMagnificationController).register(displayId);
+ verify(mMockFullScreenMagnificationController).unregister(displayId);
}
@Test
public void getMagnificationCenterY_serviceNotBelongCurrentUser_returnZero() {
final int displayId = 1;
final float centerY = 640.0f;
- when(mMockMagnificationController.getCenterY(displayId)).thenReturn(centerY);
+ when(mMockFullScreenMagnificationController.getCenterY(displayId)).thenReturn(centerY);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final float result = mServiceConnection.getMagnificationCenterY(displayId);
@@ -631,7 +654,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void resetMagnification() {
final int displayId = 1;
- when(mMockMagnificationController.reset(displayId, true)).thenReturn(true);
+ when(mMockFullScreenMagnificationController.reset(displayId, true)).thenReturn(true);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
assertThat(result, is(true));
@@ -640,7 +663,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void resetMagnification_cantControlMagnification_returnFalse() {
final int displayId = 1;
- when(mMockMagnificationController.reset(displayId, true)).thenReturn(true);
+ when(mMockFullScreenMagnificationController.reset(displayId, true)).thenReturn(true);
when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
@@ -650,7 +673,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void resetMagnification_serviceNotBelongCurrentUser_returnFalse() {
final int displayId = 1;
- when(mMockMagnificationController.reset(displayId, true)).thenReturn(true);
+ when(mMockFullScreenMagnificationController.reset(displayId, true)).thenReturn(true);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
@@ -663,14 +686,14 @@ public class AbstractAccessibilityServiceConnectionTest {
final float scale = 1.8f;
final float centerX = 50.5f;
final float centerY = 100.5f;
- when(mMockMagnificationController.setScaleAndCenter(displayId,
+ when(mMockFullScreenMagnificationController.setScaleAndCenter(displayId,
scale, centerX, centerY, true, SERVICE_ID)).thenReturn(true);
- when(mMockMagnificationController.isRegistered(displayId)).thenReturn(false);
+ when(mMockFullScreenMagnificationController.isRegistered(displayId)).thenReturn(false);
final boolean result = mServiceConnection.setMagnificationScaleAndCenter(
displayId, scale, centerX, centerY, true);
assertThat(result, is(true));
- verify(mMockMagnificationController).register(displayId);
+ verify(mMockFullScreenMagnificationController).register(displayId);
}
@Test
@@ -679,7 +702,7 @@ public class AbstractAccessibilityServiceConnectionTest {
final float scale = 1.8f;
final float centerX = 50.5f;
final float centerY = 100.5f;
- when(mMockMagnificationController.setScaleAndCenter(displayId,
+ when(mMockFullScreenMagnificationController.setScaleAndCenter(displayId,
scale, centerX, centerY, true, SERVICE_ID)).thenReturn(true);
when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
@@ -694,7 +717,7 @@ public class AbstractAccessibilityServiceConnectionTest {
final float scale = 1.8f;
final float centerX = 50.5f;
final float centerY = 100.5f;
- when(mMockMagnificationController.setScaleAndCenter(displayId,
+ when(mMockFullScreenMagnificationController.setScaleAndCenter(displayId,
scale, centerX, centerY, true, SERVICE_ID)).thenReturn(true);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
index e16693535c9f..4b2a9fcd10d2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -52,6 +52,8 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.gestures.TouchExplorer;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
@@ -89,7 +91,7 @@ public class AccessibilityInputFilterTest {
FullScreenMagnificationGestureHandler.class, TouchExplorer.class,
AutoclickController.class, AccessibilityInputFilter.class};
- private MagnificationController mMockMagnificationController;
+ private FullScreenMagnificationController mMockFullScreenMagnificationController;
private AccessibilityManagerService mAms;
private AccessibilityInputFilter mA11yInputFilter;
private EventCaptor mCaptor1;
@@ -135,12 +137,13 @@ public class AccessibilityInputFilterTest {
setDisplayCount(1);
mAms = spy(new AccessibilityManagerService(context));
- mMockMagnificationController = mock(MagnificationController.class);
+ mMockFullScreenMagnificationController = mock(FullScreenMagnificationController.class);
mA11yInputFilter = new AccessibilityInputFilter(context, mAms, mEventHandler);
mA11yInputFilter.onInstalled();
when(mAms.getValidDisplayList()).thenReturn(mDisplayList);
- when(mAms.getMagnificationController()).thenReturn(mMockMagnificationController);
+ when(mAms.getFullScreenMagnificationController()).thenReturn(
+ mMockFullScreenMagnificationController);
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index b6cf2785d771..27edfd4ef1d2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -44,6 +44,7 @@ import android.os.UserHandle;
import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -87,7 +88,8 @@ public class AccessibilityServiceConnectionTest {
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock SystemActionPerformer mMockSystemActionPerformer;
@Mock KeyEventDispatcher mMockKeyEventDispatcher;
- @Mock MagnificationController mMockMagnificationController;
+ @Mock
+ FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock IBinder mMockIBinder;
@Mock IAccessibilityServiceClient mMockServiceClient;
@Mock MotionEventInjector mMockMotionEventInjector;
@@ -98,8 +100,8 @@ public class AccessibilityServiceConnectionTest {
public void setup() {
MockitoAnnotations.initMocks(this);
when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher);
- when(mMockSystemSupport.getMagnificationController())
- .thenReturn(mMockMagnificationController);
+ when(mMockSystemSupport.getFullScreenMagnificationController())
+ .thenReturn(mMockFullScreenMagnificationController);
when(mMockSystemSupport.getMotionEventInjectorForDisplayLocked(
Display.DEFAULT_DISPLAY)).thenReturn(mMockMotionEventInjector);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 82c6498bd9be..a9f2e4a50ded 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package com.android.server.accessibility.magnification;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -47,6 +47,7 @@ import android.view.MagnificationSpec;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
@@ -66,7 +67,7 @@ import org.mockito.stubbing.Answer;
import java.util.Locale;
@RunWith(AndroidJUnit4.class)
-public class MagnificationControllerTest {
+public class FullScreenMagnificationControllerTest {
static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 100, 200);
static final PointF INITIAL_MAGNIFICATION_BOUNDS_CENTER = new PointF(
INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY());
@@ -85,8 +86,8 @@ public class MagnificationControllerTest {
static final int DISPLAY_COUNT = 2;
static final int INVALID_DISPLAY = 2;
- final MagnificationController.ControllerContext mMockControllerCtx =
- mock(MagnificationController.ControllerContext.class);
+ final FullScreenMagnificationController.ControllerContext mMockControllerCtx =
+ mock(FullScreenMagnificationController.ControllerContext.class);
final Context mMockContext = mock(Context.class);
final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
@@ -95,7 +96,7 @@ public class MagnificationControllerTest {
ValueAnimator mMockValueAnimator;
ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
- MagnificationController mMagnificationController;
+ FullScreenMagnificationController mFullScreenMagnificationController;
@Before
public void setUp() {
@@ -109,7 +110,8 @@ public class MagnificationControllerTest {
when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
initMockWindowManager();
- mMagnificationController = new MagnificationController(mMockControllerCtx, new Object());
+ mFullScreenMagnificationController = new FullScreenMagnificationController(
+ mMockControllerCtx, new Object());
}
@After
@@ -131,23 +133,23 @@ public class MagnificationControllerTest {
eq(DISPLAY_1), (MagnificationCallbacks) anyObject());
verify(mMockWindowManager).setMagnificationCallbacks(
eq(INVALID_DISPLAY), (MagnificationCallbacks) anyObject());
- assertTrue(mMagnificationController.isRegistered(DISPLAY_0));
- assertTrue(mMagnificationController.isRegistered(DISPLAY_1));
- assertFalse(mMagnificationController.isRegistered(INVALID_DISPLAY));
+ assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
+ assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_1));
+ assertFalse(mFullScreenMagnificationController.isRegistered(INVALID_DISPLAY));
}
@Test
public void testRegister_WindowManagerAndContextUnregisterListeners() {
register(DISPLAY_0);
register(DISPLAY_1);
- mMagnificationController.unregister(DISPLAY_0);
+ mFullScreenMagnificationController.unregister(DISPLAY_0);
verify(mMockContext, times(0)).unregisterReceiver((BroadcastReceiver) anyObject());
- mMagnificationController.unregister(DISPLAY_1);
+ mFullScreenMagnificationController.unregister(DISPLAY_1);
verify(mMockContext).unregisterReceiver((BroadcastReceiver) anyObject());
verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_0), eq(null));
verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_1), eq(null));
- assertFalse(mMagnificationController.isRegistered(DISPLAY_0));
- assertFalse(mMagnificationController.isRegistered(DISPLAY_1));
+ assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
+ assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_1));
}
@Test
@@ -166,14 +168,14 @@ public class MagnificationControllerTest {
Rect initialBounds = new Rect();
assertEquals(expectedInitialSpec, getCurrentMagnificationSpec(displayId));
- mMagnificationController.getMagnificationRegion(displayId, initialMagRegion);
- mMagnificationController.getMagnificationBounds(displayId, initialBounds);
+ mFullScreenMagnificationController.getMagnificationRegion(displayId, initialMagRegion);
+ mFullScreenMagnificationController.getMagnificationBounds(displayId, initialBounds);
assertEquals(INITIAL_MAGNIFICATION_REGION, initialMagRegion);
assertEquals(INITIAL_MAGNIFICATION_BOUNDS, initialBounds);
assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerX(),
- mMagnificationController.getCenterX(displayId), 0.0f);
+ mFullScreenMagnificationController.getCenterX(displayId), 0.0f);
assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerY(),
- mMagnificationController.getCenterY(displayId), 0.0f);
+ mFullScreenMagnificationController.getCenterY(displayId), 0.0f);
}
@Test
@@ -185,24 +187,26 @@ public class MagnificationControllerTest {
}
private void notRegistered_publicMethodsShouldBeBenign(int displayId) {
- assertFalse(mMagnificationController.isMagnifying(displayId));
- assertFalse(mMagnificationController.magnificationRegionContains(displayId, 100, 100));
- assertFalse(mMagnificationController.reset(displayId, true));
- assertFalse(mMagnificationController.setScale(displayId, 2, 100, 100, true, 0));
- assertFalse(mMagnificationController.setCenter(displayId, 100, 100, false, 1));
- assertFalse(mMagnificationController.setScaleAndCenter(displayId,
+ assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
+ assertFalse(
+ mFullScreenMagnificationController.magnificationRegionContains(displayId, 100,
+ 100));
+ assertFalse(mFullScreenMagnificationController.reset(displayId, true));
+ assertFalse(mFullScreenMagnificationController.setScale(displayId, 2, 100, 100, true, 0));
+ assertFalse(mFullScreenMagnificationController.setCenter(displayId, 100, 100, false, 1));
+ assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId,
1.5f, 100, 100, false, 2));
- assertTrue(mMagnificationController.getIdOfLastServiceToMagnify(displayId) < 0);
+ assertTrue(mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId) < 0);
- mMagnificationController.getMagnificationRegion(displayId, new Region());
- mMagnificationController.getMagnificationBounds(displayId, new Rect());
- mMagnificationController.getScale(displayId);
- mMagnificationController.getOffsetX(displayId);
- mMagnificationController.getOffsetY(displayId);
- mMagnificationController.getCenterX(displayId);
- mMagnificationController.getCenterY(displayId);
- mMagnificationController.offsetMagnifiedRegion(displayId, 50, 50, 1);
- mMagnificationController.unregister(displayId);
+ mFullScreenMagnificationController.getMagnificationRegion(displayId, new Region());
+ mFullScreenMagnificationController.getMagnificationBounds(displayId, new Rect());
+ mFullScreenMagnificationController.getScale(displayId);
+ mFullScreenMagnificationController.getOffsetX(displayId);
+ mFullScreenMagnificationController.getOffsetY(displayId);
+ mFullScreenMagnificationController.getCenterX(displayId);
+ mFullScreenMagnificationController.getCenterY(displayId);
+ mFullScreenMagnificationController.offsetMagnifiedRegion(displayId, 50, 50, 1);
+ mFullScreenMagnificationController.unregister(displayId);
}
@Test
@@ -218,7 +222,7 @@ public class MagnificationControllerTest {
final float scale = 2.0f;
final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale);
- assertTrue(mMagnificationController
+ assertTrue(mFullScreenMagnificationController
.setScale(displayId, scale, center.x, center.y, false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
@@ -226,8 +230,8 @@ public class MagnificationControllerTest {
verify(mMockWindowManager).setMagnificationSpec(
eq(displayId), argThat(closeTo(expectedSpec)));
assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedSpec));
- assertEquals(center.x, mMagnificationController.getCenterX(displayId), 0.0);
- assertEquals(center.y, mMagnificationController.getCenterY(displayId), 0.0);
+ assertEquals(center.x, mFullScreenMagnificationController.getCenterX(displayId), 0.0);
+ assertEquals(center.y, mFullScreenMagnificationController.getCenterY(displayId), 0.0);
verify(mMockValueAnimator, times(0)).start();
}
@@ -244,7 +248,7 @@ public class MagnificationControllerTest {
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
float scale = 2.0f;
PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
- assertTrue(mMagnificationController
+ assertTrue(mFullScreenMagnificationController
.setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
@@ -254,8 +258,8 @@ public class MagnificationControllerTest {
PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
- assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
verify(mMockValueAnimator).start();
@@ -291,13 +295,13 @@ public class MagnificationControllerTest {
register(displayId);
// First zoom in
float scale = 2.0f;
- assertTrue(mMagnificationController.setScale(displayId, scale,
+ assertTrue(mFullScreenMagnificationController.setScale(displayId, scale,
INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY(),
false, SERVICE_ID_1));
Mockito.reset(mMockWindowManager);
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
- assertTrue(mMagnificationController
+ assertTrue(mFullScreenMagnificationController
.setCenter(displayId, newCenter.x, newCenter.y, false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
@@ -305,8 +309,8 @@ public class MagnificationControllerTest {
verify(mMockWindowManager).setMagnificationSpec(
eq(displayId), argThat(closeTo(expectedSpec)));
- assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.0);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.0);
+ assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.0);
+ assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.0);
verify(mMockValueAnimator, times(0)).start();
}
@@ -326,12 +330,12 @@ public class MagnificationControllerTest {
PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
- assertTrue(mMagnificationController.setScaleAndCenter(displayId, scale, newCenter.x,
- newCenter.y, true, SERVICE_ID_1));
+ assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale,
+ newCenter.x, newCenter.y, true, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
- assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
verify(mMockAms).notifyMagnificationChanged(displayId,
INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
@@ -370,30 +374,30 @@ public class MagnificationControllerTest {
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter,
- MagnificationController.MAX_SCALE);
+ FullScreenMagnificationController.MAX_SCALE);
MagnificationSpec endSpec = getMagnificationSpec(
- MagnificationController.MAX_SCALE, offsets);
+ FullScreenMagnificationController.MAX_SCALE, offsets);
- assertTrue(mMagnificationController.setScaleAndCenter(displayId,
- MagnificationController.MAX_SCALE + 1.0f,
+ assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
+ FullScreenMagnificationController.MAX_SCALE + 1.0f,
newCenter.x, newCenter.y, false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
- assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
Mockito.reset(mMockWindowManager);
// Verify that we can't zoom below 1x
- assertTrue(mMagnificationController.setScaleAndCenter(displayId, 0.5f,
+ assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, 0.5f,
INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y,
false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x,
- mMagnificationController.getCenterX(displayId), 0.5);
+ mFullScreenMagnificationController.getCenterX(displayId), 0.5);
assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.y,
- mMagnificationController.getCenterY(displayId), 0.5);
+ mFullScreenMagnificationController.getCenterY(displayId), 0.5);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec)));
}
@@ -410,27 +414,27 @@ public class MagnificationControllerTest {
float scale = 2.0f;
// Off the edge to the top and left
- assertTrue(mMagnificationController.setScaleAndCenter(displayId,
+ assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
scale, -100f, -200f, false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
- assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
argThat(closeTo(getMagnificationSpec(scale, newOffsets))));
Mockito.reset(mMockWindowManager);
// Off the edge to the bottom and right
- assertTrue(mMagnificationController.setScaleAndCenter(displayId, scale,
+ assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale,
INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1,
false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
- assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
argThat(closeTo(getMagnificationSpec(scale, newOffsets))));
}
@@ -466,7 +470,7 @@ public class MagnificationControllerTest {
float scale = 2.0f;
PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale);
// First zoom in
- assertTrue(mMagnificationController
+ assertTrue(mFullScreenMagnificationController
.setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false,
SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
@@ -474,7 +478,7 @@ public class MagnificationControllerTest {
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
- mMagnificationController.offsetMagnifiedRegion(displayId,
+ mFullScreenMagnificationController.offsetMagnifiedRegion(displayId,
startOffsets.x - newOffsets.x, startOffsets.y - newOffsets.y,
SERVICE_ID_1);
mMessageCapturingHandler.sendAllMessages();
@@ -482,8 +486,8 @@ public class MagnificationControllerTest {
MagnificationSpec expectedSpec = getMagnificationSpec(scale, newOffsets);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
argThat(closeTo(expectedSpec)));
- assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.0);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.0);
+ assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.0);
+ assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.0);
verify(mMockValueAnimator, times(0)).start();
}
@@ -499,9 +503,9 @@ public class MagnificationControllerTest {
register(displayId);
Mockito.reset(mMockWindowManager);
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
- mMagnificationController.offsetMagnifiedRegion(displayId, 10, 10, SERVICE_ID_1);
+ mFullScreenMagnificationController.offsetMagnifiedRegion(displayId, 10, 10, SERVICE_ID_1);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
- mMagnificationController.offsetMagnifiedRegion(displayId, -10, -10, SERVICE_ID_1);
+ mFullScreenMagnificationController.offsetMagnifiedRegion(displayId, -10, -10, SERVICE_ID_1);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
verifyNoMoreInteractions(mMockWindowManager);
}
@@ -520,24 +524,24 @@ public class MagnificationControllerTest {
// Upper left edges
PointF ulCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
- assertTrue(mMagnificationController
+ assertTrue(mFullScreenMagnificationController
.setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, false,
SERVICE_ID_1));
Mockito.reset(mMockWindowManager);
MagnificationSpec ulSpec = getCurrentMagnificationSpec(displayId);
- mMagnificationController.offsetMagnifiedRegion(displayId, -10, -10,
+ mFullScreenMagnificationController.offsetMagnifiedRegion(displayId, -10, -10,
SERVICE_ID_1);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(ulSpec));
verifyNoMoreInteractions(mMockWindowManager);
// Lower right edges
PointF lrCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
- assertTrue(mMagnificationController
+ assertTrue(mFullScreenMagnificationController
.setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, false,
SERVICE_ID_1));
Mockito.reset(mMockWindowManager);
MagnificationSpec lrSpec = getCurrentMagnificationSpec(displayId);
- mMagnificationController.offsetMagnifiedRegion(displayId, 10, 10,
+ mFullScreenMagnificationController.offsetMagnifiedRegion(displayId, 10, 10,
SERVICE_ID_1);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(lrSpec));
verifyNoMoreInteractions(mMockWindowManager);
@@ -554,14 +558,16 @@ public class MagnificationControllerTest {
private void getIdOfLastServiceToChange_returnsCorrectValue(int displayId) {
register(displayId);
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
- assertTrue(mMagnificationController
+ assertTrue(mFullScreenMagnificationController
.setScale(displayId, 2.0f, startCenter.x, startCenter.y, false,
SERVICE_ID_1));
- assertEquals(SERVICE_ID_1, mMagnificationController.getIdOfLastServiceToMagnify(displayId));
- assertTrue(mMagnificationController
+ assertEquals(SERVICE_ID_1,
+ mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId));
+ assertTrue(mFullScreenMagnificationController
.setScale(displayId, 1.5f, startCenter.x, startCenter.y, false,
SERVICE_ID_2));
- assertEquals(SERVICE_ID_2, mMagnificationController.getIdOfLastServiceToMagnify(displayId));
+ assertEquals(SERVICE_ID_2,
+ mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId));
}
@Test
@@ -575,16 +581,16 @@ public class MagnificationControllerTest {
private void resetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled(int displayId) {
register(displayId);
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
- mMagnificationController
+ mFullScreenMagnificationController
.setScale(displayId, 2.0f, startCenter.x, startCenter.y, false,
SERVICE_ID_1);
- mMagnificationController
+ mFullScreenMagnificationController
.setScale(displayId, 1.5f, startCenter.x, startCenter.y, false,
SERVICE_ID_2);
- assertFalse(mMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1));
- assertTrue(mMagnificationController.isMagnifying(displayId));
- assertTrue(mMagnificationController.resetIfNeeded(displayId, SERVICE_ID_2));
- assertFalse(mMagnificationController.isMagnifying(displayId));
+ assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1));
+ assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
+ assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_2));
+ assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
}
@Test
@@ -600,16 +606,16 @@ public class MagnificationControllerTest {
final int userId2 = 2;
register(displayId);
- mMagnificationController.setUserId(userId1);
+ mFullScreenMagnificationController.setUserId(userId1);
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
float scale = 2.0f;
- mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false,
- SERVICE_ID_1);
+ mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
+ false, SERVICE_ID_1);
- mMagnificationController.setUserId(userId1);
- assertTrue(mMagnificationController.isMagnifying(displayId));
- mMagnificationController.setUserId(userId2);
- assertFalse(mMagnificationController.isMagnifying(displayId));
+ mFullScreenMagnificationController.setUserId(userId1);
+ assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
+ mFullScreenMagnificationController.setUserId(userId2);
+ assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
}
@Test
@@ -625,11 +631,11 @@ public class MagnificationControllerTest {
zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
reset(mMockAms);
- assertTrue(mMagnificationController.resetIfNeeded(displayId, false));
+ assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
verify(mMockAms).notifyMagnificationChanged(eq(displayId),
eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat());
- assertFalse(mMagnificationController.isMagnifying(displayId));
- assertFalse(mMagnificationController.resetIfNeeded(displayId, false));
+ assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
+ assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
}
@Test
@@ -646,8 +652,8 @@ public class MagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
br.onReceive(mMockContext, null);
mMessageCapturingHandler.sendAllMessages();
- assertFalse(mMagnificationController.isMagnifying(DISPLAY_0));
- assertFalse(mMagnificationController.isMagnifying(DISPLAY_1));
+ assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_0));
+ assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_1));
}
@Test
@@ -665,7 +671,7 @@ public class MagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
callbacks.onUserContextChanged();
mMessageCapturingHandler.sendAllMessages();
- assertFalse(mMagnificationController.isMagnifying(displayId));
+ assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
}
@Test
@@ -681,10 +687,10 @@ public class MagnificationControllerTest {
MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
- assertTrue(mMagnificationController.isMagnifying(displayId));
+ assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
callbacks.onRotationChanged(0);
mMessageCapturingHandler.sendAllMessages();
- assertFalse(mMagnificationController.isMagnifying(displayId));
+ assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
}
@Test
@@ -722,8 +728,8 @@ public class MagnificationControllerTest {
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
float scale = 2.0f;
// setting animate parameter to true is differ from zoomIn2xToMiddle()
- mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, true,
- SERVICE_ID_1);
+ mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
+ true, SERVICE_ID_1);
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
Mockito.reset(mMockWindowManager);
@@ -750,8 +756,8 @@ public class MagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
float scale = 2.0f;
- mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false,
- SERVICE_ID_1);
+ mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
+ false, SERVICE_ID_1);
mMessageCapturingHandler.sendAllMessages();
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec)));
@@ -784,8 +790,8 @@ public class MagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
float scale = 2.0f;
- mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, true,
- SERVICE_ID_1);
+ mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
+ true, SERVICE_ID_1);
mMessageCapturingHandler.sendAllMessages();
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
when(mMockValueAnimator.isRunning()).thenReturn(true);
@@ -947,12 +953,12 @@ public class MagnificationControllerTest {
MagnificationSpec firstEndSpec = getMagnificationSpec(
scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale));
- assertTrue(mMagnificationController.setScaleAndCenter(displayId,
+ assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
scale, firstCenter.x, firstCenter.y, true, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
- assertEquals(firstCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
- assertEquals(firstCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ assertEquals(firstCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(firstCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(firstEndSpec));
verify(mMockValueAnimator, times(1)).start();
@@ -977,7 +983,7 @@ public class MagnificationControllerTest {
PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
MagnificationSpec newEndSpec = getMagnificationSpec(
scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale));
- assertTrue(mMagnificationController.setCenter(displayId,
+ assertTrue(mFullScreenMagnificationController.setCenter(displayId,
newCenter.x, newCenter.y, true, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
@@ -1032,7 +1038,7 @@ public class MagnificationControllerTest {
private void register(int displayId) {
mMockValueAnimator = mock(ValueAnimator.class);
when(mMockControllerCtx.newValueAnimator()).thenReturn(mMockValueAnimator);
- mMagnificationController.register(displayId);
+ mFullScreenMagnificationController.register(displayId);
ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class);
verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture());
@@ -1043,9 +1049,9 @@ public class MagnificationControllerTest {
private void zoomIn2xToMiddle(int displayId) {
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
float scale = 2.0f;
- mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false,
- SERVICE_ID_1);
- assertTrue(mMagnificationController.isMagnifying(displayId));
+ mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
+ false, SERVICE_ID_1);
+ assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
}
private MagnificationCallbacks getMagnificationCallbacks(int displayId) {
@@ -1084,9 +1090,9 @@ public class MagnificationControllerTest {
}
private MagnificationSpec getCurrentMagnificationSpec(int displayId) {
- return getMagnificationSpec(mMagnificationController.getScale(displayId),
- mMagnificationController.getOffsetX(displayId),
- mMagnificationController.getOffsetY(displayId));
+ return getMagnificationSpec(mFullScreenMagnificationController.getScale(displayId),
+ mFullScreenMagnificationController.getOffsetX(displayId),
+ mFullScreenMagnificationController.getOffsetY(displayId));
}
private MagSpecMatcher closeTo(MagnificationSpec spec) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 3678d5444790..008cbed10d18 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package com.android.server.accessibility.magnification;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
@@ -51,6 +51,8 @@ import android.view.ViewConfiguration;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.magnification.MagnificationGestureHandler.ScaleChangedListener;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
@@ -121,7 +123,7 @@ public class FullScreenMagnificationGestureHandlerTest {
private static final int DISPLAY_0 = 0;
private Context mContext;
- MagnificationController mMagnificationController;
+ FullScreenMagnificationController mFullScreenMagnificationController;
@Mock
ScaleChangedListener mMockScaleChangedListener;
@@ -135,8 +137,8 @@ public class FullScreenMagnificationGestureHandlerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getContext();
- final MagnificationController.ControllerContext mockController =
- mock(MagnificationController.ControllerContext.class);
+ final FullScreenMagnificationController.ControllerContext mockController =
+ mock(FullScreenMagnificationController.ControllerContext.class);
final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
when(mockController.getContext()).thenReturn(mContext);
when(mockController.getAms()).thenReturn(mock(AccessibilityManagerService.class));
@@ -145,7 +147,8 @@ public class FullScreenMagnificationGestureHandlerTest {
when(mockController.newValueAnimator()).thenReturn(new ValueAnimator());
when(mockController.getAnimationDuration()).thenReturn(1000L);
when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true);
- mMagnificationController = new MagnificationController(mockController, new Object()) {
+ mFullScreenMagnificationController = new FullScreenMagnificationController(mockController,
+ new Object()) {
@Override
public boolean magnificationRegionContains(int displayId, float x, float y) {
return true;
@@ -154,7 +157,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Override
void setForceShowMagnifiableBounds(int displayId, boolean show) {}
};
- mMagnificationController.register(DISPLAY_0);
+ mFullScreenMagnificationController.register(DISPLAY_0);
mClock = new OffsettableClock.Stopped();
boolean detectTripleTap = true;
@@ -164,14 +167,14 @@ public class FullScreenMagnificationGestureHandlerTest {
@After
public void tearDown() {
- mMagnificationController.unregister(DISPLAY_0);
+ mFullScreenMagnificationController.unregister(DISPLAY_0);
}
@NonNull
private FullScreenMagnificationGestureHandler newInstance(boolean detectTripleTap,
boolean detectShortcutTrigger) {
FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler(
- mContext, mMagnificationController, mMockScaleChangedListener,
+ mContext, mFullScreenMagnificationController, mMockScaleChangedListener,
detectTripleTap, detectShortcutTrigger, DISPLAY_0);
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
@@ -653,7 +656,7 @@ public class FullScreenMagnificationGestureHandlerTest {
}
private boolean isZoomed() {
- return mMgh.mMagnificationController.isMagnifying(DISPLAY_0);
+ return mMgh.mFullScreenMagnificationController.isMagnifying(DISPLAY_0);
}
private int tapCount() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index fd6fd896f27a..e580340a29f7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -56,16 +56,17 @@ public class WindowMagnificationGestureHandlerTest {
public static final int STATE_IDLE = 1;
public static final int STATE_SHOW_MAGNIFIER = 2;
public static final int STATE_TWO_FINGERS_DOWN = 3;
+ public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4;
//TODO: Test it after can injecting Handler to GestureMatcher is available.
public static final int FIRST_STATE = STATE_IDLE;
- public static final int LAST_STATE = STATE_TWO_FINGERS_DOWN;
+ public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_X = 301;
public static final float DEFAULT_Y = 299;
//Assume first pointer position (DEFAULT_X,DEFAULT_Y) is in the window.
- public static Rect DEFAULT_WINDOW_FRAME = new Rect(0, 0, 500, 500);
+ public static Rect DEFAULT_WINDOW_FRAME = new Rect(0, 0, 500, 500);
private static final int DISPLAY_0 = 0;
private Context mContext;
@@ -79,7 +80,8 @@ public class WindowMagnificationGestureHandlerTest {
mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0);
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler(
- mContext, mWindowMagnificationManager, mock(ScaleChangedListener.class), DISPLAY_0);
+ mContext, mWindowMagnificationManager, mock(ScaleChangedListener.class),
+ /** detectTripleTap= */true, /** detectShortcutTrigger= */true, DISPLAY_0);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(DISPLAY_0,
DEFAULT_WINDOW_FRAME);
@@ -108,7 +110,9 @@ public class WindowMagnificationGestureHandlerTest {
* <br> IDLE -> SHOW_MAGNIFIER [label="a11y\nbtn"]
* <br> SHOW_MAGNIFIER -> TWO_FINGER_DOWN [label="2hold"]
* <br> TWO_FINGER_DOWN -> SHOW_MAGNIFIER [label="release"]
- * <br> SHOW_MAGNIFIER -> IDLE [label="a11y\nbtn"]*
+ * <br> SHOW_MAGNIFIER -> IDLE [label="a11y\nbtn"]
+ * <br> IDLE -> SHOW_MAGNIFIER_TRIPLE_TAP [label="3tap"]
+ * <br> SHOW_MAGNIFIER_TRIPLE_TAP -> IDLE [label="3tap"]
* </p>
* This navigates between states using "canonical" paths, specified in
* {@link #goFromStateIdleTo} (for traversing away from {@link #STATE_IDLE}) and
@@ -167,20 +171,24 @@ public class WindowMagnificationGestureHandlerTest {
check(!isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mDetectingState, state);
- } break;
- case STATE_SHOW_MAGNIFIER: {
+ }
+ break;
+ case STATE_SHOW_MAGNIFIER:
+ case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mDetectingState, state);
- } break;
+ }
+ break;
case STATE_TWO_FINGERS_DOWN: {
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mObservePanningScalingState,
state);
- } break;
-
- default: throw new IllegalArgumentException("Illegal state: " + state);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -192,17 +200,27 @@ public class WindowMagnificationGestureHandlerTest {
switch (state) {
case STATE_IDLE: {
// no op
- } break;
+ }
+ break;
case STATE_SHOW_MAGNIFIER: {
triggerShortcut();
- } break;
+ }
+ break;
case STATE_TWO_FINGERS_DOWN: {
goFromStateIdleTo(STATE_SHOW_MAGNIFIER);
send(downEvent());
//Second finger is outside the window.
send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_WINDOW_FRAME.right + 10,
DEFAULT_WINDOW_FRAME.bottom + 10));
- } break;
+ }
+ break;
+ case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
+ // Perform triple tap gesture
+ tap();
+ tap();
+ tap();
+ }
+ break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -218,15 +236,25 @@ public class WindowMagnificationGestureHandlerTest {
switch (state) {
case STATE_IDLE: {
// no op
- } break;
+ }
+ break;
case STATE_SHOW_MAGNIFIER: {
mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, false);
- } break;
+ }
+ break;
case STATE_TWO_FINGERS_DOWN: {
send(upEvent());
returnToNormalFrom(STATE_SHOW_MAGNIFIER);
- } break;
- default: throw new IllegalArgumentException("Illegal state: " + state);
+ }
+ break;
+ case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
+ tap();
+ tap();
+ tap();
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -270,6 +298,11 @@ public class WindowMagnificationGestureHandlerTest {
return TouchEventGenerator.upEvent(DISPLAY_0, x, y);
}
+ private void tap() {
+ send(downEvent());
+ send(upEvent());
+ }
+
private MotionEvent pointerEvent(int action, float x, float y) {
final MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords();
defPointerCoords.x = DEFAULT_X;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index c9e628480a52..70e6a340816a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -32,7 +32,10 @@ import static org.testng.Assert.assertTrue;
import static java.lang.Float.NaN;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
@@ -300,6 +303,26 @@ public class WindowMagnificationManagerTest {
assertFalse(mWindowMagnificationManager.isConnected());
}
+ @Test
+ public void requestConnection_registerAndUnregisterBroadcastReceiver() {
+ assertTrue(mWindowMagnificationManager.requestConnection(true));
+ verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
+
+ assertTrue(mWindowMagnificationManager.requestConnection(false));
+ verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+ }
+
+ @Test
+ public void onReceiveScreenOff_removeMagnificationButtonAndDisableWindowMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext,
+ new Intent(Intent.ACTION_SCREEN_OFF));
+
+ verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
+ verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
+ }
+
private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
final int len = pointersLocation.length;
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index 13f63b3a52bf..52c824a020ed 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -42,7 +42,7 @@ import java.util.concurrent.TimeUnit;
@SmallTest
@Presubmit
public class AnrHelperTest {
- private final AnrHelper mAnrHelper = new AnrHelper();
+ private final AnrHelper mAnrHelper = new AnrHelper(mock(ActivityManagerService.class));
private ProcessRecord mAnrApp;
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 3ebc315fa511..b98f0257d7b7 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -18,11 +18,27 @@ package com.android.server.backup;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.app.backup.BackupManager.OperationType;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.params.BackupParams;
+import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.utils.BackupEligibilityRules;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,15 +48,26 @@ import org.mockito.MockitoAnnotations;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class UserBackupManagerServiceTest {
+ private static final String TEST_PACKAGE = "package1";
+ private static final String[] TEST_PACKAGES = new String[] { TEST_PACKAGE };
+
@Mock Context mContext;
+ @Mock IBackupManagerMonitor mBackupManagerMonitor;
+ @Mock IBackupObserver mBackupObserver;
+ @Mock PackageManager mPackageManager;
+ @Mock TransportClient mTransportClient;
+ @Mock BackupEligibilityRules mBackupEligibilityRules;
+
private TestBackupService mService;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mService = new TestBackupService(mContext);
+ mService = new TestBackupService(mContext, mPackageManager);
+ mService.setEnabled(true);
+ mService.setSetupComplete(true);
}
@Test
@@ -57,11 +84,66 @@ public class UserBackupManagerServiceTest {
assertThat(mService.isEnabledStatePersisted).isTrue();
}
+ @Test
+ public void getRequestBackupParams_appIsEligibleForFullBackup() throws Exception {
+ when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn(
+ getPackageInfo(TEST_PACKAGE));
+ when(mBackupEligibilityRules.appIsEligibleForBackup(any())).thenReturn(true);
+ when(mBackupEligibilityRules.appGetsFullBackup(any())).thenReturn(true);
+
+ BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver,
+ mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules,
+ mTransportClient, /* transportDirName */ "", OnTaskFinishedListener.NOP);
+
+ assertThat(params.kvPackages).isEmpty();
+ assertThat(params.fullPackages).contains(TEST_PACKAGE);
+ assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules);
+ }
+
+ @Test
+ public void getRequestBackupParams_appIsEligibleForKeyValueBackup() throws Exception {
+ when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn(
+ getPackageInfo(TEST_PACKAGE));
+ when(mBackupEligibilityRules.appIsEligibleForBackup(any())).thenReturn(true);
+ when(mBackupEligibilityRules.appGetsFullBackup(any())).thenReturn(false);
+
+ BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver,
+ mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules,
+ mTransportClient, /* transportDirName */ "", OnTaskFinishedListener.NOP);
+
+ assertThat(params.kvPackages).contains(TEST_PACKAGE);
+ assertThat(params.fullPackages).isEmpty();
+ assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules);
+ }
+
+ @Test
+ public void getRequestBackupParams_appIsNotEligibleForBackup() throws Exception {
+ when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn(
+ getPackageInfo(TEST_PACKAGE));
+ when(mBackupEligibilityRules.appIsEligibleForBackup(any())).thenReturn(false);
+ when(mBackupEligibilityRules.appGetsFullBackup(any())).thenReturn(false);
+
+ BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver,
+ mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules,
+ mTransportClient, /* transportDirName */ "", OnTaskFinishedListener.NOP);
+
+ assertThat(params.kvPackages).isEmpty();
+ assertThat(params.fullPackages).isEmpty();
+ assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules);
+ }
+
+ private static PackageInfo getPackageInfo(String packageName) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.packageName = packageName;
+ return packageInfo;
+ }
+
private static class TestBackupService extends UserBackupManagerService {
boolean isEnabledStatePersisted = false;
- TestBackupService(Context context) {
- super(context);
+ TestBackupService(Context context, PackageManager packageManager) {
+ super(context, packageManager);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index a9011756240d..444155d12b3f 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.backup.BackupManager.OperationType;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -41,11 +42,13 @@ import com.android.server.backup.UserBackupManagerService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class AppBackupUtilsTest {
+public class BackupEligibilityRulesTest {
private static final String CUSTOM_BACKUP_AGENT_NAME = "custom.backup.agent";
private static final String TEST_PACKAGE_NAME = "test_package";
@@ -54,15 +57,18 @@ public class AppBackupUtilsTest {
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
- private PackageManagerInternal mMockPackageManagerInternal;
+ @Mock private PackageManagerInternal mMockPackageManagerInternal;
+ @Mock private PackageManager mPackageManager;
+ private BackupEligibilityRules mBackupEligibilityRules;
private int mUserId;
@Before
public void setUp() throws Exception {
- mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+ MockitoAnnotations.initMocks(this);
mUserId = UserHandle.USER_SYSTEM;
+ mBackupEligibilityRules = getBackupEligibilityRules(OperationType.BACKUP);
}
@Test
@@ -73,8 +79,7 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mMockPackageManagerInternal, mUserId);
+ boolean isEligible = mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo);
assertThat(isEligible).isFalse();
}
@@ -88,8 +93,7 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = null;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mMockPackageManagerInternal, mUserId);
+ boolean isEligible = mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo);
assertThat(isEligible).isFalse();
}
@@ -102,8 +106,7 @@ public class AppBackupUtilsTest {
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
- boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mMockPackageManagerInternal, mUserId);
+ boolean isEligible = mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo);
assertThat(isEligible).isFalse();
}
@@ -119,8 +122,7 @@ public class AppBackupUtilsTest {
when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
- boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mMockPackageManagerInternal, mUserId);
+ boolean isEligible = mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo);
assertThat(isEligible).isTrue();
}
@@ -136,8 +138,7 @@ public class AppBackupUtilsTest {
when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
- boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mMockPackageManagerInternal, mUserId);
+ boolean isEligible = mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo);
assertThat(isEligible).isTrue();
}
@@ -153,8 +154,7 @@ public class AppBackupUtilsTest {
when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
- boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mMockPackageManagerInternal, mUserId);
+ boolean isEligible = mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo);
assertThat(isEligible).isTrue();
}
@@ -170,8 +170,7 @@ public class AppBackupUtilsTest {
when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
- boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mMockPackageManagerInternal, mUserId);
+ boolean isEligible = mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo);
assertThat(isEligible).isFalse();
}
@@ -187,8 +186,7 @@ public class AppBackupUtilsTest {
when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
- boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mMockPackageManagerInternal, mUserId);
+ boolean isEligible = mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo);
assertThat(isEligible).isFalse();
}
@@ -204,8 +202,33 @@ public class AppBackupUtilsTest {
when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
- boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mMockPackageManagerInternal, mUserId);
+ boolean isEligible = mBackupEligibilityRules.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isFalse();
+ }
+
+ @Test
+ public void appIsEligibleForBackup_backupNotAllowedAndInMigration_returnsTrue()
+ throws Exception {
+ ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID,
+ /* flags */ 0, CUSTOM_BACKUP_AGENT_NAME);
+
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.MIGRATION);
+ boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ public void appIsEligibleForBackup_backupNotAllowedForSystemAppAndInMigration_returnsFalse()
+ throws Exception {
+ ApplicationInfo applicationInfo = getApplicationInfo(Process.SYSTEM_UID,
+ /* flags */ 0, CUSTOM_BACKUP_AGENT_NAME);
+
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.MIGRATION);
+ boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo);
assertThat(isEligible).isFalse();
}
@@ -222,7 +245,7 @@ public class AppBackupUtilsTest {
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
boolean isDisabled =
- AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
+ mBackupEligibilityRules.appIsDisabled(applicationInfo);
assertThat(isDisabled).isFalse();
}
@@ -240,7 +263,7 @@ public class AppBackupUtilsTest {
boolean isDisabled =
- AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
+ mBackupEligibilityRules.appIsDisabled(applicationInfo);
assertThat(isDisabled).isTrue();
}
@@ -257,7 +280,7 @@ public class AppBackupUtilsTest {
boolean isDisabled =
- AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
+ mBackupEligibilityRules.appIsDisabled(applicationInfo);
assertThat(isDisabled).isFalse();
}
@@ -274,7 +297,7 @@ public class AppBackupUtilsTest {
boolean isDisabled =
- AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
+ mBackupEligibilityRules.appIsDisabled(applicationInfo);
assertThat(isDisabled).isTrue();
}
@@ -290,7 +313,7 @@ public class AppBackupUtilsTest {
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
boolean isDisabled =
- AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
+ mBackupEligibilityRules.appIsDisabled(applicationInfo);
assertThat(isDisabled).isTrue();
}
@@ -306,7 +329,7 @@ public class AppBackupUtilsTest {
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
boolean isDisabled =
- AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
+ mBackupEligibilityRules.appIsDisabled(applicationInfo);
assertThat(isDisabled).isTrue();
}
@@ -316,7 +339,7 @@ public class AppBackupUtilsTest {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
- boolean isStopped = AppBackupUtils.appIsStopped(applicationInfo);
+ boolean isStopped = mBackupEligibilityRules.appIsStopped(applicationInfo);
assertThat(isStopped).isTrue();
}
@@ -326,7 +349,7 @@ public class AppBackupUtilsTest {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.flags = ~ApplicationInfo.FLAG_STOPPED;
- boolean isStopped = AppBackupUtils.appIsStopped(applicationInfo);
+ boolean isStopped = mBackupEligibilityRules.appIsStopped(applicationInfo);
assertThat(isStopped).isFalse();
}
@@ -337,7 +360,7 @@ public class AppBackupUtilsTest {
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.backupAgentName = null;
- boolean result = AppBackupUtils.appGetsFullBackup(packageInfo);
+ boolean result = mBackupEligibilityRules.appGetsFullBackup(packageInfo);
assertThat(result).isTrue();
}
@@ -350,7 +373,7 @@ public class AppBackupUtilsTest {
packageInfo.applicationInfo.backupAgentName = "backup.agent";
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
- boolean result = AppBackupUtils.appGetsFullBackup(packageInfo);
+ boolean result = mBackupEligibilityRules.appGetsFullBackup(packageInfo);
assertThat(result).isTrue();
}
@@ -363,7 +386,35 @@ public class AppBackupUtilsTest {
packageInfo.applicationInfo.backupAgentName = "backup.agent";
packageInfo.applicationInfo.flags = ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
- boolean result = AppBackupUtils.appGetsFullBackup(packageInfo);
+ boolean result = mBackupEligibilityRules.appGetsFullBackup(packageInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void appGetsFullBackup_withCustomBackupAgentAndWithoutFullBackupOnlyFlagAndInMigration_returnsTrue()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID,
+ ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY, CUSTOM_BACKUP_AGENT_NAME);
+
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.MIGRATION);
+ boolean result = eligibilityRules.appGetsFullBackup(packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void appGetsFullBackup_systemAppWithCustomBackupAgentAndWithoutFullBackupOnlyFlagAndInMigration_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = getApplicationInfo(Process.SYSTEM_UID,
+ ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY, CUSTOM_BACKUP_AGENT_NAME);
+
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.MIGRATION);
+ boolean result = eligibilityRules.appGetsFullBackup(packageInfo);
assertThat(result).isFalse();
}
@@ -374,7 +425,7 @@ public class AppBackupUtilsTest {
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.backupAgentName = null;
- boolean result = AppBackupUtils.appIsKeyValueOnly(packageInfo);
+ boolean result = mBackupEligibilityRules.appIsKeyValueOnly(packageInfo);
assertThat(result).isFalse();
}
@@ -387,7 +438,7 @@ public class AppBackupUtilsTest {
packageInfo.applicationInfo.backupAgentName = "backup.agent";
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
- boolean result = AppBackupUtils.appIsKeyValueOnly(packageInfo);
+ boolean result = mBackupEligibilityRules.appIsKeyValueOnly(packageInfo);
assertThat(result).isFalse();
}
@@ -400,15 +451,60 @@ public class AppBackupUtilsTest {
packageInfo.applicationInfo.backupAgentName = "backup.agent";
packageInfo.applicationInfo.flags = ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
- boolean result = AppBackupUtils.appIsKeyValueOnly(packageInfo);
+ boolean result = mBackupEligibilityRules.appIsKeyValueOnly(packageInfo);
assertThat(result).isTrue();
}
@Test
+ public void appIgnoresIncludeExcludeRules_systemAppAndInMigration_returnsFalse() {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = Process.SYSTEM_UID;
+
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.MIGRATION);
+ boolean result = eligibilityRules.appIgnoresIncludeExcludeRules(applicationInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void appIgnoresIncludeExcludeRules_systemAppInBackup_returnsFalse() {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = Process.SYSTEM_UID;
+
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.MIGRATION);
+ boolean result = eligibilityRules.appIgnoresIncludeExcludeRules(applicationInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void appIgnoresIncludeExcludeRules_nonSystemAppInMigration_returnsTrue() {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = Process.FIRST_APPLICATION_UID;
+
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.MIGRATION);
+ boolean result = eligibilityRules.appIgnoresIncludeExcludeRules(applicationInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void appIgnoresIncludeExcludeRules_nonSystemInBackup_returnsFalse() {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = Process.FIRST_APPLICATION_UID;
+
+ boolean result = mBackupEligibilityRules.appIgnoresIncludeExcludeRules(applicationInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
- boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, null,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(new Signature[] {SIGNATURE_1}, null);
assertThat(result).isFalse();
}
@@ -420,8 +516,7 @@ public class AppBackupUtilsTest {
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
- boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(new Signature[0], packageInfo);
assertThat(result).isTrue();
}
@@ -439,8 +534,7 @@ public class AppBackupUtilsTest {
null));
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(null, packageInfo);
assertThat(result).isFalse();
}
@@ -458,8 +552,7 @@ public class AppBackupUtilsTest {
null));
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(new Signature[0], packageInfo);
assertThat(result).isFalse();
}
@@ -474,8 +567,8 @@ public class AppBackupUtilsTest {
packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(new Signature[] {SIGNATURE_1},
+ packageInfo);
assertThat(result).isFalse();
}
@@ -489,8 +582,8 @@ public class AppBackupUtilsTest {
packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(new Signature[] {SIGNATURE_1},
+ packageInfo);
assertThat(result).isFalse();
}
@@ -502,8 +595,7 @@ public class AppBackupUtilsTest {
packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(null, packageInfo);
assertThat(result).isFalse();
}
@@ -516,8 +608,7 @@ public class AppBackupUtilsTest {
packageInfo.signingInfo = null;
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(new Signature[0], packageInfo);
assertThat(result).isFalse();
}
@@ -538,9 +629,8 @@ public class AppBackupUtilsTest {
null));
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(
- new Signature[] {signature3Copy, signature1Copy, signature2Copy}, packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(
+ new Signature[] {signature3Copy, signature1Copy, signature2Copy}, packageInfo);
assertThat(result).isTrue();
}
@@ -560,9 +650,8 @@ public class AppBackupUtilsTest {
null));
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(
- new Signature[]{signature2Copy, signature1Copy}, packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(
+ new Signature[]{signature2Copy, signature1Copy}, packageInfo);
assertThat(result).isTrue();
}
@@ -582,9 +671,8 @@ public class AppBackupUtilsTest {
null));
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(
- new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}, packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(
+ new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}, packageInfo);
assertThat(result).isFalse();
}
@@ -604,9 +692,8 @@ public class AppBackupUtilsTest {
null));
packageInfo.applicationInfo = new ApplicationInfo();
- boolean result = AppBackupUtils.signaturesMatch(
- new Signature[]{signature1Copy, signature2Copy, SIGNATURE_4}, packageInfo,
- mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(
+ new Signature[]{signature1Copy, signature2Copy, SIGNATURE_4}, packageInfo);
assertThat(result).isFalse();
}
@@ -629,8 +716,8 @@ public class AppBackupUtilsTest {
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
packageInfo.packageName);
- boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
- packageInfo, mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(new Signature[] {signature1Copy},
+ packageInfo);
assertThat(result).isTrue();
}
@@ -655,8 +742,8 @@ public class AppBackupUtilsTest {
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
packageInfo.packageName);
- boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
- packageInfo, mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(new Signature[] {signature1Copy},
+ packageInfo);
assertThat(result).isTrue();
}
@@ -682,15 +769,30 @@ public class AppBackupUtilsTest {
doReturn(false).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
packageInfo.packageName);
- boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
- packageInfo, mMockPackageManagerInternal);
+ boolean result = mBackupEligibilityRules.signaturesMatch(new Signature[] {signature1Copy},
+ packageInfo);
assertThat(result).isFalse();
}
+ private BackupEligibilityRules getBackupEligibilityRules(@OperationType int operationType) {
+ return new BackupEligibilityRules(mPackageManager, mMockPackageManagerInternal, mUserId,
+ operationType);
+ }
+
private static Signature generateSignature(byte i) {
byte[] signatureBytes = new byte[256];
signatureBytes[0] = i;
return new Signature(signatureBytes);
}
+
+ private static ApplicationInfo getApplicationInfo(int appUid, int flags,
+ String backupAgentName) {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.flags = 0;
+ applicationInfo.packageName = TEST_PACKAGE_NAME;
+ applicationInfo.uid = appUid;
+ applicationInfo.backupAgentName = backupAgentName;
+ return applicationInfo;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 75e0d9144f57..a5df53205a36 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -75,6 +76,8 @@ public class AuthServiceTest {
IIrisService mIrisService;
@Mock
IFaceService mFaceService;
+ @Mock
+ AppOpsManager mAppOpsManager;
@Before
public void setUp() {
@@ -93,6 +96,7 @@ public class AuthServiceTest {
when(mInjector.getFingerprintService()).thenReturn(mFingerprintService);
when(mInjector.getFaceService()).thenReturn(mFaceService);
when(mInjector.getIrisService()).thenReturn(mIrisService);
+ when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager);
}
@Test
@@ -140,7 +144,9 @@ public class AuthServiceTest {
// TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId
@Test
- public void testAuthenticate_callsBiometricServiceAuthenticate() throws Exception {
+ public void testAuthenticate_appOpsOk_callsBiometricServiceAuthenticate() throws Exception {
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(),
+ any())).thenReturn(AppOpsManager.MODE_ALLOWED);
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
@@ -170,6 +176,38 @@ public class AuthServiceTest {
}
@Test
+ public void testAuthenticate_appOpsDenied_doesNotCallBiometricService() throws Exception {
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(),
+ any())).thenReturn(AppOpsManager.MODE_ERRORED);
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final Binder token = new Binder();
+ final PromptInfo promptInfo = new PromptInfo();
+ final long sessionId = 0;
+ final int userId = 0;
+
+ mAuthService.mImpl.authenticate(
+ token,
+ sessionId,
+ userId,
+ mReceiver,
+ TEST_OP_PACKAGE_NAME,
+ promptInfo);
+ waitForIdle();
+ verify(mBiometricService, never()).authenticate(
+ eq(token),
+ eq(sessionId),
+ eq(userId),
+ eq(mReceiver),
+ eq(TEST_OP_PACKAGE_NAME),
+ eq(promptInfo),
+ eq(Binder.getCallingUid()),
+ eq(Binder.getCallingPid()),
+ eq(UserHandle.getCallingUserId()));
+ }
+
+ @Test
public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 4afed48ce8ee..f0be9f1d3213 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -64,6 +64,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.biometrics.sensors.LockoutTracker;
import org.junit.Before;
import org.junit.Test;
@@ -825,6 +826,62 @@ public class BiometricServiceTest {
}
@Test
+ public void testBiometricAuth_whenBiometricLockoutTimed_sendsErrorAndModality()
+ throws Exception {
+ testBiometricAuth_whenLockout(LockoutTracker.LOCKOUT_TIMED,
+ BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT);
+ }
+
+ @Test
+ public void testBiometricAuth_whenBiometricLockoutPermanent_sendsErrorAndModality()
+ throws Exception {
+ testBiometricAuth_whenLockout(LockoutTracker.LOCKOUT_PERMANENT,
+ BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
+ }
+
+ private void testBiometricAuth_whenLockout(@LockoutTracker.LockoutMode int lockoutMode,
+ int biometricPromptError) throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+ .thenReturn(lockoutMode);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, null /* authenticators */);
+ waitForIdle();
+
+ // Modality and error are sent
+ verify(mReceiver1).onError(eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(biometricPromptError), eq(0) /* vendorCode */);
+ }
+
+ @Test
+ public void testBiometricOrCredentialAuth_whenBiometricLockout_showsCredential()
+ throws Exception {
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+ .thenReturn(LockoutTracker.LOCKOUT_PERMANENT);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */,
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_STRONG);
+ waitForIdle();
+
+ verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
+ assertNotNull(mBiometricService.mCurrentAuthSession);
+ assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(Authenticators.DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mPromptInfo.getAuthenticators());
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ any(IBiometricSysuiReceiver.class),
+ eq(0 /* biometricModality */),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME),
+ anyLong() /* sessionId */);
+ }
+
+ @Test
public void testCombineAuthenticatorBundles_withKeyDeviceCredential_andKeyAuthenticators() {
final boolean allowDeviceCredential = false;
final @Authenticators.Types int authenticators =
@@ -1199,6 +1256,29 @@ public class BiometricServiceTest {
}
@Test
+ public void testCanAuthenticate_whenLockoutTimed() throws Exception {
+ testCanAuthenticate_whenLockedOut(LockoutTracker.LOCKOUT_TIMED);
+ }
+
+ @Test
+ public void testCanAuthenticate_whenLockoutPermanent() throws Exception {
+ testCanAuthenticate_whenLockedOut(LockoutTracker.LOCKOUT_PERMANENT);
+ }
+
+ private void testCanAuthenticate_whenLockedOut(@LockoutTracker.LockoutMode int lockoutMode)
+ throws Exception {
+ // When only biometric is requested, and sensor is strong enough
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+
+ when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+ .thenReturn(lockoutMode);
+
+ // Lockout is not considered an error for BiometricManager#canAuthenticate
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG));
+ }
+
+ @Test
public void testAuthenticatorActualStrength() {
// Tuple of OEM config, updatedStrength, and expectedStrength
final int[][] testCases = {
@@ -1497,6 +1577,8 @@ public class BiometricServiceTest {
when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
.thenReturn(enrolled);
when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
+ when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+ .thenReturn(LockoutTracker.LOCKOUT_NONE);
mBiometricService.mImpl.registerAuthenticator(SENSOR_ID_FINGERPRINT, modality, strength,
mFingerprintAuthenticator);
}
@@ -1504,6 +1586,8 @@ public class BiometricServiceTest {
if ((modality & BiometricAuthenticator.TYPE_FACE) != 0) {
when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(enrolled);
when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
+ when(mFaceAuthenticator.getLockoutModeForUser(anyInt()))
+ .thenReturn(LockoutTracker.LOCKOUT_NONE);
mBiometricService.mImpl.registerAuthenticator(SENSOR_ID_FACE, modality, strength,
mFaceAuthenticator);
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index 8aa0406469ec..b05a819ba489 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -225,7 +225,11 @@ public class UtilsTest {
{BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE},
{BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
- BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE}
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE},
+ {BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+ BiometricManager.BIOMETRIC_SUCCESS},
+ {BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
+ BiometricManager.BIOMETRIC_SUCCESS}
};
for (int i = 0; i < testCases.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
deleted file mode 100644
index 03b336632f8a..000000000000
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.biometrics.sensors;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@Presubmit
-@SmallTest
-public class BiometricServiceBaseTest {
- private static class TestableBiometricServiceBase extends
- com.android.server.biometrics.sensors.BiometricServiceBase {
- TestableBiometricServiceBase(Context context) {
- super(context);
- }
-
- @Override
- protected String getTag() {
- return null;
- }
-
- @Override
- protected DaemonWrapper getDaemonWrapper() {
- return null;
- }
-
- @Override
- protected BiometricUtils getBiometricUtils() {
- return null;
- }
-
- @Override
- protected boolean hasReachedEnrollmentLimit(int userId) {
- return false;
- }
-
- @Override
- protected void updateActiveGroup(int userId, String clientPackage) {
- }
-
- @Override
- protected boolean hasEnrolledBiometrics(int userId) {
- return false;
- }
-
- @Override
- protected String getManageBiometricPermission() {
- return null;
- }
-
- @Override
- protected void checkUseBiometricPermission() {
- }
-
- @Override
- protected boolean checkAppOps(int uid, String opPackageName) {
- return false;
- }
-
- @Override
- protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
- int userId) {
- return null;
- }
-
- @Override
- protected int statsModality() {
- return 0;
- }
-
- @Override
- protected int getLockoutMode(int userId) {
- return 0;
- }
- }
-
- private static final int CLIENT_COOKIE = 0xc00c1e;
-
- private BiometricServiceBase mBiometricServiceBase;
-
- @Mock
- private Context mContext;
- @Mock
- private Resources mResources;
- @Mock
- private BiometricAuthenticator.Identifier mIdentifier;
- @Mock
- private ClientMonitor mClient;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getString(anyInt())).thenReturn("");
- when(mClient.getCookie()).thenReturn(CLIENT_COOKIE);
-
- mBiometricServiceBase = new TestableBiometricServiceBase(mContext);
- }
-
- @Test
- public void testHandleEnumerate_doesNotCrash_withNullClient() {
- mBiometricServiceBase.handleEnumerate(mIdentifier, 0 /* remaining */);
- }
-
- @Test
- public void testStartClient_sendsErrorAndRemovesClient_onNonzeroErrorCode() {
- when(mClient.start()).thenReturn(1);
-
- mBiometricServiceBase.startClient(mClient, false /* initiatedByClient */);
-
- verify(mClient).onError(anyInt(), anyInt());
- verify(mClient).destroy();
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index daaabf8141ff..9a465a91e84e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -48,8 +48,6 @@ import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -4931,20 +4929,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.thenReturn(passwordMetrics);
dpm.reportPasswordChanged(userHandle);
- // Drain ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED broadcasts as part of
- // reportPasswordChanged()
- // This broadcast should be sent 2-4 times:
- // * Twice from calls to DevicePolicyManagerService.updatePasswordExpirationsLocked,
- // once for each affected user, in DevicePolicyManagerService.reportPasswordChanged.
- // * Optionally, at most twice from calls to DevicePolicyManagerService.saveSettingsLocked
- // in DevicePolicyManagerService.reportPasswordChanged, once with the userId
- // the password change is relevant to and another with the credential owner of said
- // userId, if the password checkpoint value changes.
- verify(mContext.spiedContext, atMost(4)).sendBroadcastAsUser(
- MockUtils.checkIntentAction(
- DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(userHandle));
- verify(mContext.spiedContext, atLeast(2)).sendBroadcastAsUser(
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
MockUtils.checkUserHandle(userHandle));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 06d395b045e9..775e88750cef 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -291,7 +291,7 @@ public class HdmiControlServiceTest {
}
@Test
- public void disableAndReenableCec_volumeControlReturnsToOriginalValue() {
+ public void disableAndReenableCec_volumeControlReturnsToOriginalValue_enabled() {
boolean volumeControlEnabled = true;
mHdmiControlService.setHdmiCecVolumeControlEnabled(volumeControlEnabled);
@@ -304,6 +304,19 @@ public class HdmiControlServiceTest {
}
@Test
+ public void disableAndReenableCec_volumeControlReturnsToOriginalValue_disabled() {
+ boolean volumeControlEnabled = false;
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(volumeControlEnabled);
+
+ mHdmiControlService.setControlEnabled(false);
+ assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse();
+
+ mHdmiControlService.setControlEnabled(true);
+ assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isEqualTo(
+ volumeControlEnabled);
+ }
+
+ @Test
public void disableAndReenableCec_volumeControlFeatureListenersNotified() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 09f946d4b107..e7eff00c472e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -102,13 +102,13 @@ public class ScanTests {
@Before
public void setupDefaultAbiBehavior() throws Exception {
when(mMockPackageAbiHelper.derivePackageAbi(
- any(AndroidPackage.class), anyBoolean(), nullable(String.class), anyBoolean()))
+ any(AndroidPackage.class), anyBoolean(), nullable(String.class)))
.thenReturn(new Pair<>(
new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"),
new PackageAbiHelper.NativeLibraryPaths(
"derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2")));
- when(mMockPackageAbiHelper.getNativeLibraryPaths(
- any(AndroidPackage.class), any(PackageSetting.class), any(File.class)))
+ when(mMockPackageAbiHelper.deriveNativeLibraryPaths(
+ any(AndroidPackage.class), anyBoolean(), any(File.class)))
.thenReturn(new PackageAbiHelper.NativeLibraryPaths(
"getRootDir", true, "getNativeDir", "getNativeDir2"
));
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 420ff19aab74..0f5f0af8f838 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -96,30 +96,29 @@ open class AndroidPackageParsingTestBase {
anyString(), anyInt())) { true }
}
- lateinit var oldPackages: List<PackageParser.Package>
+ val oldPackages = mutableListOf<PackageParser.Package>()
- lateinit var newPackages: List<AndroidPackage>
+ val newPackages = mutableListOf<AndroidPackage>()
@Suppress("ConstantConditionIf")
@JvmStatic
@BeforeClass
fun setUpPackages() {
- this.oldPackages = apks.mapNotNull {
- try {
- packageParser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
- } catch (ignored: Exception) {
- // Parsing issues will be caught by SystemPartitionParseTest
- null
- }
- }
-
- this.newPackages = apks.mapNotNull {
+ apks.mapNotNull {
try {
+ packageParser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false) to
packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
} catch (ignored: Exception) {
- // Parsing issues will be caught by SystemPartitionParseTest
+ // It is intentional that a failure of either call here will result in failing
+ // both. Having null on one side would mean nothing to compare. Due to the
+ // nature of presubmit, this may not be caused by the change being tested, so
+ // it's unhelpful to consider it a failure. Actual parsing issues will be
+ // reported by SystemPartitionParseTest in postsubmit.
null
}
+ }.forEach { (old, new) ->
+ oldPackages += old
+ newPackages += new
}
if (DUMP_HPROF_TO_EXTERNAL) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
index 605841df0c5a..4cd057cb482f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
@@ -20,7 +20,11 @@ import android.content.pm.PackageManager
import android.content.pm.PackageParser
import android.platform.test.annotations.Postsubmit
import com.android.server.pm.PackageManagerService
+import com.android.server.pm.PackageManagerServiceUtils
+import org.junit.Rule
import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import java.io.File
/**
* This test parses all the system APKs on the device image to ensure that they succeed.
@@ -34,20 +38,46 @@ import org.junit.Test
@Postsubmit
class SystemPartitionParseTest {
- private val APKS = PackageManagerService.SYSTEM_PARTITIONS
- .flatMap { listOfNotNull(it.appFolder, it.privAppFolder, it.overlayFolder) }
- .flatMap {
- it.walkTopDown()
- .filter { it.name.endsWith(".apk") }
- .toList()
- }
- .distinct()
-
private val parser = PackageParser2.forParsingFileWithDefaults()
+ @get:Rule
+ val tempFolder = TemporaryFolder()
+
+ private fun buildApks(): List<File> {
+ val files = PackageManagerService.SYSTEM_PARTITIONS
+ .flatMap { listOfNotNull(it.appFolder, it.privAppFolder, it.overlayFolder) }
+ .flatMap {
+ it.listFiles()
+ ?.toList()
+ ?: emptyList()
+ }
+ .distinct()
+ .toMutableList()
+
+ val compressedFiles = mutableListOf<File>()
+
+ files.removeAll { it ->
+ it.listFiles()?.toList().orEmpty()
+ .filter { it.name.endsWith(PackageManagerService.COMPRESSED_EXTENSION) }
+ .also { compressedFiles.addAll(it) }
+ .isNotEmpty()
+ }
+
+ compressedFiles.mapTo(files) { input ->
+ tempFolder.newFolder()
+ .also {
+ // Decompress to an APK file inside the temp folder which can be tested.
+ it.resolve(input.nameWithoutExtension + ".apk")
+ .apply { PackageManagerServiceUtils.decompressFile(input, this) }
+ }
+ }
+
+ return files
+ }
+
@Test
fun verify() {
- val exceptions = APKS
+ val exceptions = buildApks()
.map {
runCatching {
parser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
diff --git a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java b/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
new file mode 100644
index 000000000000..a1f105604559
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Run: atest FrameworksServicesTests:ShutdownCheckPointsTest
+ */
+@Presubmit
+public class ShutdownCheckPointsTest {
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock
+ private IActivityManager mActivityManager;
+
+ private TestInjector mTestInjector;
+ private ShutdownCheckPoints mInstance;
+
+ @Before
+ public void setUp() {
+ Locale.setDefault(Locale.UK);
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ mTestInjector = new TestInjector(mActivityManager);
+ mInstance = new ShutdownCheckPoints(mTestInjector);
+ }
+
+ @Test
+ public void testSystemServerEntry() {
+ mTestInjector.setCurrentTime(1000);
+ mInstance.recordCheckPointInternal();
+
+ assertTrue(dumpToString().startsWith(
+ "Shutdown request from SYSTEM at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "com.android.server.power.ShutdownCheckPointsTest"
+ + ".testSystemServerEntry\n at "));
+ }
+
+ @Test
+ public void testSystemServiceBinderEntry() {
+ mTestInjector.setCurrentTime(1000);
+ mInstance.recordCheckPointInternal(Process.myPid());
+
+ assertTrue(dumpToString().startsWith(
+ "Shutdown request from SYSTEM at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "com.android.server.power.ShutdownCheckPointsTest"
+ + ".testSystemServiceBinderEntry\n at "));
+ }
+
+ @Test
+ public void testCallerProcessBinderEntry() throws RemoteException {
+ List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfos = new ArrayList<>();
+ runningAppProcessInfos.add(
+ new ActivityManager.RunningAppProcessInfo("process_name", 1, new String[0]));
+ when(mActivityManager.getRunningAppProcesses()).thenReturn(runningAppProcessInfos);
+
+ mTestInjector.setCurrentTime(1000);
+ mInstance.recordCheckPointInternal(1);
+
+ assertEquals(
+ "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "com.android.server.power.ShutdownCheckPointsTest"
+ + ".testCallerProcessBinderEntry\n"
+ + "From process process_name (pid=1)\n\n",
+ dumpToString());
+ }
+
+ @Test
+ public void testRemoteExceptionOnBinderEntry() throws RemoteException {
+ when(mActivityManager.getRunningAppProcesses()).thenThrow(new RemoteException("Error"));
+
+ mTestInjector.setCurrentTime(1000);
+ mInstance.recordCheckPointInternal(1);
+
+ assertEquals(
+ "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "com.android.server.power.ShutdownCheckPointsTest"
+ + ".testRemoteExceptionOnBinderEntry\n"
+ + "From process ? (pid=1)\n\n",
+ dumpToString());
+ }
+
+ @Test
+ public void testUnknownProcessBinderEntry() {
+ mTestInjector.setCurrentTime(1000);
+ mInstance.recordCheckPointInternal(1);
+
+ assertEquals(
+ "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "com.android.server.power.ShutdownCheckPointsTest"
+ + ".testUnknownProcessBinderEntry\n"
+ + "From process ? (pid=1)\n\n",
+ dumpToString());
+ }
+
+ @Test
+ public void testSystemServiceIntentEntry() {
+ mTestInjector.setCurrentTime(1000);
+ mInstance.recordCheckPointInternal("some.intent", "android");
+
+ assertTrue(dumpToString().startsWith(
+ "Shutdown request from SYSTEM at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "com.android.server.power.ShutdownCheckPointsTest"
+ + ".testSystemServiceIntentEntry\n at "));
+ }
+
+ @Test
+ public void testIntentEntry() {
+ mTestInjector.setCurrentTime(1000);
+ mInstance.recordCheckPointInternal("some.intent", "some.app");
+
+ assertEquals(
+ "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "Intent: some.intent\n"
+ + "Package: some.app\n\n",
+ dumpToString());
+ }
+
+ @Test
+ public void testMultipleEntries() {
+ mTestInjector.setCurrentTime(1000);
+ mInstance.recordCheckPointInternal(1);
+ mTestInjector.setCurrentTime(2000);
+ mInstance.recordCheckPointInternal(2);
+ mTestInjector.setCurrentTime(3000);
+ mInstance.recordCheckPointInternal("intent", "app");
+
+ assertEquals(
+ "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "com.android.server.power.ShutdownCheckPointsTest.testMultipleEntries\n"
+ + "From process ? (pid=1)\n"
+ + "\n"
+ + "Shutdown request from BINDER at 1970-01-01 00:00:02.000 UTC (epoch=2000)"
+ + "\n"
+ + "com.android.server.power.ShutdownCheckPointsTest.testMultipleEntries\n"
+ + "From process ? (pid=2)\n"
+ + "\n"
+ + "Shutdown request from INTENT at 1970-01-01 00:00:03.000 UTC (epoch=3000)"
+ + "\n"
+ + "Intent: intent\n"
+ + "Package: app\n"
+ + "\n",
+ dumpToString());
+ }
+
+ @Test
+ public void testTooManyEntriesDropsOlderOnes() {
+ mTestInjector.setCheckPointsLimit(2);
+ ShutdownCheckPoints limitedInstance = new ShutdownCheckPoints(mTestInjector);
+
+ mTestInjector.setCurrentTime(1000);
+ limitedInstance.recordCheckPointInternal("intent.1", "app.1");
+ mTestInjector.setCurrentTime(2000);
+ limitedInstance.recordCheckPointInternal("intent.2", "app.2");
+ mTestInjector.setCurrentTime(3000);
+ limitedInstance.recordCheckPointInternal("intent.3", "app.3");
+
+ // Drops first intent.
+ assertEquals(
+ "Shutdown request from INTENT at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n"
+ + "Intent: intent.2\n"
+ + "Package: app.2\n"
+ + "\n"
+ + "Shutdown request from INTENT at 1970-01-01 00:00:03.000 UTC (epoch=3000)"
+ + "\n"
+ + "Intent: intent.3\n"
+ + "Package: app.3\n"
+ + "\n",
+ dumpToString(limitedInstance));
+ }
+
+ @Test
+ public void testDumpToFile() throws Exception {
+ File tempDir = createTempDir();
+ File baseFile = new File(tempDir, "checkpoints");
+
+ mTestInjector.setCurrentTime(1000);
+ mInstance.recordCheckPointInternal("first.intent", "first.app");
+ dumpToFile(baseFile);
+
+ mTestInjector.setCurrentTime(2000);
+ mInstance.recordCheckPointInternal("second.intent", "second.app");
+ dumpToFile(baseFile);
+
+ File[] dumpFiles = tempDir.listFiles();
+ Arrays.sort(dumpFiles);
+
+ assertEquals(2, dumpFiles.length);
+ assertEquals(
+ "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "Intent: first.intent\n"
+ + "Package: first.app\n\n",
+ readFileAsString(dumpFiles[0].getAbsolutePath()));
+ assertEquals(
+ "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "Intent: first.intent\n"
+ + "Package: first.app\n\n"
+ + "Shutdown request from INTENT at 1970-01-01 00:00:02.000 UTC (epoch=2000)"
+ + "\n"
+ + "Intent: second.intent\n"
+ + "Package: second.app\n\n",
+ readFileAsString(dumpFiles[1].getAbsolutePath()));
+ }
+
+ @Test
+ public void testTooManyFilesDropsOlderOnes() throws Exception {
+ mTestInjector.setDumpFilesLimit(1);
+ ShutdownCheckPoints instance = new ShutdownCheckPoints(mTestInjector);
+ File tempDir = createTempDir();
+ File baseFile = new File(tempDir, "checkpoints");
+
+ mTestInjector.setCurrentTime(1000);
+ instance.recordCheckPointInternal("first.intent", "first.app");
+ dumpToFile(instance, baseFile);
+
+ mTestInjector.setCurrentTime(2000);
+ instance.recordCheckPointInternal("second.intent", "second.app");
+ dumpToFile(instance, baseFile);
+
+ File[] dumpFiles = tempDir.listFiles();
+ assertEquals(1, dumpFiles.length);
+ assertEquals(
+ "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "Intent: first.intent\n"
+ + "Package: first.app\n\n"
+ + "Shutdown request from INTENT at 1970-01-01 00:00:02.000 UTC (epoch=2000)"
+ + "\n"
+ + "Intent: second.intent\n"
+ + "Package: second.app\n\n",
+ readFileAsString(dumpFiles[0].getAbsolutePath()));
+ }
+
+ private String dumpToString() {
+ return dumpToString(mInstance);
+ }
+
+ private String dumpToString(ShutdownCheckPoints instance) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ instance.dumpInternal(pw);
+ return sw.toString();
+ }
+
+ private void dumpToFile(File baseFile) throws InterruptedException {
+ dumpToFile(mInstance, baseFile);
+ }
+
+ private void dumpToFile(ShutdownCheckPoints instance, File baseFile)
+ throws InterruptedException {
+ Thread dumpThread = instance.newDumpThreadInternal(baseFile);
+ dumpThread.start();
+ dumpThread.join();
+ }
+
+ private String readFileAsString(String absolutePath) throws IOException {
+ return new String(Files.readAllBytes(Paths.get(absolutePath)), StandardCharsets.UTF_8);
+ }
+
+ private File createTempDir() throws IOException {
+ File tempDir = File.createTempFile("checkpoints", "out");
+ tempDir.delete();
+ tempDir.mkdir();
+ return tempDir;
+ }
+
+ /** Fake system dependencies for testing. */
+ private final class TestInjector implements ShutdownCheckPoints.Injector {
+ private long mNow;
+ private int mCheckPointsLimit;
+ private int mDumpFilesLimit;
+ private IActivityManager mActivityManager;
+
+ TestInjector(IActivityManager activityManager) {
+ mNow = 0;
+ mCheckPointsLimit = 100;
+ mDumpFilesLimit = 2;
+ mActivityManager = activityManager;
+ }
+
+ @Override
+ public long currentTimeMillis() {
+ return mNow;
+ }
+
+ @Override
+ public int maxCheckPoints() {
+ return mCheckPointsLimit;
+ }
+
+ @Override
+ public int maxDumpFiles() {
+ return mDumpFilesLimit;
+ }
+
+ @Override
+ public IActivityManager activityManager() {
+ return mActivityManager;
+ }
+
+ void setCurrentTime(long time) {
+ mNow = time;
+ }
+
+ void setCheckPointsLimit(int limit) {
+ mCheckPointsLimit = limit;
+ }
+
+ void setDumpFilesLimit(int dumpFilesLimit) {
+ mDumpFilesLimit = dumpFilesLimit;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index 76239fcd9385..72d6caf1a5be 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -33,6 +33,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
+import com.android.server.power.batterysaver.BatterySavingStats.PlugState;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -118,7 +119,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(4);
target.drainBattery(100);
@@ -126,7 +128,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(2);
target.drainBattery(500);
@@ -134,7 +137,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(4);
target.drainBattery(100);
@@ -142,7 +146,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(2);
target.drainBattery(500);
@@ -150,7 +155,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(3);
target.drainBattery(100);
@@ -158,7 +164,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.LIGHT);
+ DozeState.LIGHT,
+ PlugState.UNPLUGGED);
target.advanceClock(5);
target.drainBattery(100);
@@ -166,7 +173,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.DEEP);
+ DozeState.DEEP,
+ PlugState.UNPLUGGED);
target.advanceClock(1);
target.drainBattery(200);
@@ -174,7 +182,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.ON,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(1);
target.drainBattery(300);
@@ -182,7 +191,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(3);
target.drainBattery(500);
@@ -190,12 +200,17 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.ON,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(3);
target.drainBattery(500);
- target.startCharging();
+ target.transitionState(
+ BatterySaverState.ON,
+ InteractiveState.INTERACTIVE,
+ DozeState.NOT_DOZING,
+ PlugState.PLUGGED);
target.advanceClock(5);
target.drainBattery(1000);
@@ -203,28 +218,34 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.ON,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(5);
target.drainBattery(100);
- target.startCharging();
+ target.transitionState(
+ BatterySaverState.ON,
+ InteractiveState.INTERACTIVE,
+ DozeState.NOT_DOZING,
+ PlugState.PLUGGED);
target.assertDumpable();
assertEquals(
- "BS=0,I=0,D=0:{4m,1000,15000.00uA/H,1500.00%}\n" +
- "BS=1,I=0,D=0:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=0,I=1,D=0:{14m,800,3428.57uA/H,342.86%}\n" +
- "BS=1,I=1,D=0:{9m,900,6000.00uA/H,600.00%}\n" +
- "BS=0,I=0,D=1:{5m,100,1200.00uA/H,120.00%}\n" +
- "BS=1,I=0,D=1:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=0,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=1,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=0,I=0,D=2:{1m,200,12000.00uA/H,1200.00%}\n" +
- "BS=1,I=0,D=2:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=0,I=1,D=2:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=1,I=1,D=2:{0m,0,0.00uA/H,0.00%}",
+ "BS=0,I=0,D=0,P=0:{4m,1000,15000.00uA/H,1500.00%}\n"
+ + "BS=1,I=0,D=0,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=0,I=1,D=0,P=0:{14m,800,3428.57uA/H,342.86%}\n"
+ + "BS=1,I=1,D=0,P=0:{9m,900,6000.00uA/H,600.00%}\n"
+ + "BS=0,I=0,D=1,P=0:{5m,100,1200.00uA/H,120.00%}\n"
+ + "BS=1,I=0,D=1,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=0,I=1,D=1,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=1,I=1,D=1,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=0,I=0,D=2,P=0:{1m,200,12000.00uA/H,1200.00%}\n"
+ + "BS=1,I=0,D=2,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=0,I=1,D=2,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=1,I=1,D=2,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=1,I=1,D=0,P=1:{5m,1000,12000.00uA/H,1200.00%}",
target.toDebugString());
}
@@ -242,7 +263,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
@@ -253,7 +275,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
assertLog();
@@ -264,7 +287,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.DEEP);
+ DozeState.DEEP,
+ PlugState.UNPLUGGED);
target.advanceClock(1);
target.drainBattery(2000);
@@ -274,7 +298,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.LIGHT);
+ DozeState.LIGHT,
+ PlugState.UNPLUGGED);
target.advanceClock(1);
target.drainBattery(2000);
@@ -284,7 +309,8 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.ON,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
assertLog();
@@ -292,7 +318,11 @@ public class BatterySavingStatsTest {
target.drainBattery(10000);
reset(mMetricsLogger);
- target.startCharging();
+ target.transitionState(
+ BatterySaverState.ON,
+ InteractiveState.INTERACTIVE,
+ DozeState.NOT_DOZING,
+ PlugState.PLUGGED);
assertLog();
@@ -303,14 +333,19 @@ public class BatterySavingStatsTest {
target.transitionState(
BatterySaverState.ON,
InteractiveState.NON_INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
target.advanceClock(1);
target.drainBattery(2000);
- target.startCharging();
+ target.transitionState(
+ BatterySaverState.ON,
+ InteractiveState.NON_INTERACTIVE,
+ DozeState.NOT_DOZING,
+ PlugState.PLUGGED);
assertLog();
}
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 6d9b43a967c4..cd2c9230221c 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -252,6 +252,7 @@ public class RollbackUnitTest {
verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(111));
verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(222));
verify(mMockDataHelper, never()).destroyApexDeSnapshots(anyInt());
+ verify(mMockDataHelper, never()).destroyApexCeSnapshots(anyInt(), anyInt());
assertThat(rollback.isDeleted()).isTrue();
}
@@ -273,6 +274,8 @@ public class RollbackUnitTest {
verify(mMockDataHelper, never())
.destroyAppDataSnapshot(anyInt(), pkgRollbackInfoFor(PKG_2), anyInt());
verify(mMockDataHelper).destroyApexDeSnapshots(123);
+ verify(mMockDataHelper).destroyApexCeSnapshots(111, 123);
+ verify(mMockDataHelper).destroyApexCeSnapshots(222, 123);
assertThat(rollback.isDeleted()).isTrue();
}
diff --git a/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java
index 72580a3b98c2..a787c321fc66 100644
--- a/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java
@@ -50,8 +50,7 @@ public final class IconsContentProviderTest {
final Drawable actual = Icon.createWithContentUri(uri).loadDrawable(context);
assertThat(actual).isNotNull();
- assertThat(IconsContentProvider.getBitmapData(actual))
- .isEqualTo(IconsContentProvider.getBitmapData(expected));
+ assertThat(IconsContentProvider.sameIcon(actual, expected)).isTrue();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index f0531f5166b9..9f59763cfa58 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -35,6 +35,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.os.HandlerThread;
import android.os.TimestampedValue;
+import android.util.IndentingPrintWriter;
import androidx.test.runner.AndroidJUnit4;
@@ -46,6 +47,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.PrintWriter;
+import java.io.StringWriter;
@RunWith(AndroidJUnit4.class)
public class TimeDetectorServiceTest {
@@ -177,7 +179,8 @@ public class TimeDetectorServiceTest {
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
.thenReturn(PackageManager.PERMISSION_GRANTED);
- mTimeDetectorService.dump(null, null, null);
+ PrintWriter pw = new PrintWriter(new StringWriter());
+ mTimeDetectorService.dump(null, pw, null);
verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
mStubbedTimeDetectorStrategy.verifyDumpCalled();
@@ -251,7 +254,7 @@ public class TimeDetectorServiceTest {
}
@Override
- public void dump(PrintWriter pw, String[] args) {
+ public void dump(IndentingPrintWriter pw, String[] args) {
mDumpCalled = true;
}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
index 5c6fe0fc4cad..2c4c4d0ee91f 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
@@ -43,13 +43,13 @@ import android.app.timezone.RulesManager;
import android.app.timezone.RulesState;
import android.os.ParcelFileDescriptor;
+import com.android.i18n.timezone.TzDataSetVersion;
import com.android.timezone.distro.DistroVersion;
import com.android.timezone.distro.StagedDistroOperation;
import com.android.timezone.distro.TimeZoneDistro;
import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
import libcore.io.IoUtils;
-import libcore.timezone.TzDataSetVersion;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
new file mode 100644
index 000000000000..dcf319058ca2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
+import android.app.timezonedetector.TimeZoneCapabilities;
+import android.app.timezonedetector.TimeZoneConfiguration;
+import android.util.IndentingPrintWriter;
+
+class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
+
+ private StrategyListener mListener;
+
+ // Fake state
+ private TimeZoneCapabilities mCapabilities;
+ private TimeZoneConfiguration mConfiguration;
+
+ // Call tracking.
+ private GeolocationTimeZoneSuggestion mLastGeolocationSuggestion;
+ private ManualTimeZoneSuggestion mLastManualSuggestion;
+ private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
+ private boolean mHandleAutoTimeZoneConfigChangedCalled;
+ private boolean mDumpCalled;
+
+ @Override
+ public void setStrategyListener(@NonNull StrategyListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
+ return mCapabilities;
+ }
+
+ @Override
+ public boolean updateConfiguration(
+ @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) {
+ assertNotNull(mConfiguration);
+ assertNotNull(configuration);
+
+ // Simulate the strategy's behavior: the new configuration will be the old configuration
+ // merged with the new.
+ TimeZoneConfiguration oldConfiguration = mConfiguration;
+ TimeZoneConfiguration newConfiguration =
+ new TimeZoneConfiguration.Builder(mConfiguration)
+ .mergeProperties(configuration)
+ .build();
+
+ if (newConfiguration.equals(oldConfiguration)) {
+ return false;
+ }
+ mConfiguration = newConfiguration;
+ mListener.onConfigurationChanged();
+ return true;
+ }
+
+ @Override
+ @NonNull
+ public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
+ return mConfiguration;
+ }
+
+ @Override
+ public void suggestGeolocationTimeZone(GeolocationTimeZoneSuggestion timeZoneSuggestion) {
+ mLastGeolocationSuggestion = timeZoneSuggestion;
+ }
+
+ @Override
+ public boolean suggestManualTimeZone(
+ @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
+ mLastManualSuggestion = timeZoneSuggestion;
+ return true;
+ }
+
+ @Override
+ public void suggestTelephonyTimeZone(
+ @NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) {
+ mLastTelephonySuggestion = timeZoneSuggestion;
+ }
+
+ @Override
+ public void handleAutoTimeZoneConfigChanged() {
+ mHandleAutoTimeZoneConfigChangedCalled = true;
+ }
+
+ @Override
+ public void addDumpable(Dumpable dumpable) {
+ // Stubbed
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ mDumpCalled = true;
+ }
+
+ void initializeConfiguration(TimeZoneConfiguration configuration) {
+ mConfiguration = configuration;
+ }
+
+ void initializeCapabilities(TimeZoneCapabilities capabilities) {
+ mCapabilities = capabilities;
+ }
+
+ void resetCallTracking() {
+ mLastGeolocationSuggestion = null;
+ mLastManualSuggestion = null;
+ mLastTelephonySuggestion = null;
+ mHandleAutoTimeZoneConfigChangedCalled = false;
+ mDumpCalled = false;
+ }
+
+ void verifySuggestGeolocationTimeZoneCalled(
+ GeolocationTimeZoneSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion, mLastGeolocationSuggestion);
+ }
+
+ void verifySuggestManualTimeZoneCalled(ManualTimeZoneSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion, mLastManualSuggestion);
+ }
+
+ void verifySuggestTelephonyTimeZoneCalled(TelephonyTimeZoneSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion, mLastTelephonySuggestion);
+ }
+
+ void verifyHandleAutoTimeZoneConfigChangedCalled() {
+ assertTrue(mHandleAutoTimeZoneConfigChangedCalled);
+ }
+
+ void verifyDumpCalled() {
+ assertTrue(mDumpCalled);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
new file mode 100644
index 000000000000..5870a70c49ae
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class GeolocationTimeZoneSuggestionTest {
+
+ private static final List<String> ARBITRARY_ZONE_IDS1 =
+ Collections.singletonList("Europe/London");
+ private static final List<String> ARBITRARY_ZONE_IDS2 =
+ Arrays.asList("Europe/Paris", "Europe/Brussels");
+
+ @Test
+ public void testEquals() {
+ GeolocationTimeZoneSuggestion one = new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS1);
+ assertEquals(one, one);
+
+ GeolocationTimeZoneSuggestion two = new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS1);
+ assertEquals(one, two);
+ assertEquals(two, one);
+
+ GeolocationTimeZoneSuggestion nullZone = new GeolocationTimeZoneSuggestion(null);
+ assertNotEquals(one, nullZone);
+ assertNotEquals(nullZone, one);
+ assertEquals(nullZone, nullZone);
+
+ GeolocationTimeZoneSuggestion three =
+ new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS2);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+
+ // DebugInfo must not be considered in equals().
+ one.addDebugInfo("Debug info 1");
+ two.addDebugInfo("Debug info 2");
+ assertEquals(one, two);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
new file mode 100644
index 000000000000..0e2c22756097
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.HandlerThread;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeZoneDetectorInternalImplTest {
+
+ private static final List<String> ARBITRARY_ZONE_IDS = Arrays.asList("TestZoneId");
+
+ private Context mMockContext;
+ private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
+
+ private TimeZoneDetectorInternalImpl mTimeZoneDetectorInternal;
+ private HandlerThread mHandlerThread;
+ private TestHandler mTestHandler;
+
+
+ @Before
+ public void setUp() {
+ mMockContext = mock(Context.class);
+
+ // Create a thread + handler for processing the work that the service posts.
+ mHandlerThread = new HandlerThread("TimeZoneDetectorInternalTest");
+ mHandlerThread.start();
+ mTestHandler = new TestHandler(mHandlerThread.getLooper());
+
+ mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy();
+
+ mTimeZoneDetectorInternal = new TimeZoneDetectorInternalImpl(
+ mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ mHandlerThread.join();
+ }
+
+ @Test
+ public void testSuggestGeolocationTimeZone() throws Exception {
+ GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
+ mTimeZoneDetectorInternal.suggestGeolocationTimeZone(timeZoneSuggestion);
+ mTestHandler.assertTotalMessagesEnqueued(1);
+
+ mTestHandler.waitForMessagesToBeProcessed();
+ mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
+ }
+
+ private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
+ return new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 42762e78e5b8..153634548c17 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -19,20 +19,20 @@ package com.android.server.timezonedetector;
import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
import android.app.timezonedetector.ITimeZoneConfigurationListener;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
@@ -51,6 +51,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.PrintWriter;
+import java.io.StringWriter;
@RunWith(AndroidJUnit4.class)
public class TimeZoneDetectorServiceTest {
@@ -161,32 +162,94 @@ public class TimeZoneDetectorServiceTest {
}
}
+ @Test(expected = SecurityException.class)
+ public void testRemoveConfigurationListener_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
+
+ ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
+ try {
+ mTimeZoneDetectorService.removeConfigurationListener(mockListener);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ }
+ }
+
@Test
public void testConfigurationChangeListenerRegistrationAndCallbacks() throws Exception {
- doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
-
TimeZoneConfiguration autoDetectDisabledConfiguration =
createTimeZoneConfiguration(false /* autoDetectionEnabled */);
+
mFakeTimeZoneDetectorStrategy.initializeConfiguration(autoDetectDisabledConfiguration);
IBinder mockListenerBinder = mock(IBinder.class);
ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
- when(mockListener.asBinder()).thenReturn(mockListenerBinder);
- mTimeZoneDetectorService.addConfigurationListener(mockListener);
+ {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+ when(mockListener.asBinder()).thenReturn(mockListenerBinder);
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
- anyString());
- verify(mockListenerBinder).linkToDeath(any(), eq(0));
+ mTimeZoneDetectorService.addConfigurationListener(mockListener);
+
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ verify(mockListener).asBinder();
+ verify(mockListenerBinder).linkToDeath(any(), anyInt());
+ verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
+ reset(mockListenerBinder, mockListener, mMockContext);
+ }
+
+ {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+ // Simulate the configuration being changed and verify the mockListener was notified.
+ TimeZoneConfiguration autoDetectEnabledConfiguration =
+ createTimeZoneConfiguration(true /* autoDetectionEnabled */);
+
+ mTimeZoneDetectorService.updateConfiguration(autoDetectEnabledConfiguration);
+
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ verify(mockListener).onChange(autoDetectEnabledConfiguration);
+ verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
+ reset(mockListenerBinder, mockListener, mMockContext);
+ }
+
+ {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+ when(mockListener.asBinder()).thenReturn(mockListenerBinder);
+ when(mockListenerBinder.unlinkToDeath(any(), anyInt())).thenReturn(true);
+
+ // Now remove the listener, change the config again, and verify the listener is not
+ // called.
+ mTimeZoneDetectorService.removeConfigurationListener(mockListener);
+
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ verify(mockListener).asBinder();
+ verify(mockListenerBinder).unlinkToDeath(any(), eq(0));
+ verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
+ reset(mockListenerBinder, mockListener, mMockContext);
+ }
- // Simulate the configuration being changed and verify the mockListener was notified.
- TimeZoneConfiguration autoDetectEnabledConfiguration =
- createTimeZoneConfiguration(true /* autoDetectionEnabled */);
- mFakeTimeZoneDetectorStrategy.updateConfiguration(
- ARBITRARY_USER_ID, autoDetectEnabledConfiguration);
+ {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
- verify(mockListener).onChange(autoDetectEnabledConfiguration);
+ mTimeZoneDetectorService.updateConfiguration(autoDetectDisabledConfiguration);
+
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ verify(mockListener, never()).onChange(any());
+ verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
+ reset(mockListenerBinder, mockListener, mMockContext);
+ }
}
@Test(expected = SecurityException.class)
@@ -275,7 +338,8 @@ public class TimeZoneDetectorServiceTest {
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
.thenReturn(PackageManager.PERMISSION_GRANTED);
- mTimeZoneDetectorService.dump(null, null, null);
+ PrintWriter pw = new PrintWriter(new StringWriter());
+ mTimeZoneDetectorService.dump(null, pw, null);
verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
mFakeTimeZoneDetectorStrategy.verifyDumpCalled();
@@ -283,14 +347,14 @@ public class TimeZoneDetectorServiceTest {
@Test
public void testAutoTimeZoneDetectionChanged() throws Exception {
- mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+ mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
mTestHandler.assertTotalMessagesEnqueued(1);
mTestHandler.waitForMessagesToBeProcessed();
mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
mFakeTimeZoneDetectorStrategy.resetCallTracking();
- mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+ mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
mTestHandler.assertTotalMessagesEnqueued(2);
mTestHandler.waitForMessagesToBeProcessed();
mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
@@ -322,112 +386,4 @@ public class TimeZoneDetectorServiceTest {
.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
.build();
}
-
-
- private static class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
-
- private StrategyListener mListener;
-
- // Fake state
- private TimeZoneCapabilities mCapabilities;
- private TimeZoneConfiguration mConfiguration;
-
- // Call tracking.
- private ManualTimeZoneSuggestion mLastManualSuggestion;
- private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
- private boolean mHandleAutoTimeZoneConfigChangedCalled;
- private boolean mDumpCalled;
-
- @Override
- public void setStrategyListener(@NonNull StrategyListener listener) {
- mListener = listener;
- }
-
- @Override
- public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
- return mCapabilities;
- }
-
- @Override
- public boolean updateConfiguration(
- @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) {
- assertNotNull(mConfiguration);
- assertNotNull(configuration);
-
- // Simulate the strategy's behavior: the new configuration will be the old configuration
- // merged with the new.
- TimeZoneConfiguration oldConfiguration = mConfiguration;
- TimeZoneConfiguration newConfiguration =
- new TimeZoneConfiguration.Builder(mConfiguration)
- .mergeProperties(configuration)
- .build();
-
- if (newConfiguration.equals(oldConfiguration)) {
- return false;
- }
- mConfiguration = newConfiguration;
- mListener.onConfigurationChanged();
- return true;
- }
-
- @Override
- @NonNull
- public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
- return mConfiguration;
- }
-
- @Override
- public boolean suggestManualTimeZone(
- @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
- mLastManualSuggestion = timeZoneSuggestion;
- return true;
- }
-
- @Override
- public void suggestTelephonyTimeZone(
- @NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) {
- mLastTelephonySuggestion = timeZoneSuggestion;
- }
-
- @Override
- public void handleAutoTimeZoneConfigChanged() {
- mHandleAutoTimeZoneConfigChangedCalled = true;
- }
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- mDumpCalled = true;
- }
-
- void initializeConfiguration(TimeZoneConfiguration configuration) {
- mConfiguration = configuration;
- }
-
- void initializeCapabilities(TimeZoneCapabilities capabilities) {
- mCapabilities = capabilities;
- }
-
- void resetCallTracking() {
- mLastManualSuggestion = null;
- mLastTelephonySuggestion = null;
- mHandleAutoTimeZoneConfigChangedCalled = false;
- mDumpCalled = false;
- }
-
- void verifySuggestManualTimeZoneCalled(ManualTimeZoneSuggestion expectedSuggestion) {
- assertEquals(expectedSuggestion, mLastManualSuggestion);
- }
-
- void verifySuggestTelephonyTimeZoneCalled(TelephonyTimeZoneSuggestion expectedSuggestion) {
- assertEquals(expectedSuggestion, mLastTelephonySuggestion);
- }
-
- void verifyHandleAutoTimeZoneConfigChangedCalled() {
- assertTrue(mHandleAutoTimeZoneConfigChangedCalled);
- }
-
- void verifyDumpCalled() {
- assertTrue(mDumpCalled);
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index b254d5706c18..68554451e43a 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -48,17 +48,20 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
import android.app.timezonedetector.TimeZoneCapabilities;
import android.app.timezonedetector.TimeZoneConfiguration;
+import android.util.IndentingPrintWriter;
import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
import org.junit.Before;
import org.junit.Test;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* White-box unit tests for {@link TimeZoneDetectorStrategyImpl}.
@@ -71,21 +74,23 @@ public class TimeZoneDetectorStrategyImplTest {
private static final int SLOT_INDEX1 = 10000;
private static final int SLOT_INDEX2 = 20000;
- // Suggestion test cases are ordered so that each successive one is of the same or higher score
+ // Telephony test cases are ordered so that each successive one is of the same or higher score
// than the previous.
- private static final SuggestionTestCase[] TEST_CASES = new SuggestionTestCase[] {
- newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+ private static final TelephonyTestCase[] TELEPHONY_TEST_CASES = new TelephonyTestCase[] {
+ newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, TELEPHONY_SCORE_LOW),
- newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET,
- TELEPHONY_SCORE_MEDIUM),
- newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET,
+ newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, TELEPHONY_SCORE_MEDIUM),
- newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH),
- newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
+ newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET,
+ QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, TELEPHONY_SCORE_MEDIUM),
+ newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE,
+ TELEPHONY_SCORE_HIGH),
+ newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
TELEPHONY_SCORE_HIGH),
- newTestCase(MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY,
+ newTelephonyTestCase(MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY,
QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, TELEPHONY_SCORE_HIGHEST),
- newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGHEST),
+ newTelephonyTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE,
+ TELEPHONY_SCORE_HIGHEST),
};
private static final TimeZoneConfiguration CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED =
@@ -114,7 +119,8 @@ public class TimeZoneDetectorStrategyImplTest {
@Test
public void testGetCapabilities() {
new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
TimeZoneCapabilities expectedCapabilities = mFakeCallback.getCapabilities(USER_ID);
assertEquals(expectedCapabilities, mTimeZoneDetectorStrategy.getCapabilities(USER_ID));
}
@@ -122,17 +128,19 @@ public class TimeZoneDetectorStrategyImplTest {
@Test
public void testGetConfiguration() {
new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
TimeZoneConfiguration expectedConfiguration = mFakeCallback.getConfiguration(USER_ID);
assertTrue(expectedConfiguration.isComplete());
assertEquals(expectedConfiguration, mTimeZoneDetectorStrategy.getConfiguration(USER_ID));
}
@Test
- public void testCapabilitiesTestInfra_owner() {
+ public void testCapabilitiesTestInfra_unrestricted() {
Script script = new Script();
- script.initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
@@ -140,7 +148,8 @@ public class TimeZoneDetectorStrategyImplTest {
assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone());
}
- script.initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
+ script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
@@ -150,10 +159,11 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testCapabilitiesTestInfra_nonOwner() {
+ public void testCapabilitiesTestInfra_restricted() {
Script script = new Script();
- script.initializeUser(USER_ID, UserCase.NON_OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ script.initializeUser(USER_ID, UserCase.RESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
@@ -161,7 +171,7 @@ public class TimeZoneDetectorStrategyImplTest {
assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
}
- script.initializeUser(USER_ID, UserCase.NON_OWNER,
+ script.initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
{
// Check the fake test infra is doing what is expected.
@@ -172,10 +182,10 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testCapabilitiesTestInfra_ownerAutoDetectNotSupported() {
+ public void testCapabilitiesTestInfra_autoDetectNotSupported() {
Script script = new Script();
- script.initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
{
// Check the fake test infra is doing what is expected.
@@ -184,7 +194,7 @@ public class TimeZoneDetectorStrategyImplTest {
assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
}
- script.initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
{
// Check the fake test infra is doing what is expected.
@@ -195,9 +205,10 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testUpdateConfiguration_owner() {
+ public void testUpdateConfiguration_unrestricted() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
// Set the configuration with auto detection enabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
@@ -220,9 +231,9 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testUpdateConfiguration_nonOwner() {
+ public void testUpdateConfiguration_restricted() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.NON_OWNER,
+ .initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
// Try to update the configuration with auto detection disabled.
@@ -239,9 +250,9 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testUpdateConfiguration_ownerAutoDetectNotSupported() {
+ public void testUpdateConfiguration_autoDetectNotSupported() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
// Try to update the configuration with auto detection disabled.
@@ -264,7 +275,8 @@ public class TimeZoneDetectorStrategyImplTest {
TelephonyTimeZoneSuggestion slotIndex2TimeZoneSuggestion =
createEmptySlotIndex2Suggestion();
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
script.simulateTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion)
@@ -300,13 +312,14 @@ public class TimeZoneDetectorStrategyImplTest {
public void testTelephonySuggestionsWhenTimeZoneUninitialized() {
assertTrue(TELEPHONY_SCORE_LOW < TELEPHONY_SCORE_USAGE_THRESHOLD);
assertTrue(TELEPHONY_SCORE_HIGH >= TELEPHONY_SCORE_USAGE_THRESHOLD);
- SuggestionTestCase testCase = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+ TelephonyTestCase testCase = newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, TELEPHONY_SCORE_LOW);
- SuggestionTestCase testCase2 = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+ TelephonyTestCase testCase2 = newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
// A low quality suggestions will not be taken: The device time zone setting is left
// uninitialized.
@@ -369,9 +382,10 @@ public class TimeZoneDetectorStrategyImplTest {
public void testTogglingAutoTimeZoneDetection() {
Script script = new Script();
- for (SuggestionTestCase testCase : TEST_CASES) {
+ for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
// Start with the device in a known state.
- script.initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+ script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
TelephonyTimeZoneSuggestion suggestion =
@@ -422,10 +436,11 @@ public class TimeZoneDetectorStrategyImplTest {
@Test
public void testTelephonySuggestionsSingleSlotId() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
- for (SuggestionTestCase testCase : TEST_CASES) {
+ for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
makeSlotIndex1SuggestionAndCheckState(script, testCase);
}
@@ -436,16 +451,16 @@ public class TimeZoneDetectorStrategyImplTest {
*/
// Each test case will have the same or lower score than the last.
- ArrayList<SuggestionTestCase> descendingCasesByScore =
- new ArrayList<>(Arrays.asList(TEST_CASES));
+ ArrayList<TelephonyTestCase> descendingCasesByScore =
+ new ArrayList<>(Arrays.asList(TELEPHONY_TEST_CASES));
Collections.reverse(descendingCasesByScore);
- for (SuggestionTestCase testCase : descendingCasesByScore) {
+ for (TelephonyTestCase testCase : descendingCasesByScore) {
makeSlotIndex1SuggestionAndCheckState(script, testCase);
}
}
- private void makeSlotIndex1SuggestionAndCheckState(Script script, SuggestionTestCase testCase) {
+ private void makeSlotIndex1SuggestionAndCheckState(Script script, TelephonyTestCase testCase) {
// Give the next suggestion a different zone from the currently set device time zone;
String currentZoneId = mFakeCallback.getDeviceTimeZone();
String suggestionZoneId =
@@ -476,7 +491,7 @@ public class TimeZoneDetectorStrategyImplTest {
* suggestion is of sufficient quality.
*/
@Test
- public void testMultipleSlotIndexSuggestionScoringAndSlotIndexBias() {
+ public void testTelephonySuggestionMultipleSlotIndexSuggestionScoringAndSlotIndexBias() {
String[] zoneIds = { "Europe/London", "Europe/Paris" };
TelephonyTimeZoneSuggestion emptySlotIndex1Suggestion = createEmptySlotIndex1Suggestion();
TelephonyTimeZoneSuggestion emptySlotIndex2Suggestion = createEmptySlotIndex2Suggestion();
@@ -488,7 +503,8 @@ public class TimeZoneDetectorStrategyImplTest {
TELEPHONY_SCORE_NONE);
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
// Initialize the latest suggestions as empty so we don't need to worry about nulls
// below for the first loop.
@@ -496,7 +512,7 @@ public class TimeZoneDetectorStrategyImplTest {
.simulateTelephonyTimeZoneSuggestion(emptySlotIndex2Suggestion)
.resetConfigurationTracking();
- for (SuggestionTestCase testCase : TEST_CASES) {
+ for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
TelephonyTimeZoneSuggestion zoneSlotIndex1Suggestion =
testCase.createSuggestion(SLOT_INDEX1, zoneIds[0]);
TelephonyTimeZoneSuggestion zoneSlotIndex2Suggestion =
@@ -572,13 +588,13 @@ public class TimeZoneDetectorStrategyImplTest {
* knows the current setting.
*/
@Test
- public void testTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() {
+ public void testTelephonySuggestionTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
- SuggestionTestCase testCase =
- newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
- TELEPHONY_SCORE_HIGH);
+ TelephonyTestCase testCase = newTelephonyTestCase(
+ MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
TelephonyTimeZoneSuggestion losAngelesSuggestion =
testCase.createSuggestion(SLOT_INDEX1, "America/Los_Angeles");
TelephonyTimeZoneSuggestion newYorkSuggestion =
@@ -609,21 +625,22 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testManualSuggestion_owner_simulateAutoTimeZoneEnabled() {
+ public void testManualSuggestion_unrestricted_simulateAutoTimeZoneEnabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Auto time zone detection is enabled so the manual suggestion should be ignored.
script.simulateManualTimeZoneSuggestion(
USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */)
- .verifyTimeZoneNotChanged();
+ .verifyTimeZoneNotChanged();
}
@Test
- public void testManualSuggestion_nonOwner_simulateAutoTimeZoneEnabled() {
+ public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.NON_OWNER,
+ .initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
@@ -634,9 +651,9 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testManualSuggestion_ownerAutoDetectNotSupported_simulateAutoTimeZoneEnabled() {
+ public void testManualSuggestion_autoDetectNotSupported_simulateAutoTimeZoneEnabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
@@ -648,9 +665,10 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testManualSuggestion_owner_autoTimeZoneDetectionDisabled() {
+ public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Auto time zone detection is disabled so the manual suggestion should be used.
@@ -661,13 +679,13 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testManualSuggestion_nonOwner_autoTimeZoneDetectionDisabled() {
+ public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.NON_OWNER,
+ .initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
- // Only owners have the capability.
+ // Restricted users do not have the capability.
ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
script.simulateManualTimeZoneSuggestion(
USER_ID, manualSuggestion, false /* expectedResult */)
@@ -675,19 +693,42 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Test
- public void testManualSuggestion_ownerAutoDetectNotSupported_autoTimeZoneDetectionDisabled() {
+ public void testManualSuggestion_autoDetectNotSupported_autoTimeZoneDetectionDisabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
- // Only owners have the capability.
+ // Unrestricted users have the capability.
ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
script.simulateManualTimeZoneSuggestion(
USER_ID, manualSuggestion, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(manualSuggestion);
}
+ @Test
+ public void testAddDumpable() {
+ new Script()
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+ .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+ AtomicBoolean dumpCalled = new AtomicBoolean(false);
+ class FakeDumpable implements Dumpable {
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ dumpCalled.set(true);
+ }
+ }
+
+ mTimeZoneDetectorStrategy.addDumpable(new FakeDumpable());
+ IndentingPrintWriter ipw = new IndentingPrintWriter(new StringWriter());
+ String[] args = {"ArgOne", "ArgTwo"};
+ mTimeZoneDetectorStrategy.dump(ipw, args);
+
+ assertTrue(dumpCalled.get());
+ }
+
private static ManualTimeZoneSuggestion createManualSuggestion(String zoneId) {
return new ManualTimeZoneSuggestion(zoneId);
}
@@ -885,12 +926,15 @@ public class TimeZoneDetectorStrategyImplTest {
/** Simulated user test cases. */
enum UserCase {
- /** A catch-all for users that can set time zone config. */
- OWNER,
- /** A catch-all for users that can't set time zone config. */
- NON_OWNER,
- /** Owner, but auto tz detection is not supported on the device. */
- OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ /** A catch-all for users that can set auto time zone config. */
+ UNRESTRICTED,
+ /** A catch-all for users that can't set auto time zone config. */
+ RESTRICTED,
+ /**
+ * Like {@link #UNRESTRICTED}, but auto tz detection is not
+ * supported on the device.
+ */
+ AUTO_DETECT_NOT_SUPPORTED,
}
/**
@@ -900,7 +944,7 @@ public class TimeZoneDetectorStrategyImplTest {
private static TimeZoneCapabilities createCapabilities(
int userId, UserCase userRole, TimeZoneConfiguration configuration) {
switch (userRole) {
- case OWNER: {
+ case UNRESTRICTED: {
int suggestManualTimeZoneCapability = configuration.isAutoDetectionEnabled()
? CAPABILITY_NOT_APPLICABLE : CAPABILITY_POSSESSED;
return new TimeZoneCapabilities.Builder(userId)
@@ -908,14 +952,14 @@ public class TimeZoneDetectorStrategyImplTest {
.setSuggestManualTimeZone(suggestManualTimeZoneCapability)
.build();
}
- case NON_OWNER: {
+ case RESTRICTED: {
return new TimeZoneCapabilities.Builder(userId)
.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED)
.build();
}
- case OWNER_AUTO_DETECT_NOT_SUPPORTED: {
+ case AUTO_DETECT_NOT_SUPPORTED: {
return new TimeZoneCapabilities.Builder(userId)
.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED)
.setSuggestManualTimeZone(CAPABILITY_POSSESSED)
@@ -1031,12 +1075,12 @@ public class TimeZoneDetectorStrategyImplTest {
}
}
- private static class SuggestionTestCase {
+ private static class TelephonyTestCase {
public final int matchType;
public final int quality;
public final int expectedScore;
- SuggestionTestCase(int matchType, int quality, int expectedScore) {
+ TelephonyTestCase(int matchType, int quality, int expectedScore) {
this.matchType = matchType;
this.quality = quality;
this.expectedScore = expectedScore;
@@ -1051,9 +1095,9 @@ public class TimeZoneDetectorStrategyImplTest {
}
}
- private static SuggestionTestCase newTestCase(
+ private static TelephonyTestCase newTelephonyTestCase(
@MatchType int matchType, @Quality int quality, int expectedScore) {
- return new SuggestionTestCase(matchType, quality, expectedScore);
+ return new TelephonyTestCase(matchType, quality, expectedScore);
}
private static class MockStrategyListener implements TimeZoneDetectorStrategy.StrategyListener {
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 62b6a65cc6cb..614949c91b9a 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -43,11 +43,19 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.content.ClipData;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.net.Uri;
+import android.os.UserHandle;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +70,12 @@ public class UriGrantsManagerServiceTest {
private UriGrantsMockContext mContext;
private UriGrantsManagerInternal mService;
+ // we expect the following only during grant if a grant is expected
+ private void verifyNoVisibilityGrant() {
+ verify(mContext.mPmInternal, never())
+ .grantImplicitAccess(anyInt(), any(), anyInt(), anyInt(), anyBoolean());
+ }
+
@Before
public void setUp() throws Exception {
mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext());
@@ -83,6 +97,7 @@ public class UriGrantsManagerServiceTest {
assertEquals(UID_PRIMARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
@@ -100,6 +115,7 @@ public class UriGrantsManagerServiceTest {
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
@@ -111,6 +127,8 @@ public class UriGrantsManagerServiceTest {
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
intent, UID_PRIMARY_PUBLIC, PKG_SOCIAL, USER_PRIMARY);
assertNull(needed);
+ verify(mContext.mPmInternal).grantImplicitAccess(eq(USER_PRIMARY), isNull(), eq(
+ UserHandle.getAppId(UID_PRIMARY_SOCIAL)), eq(UID_PRIMARY_PUBLIC), eq(false));
}
/**
@@ -128,6 +146,7 @@ public class UriGrantsManagerServiceTest {
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 1a04d2ff8c29..4dec7a1a0ab9 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -218,6 +218,10 @@ public class AppStandbyControllerTests {
}
@Override
+ void updatePowerWhitelistCache() {
+ }
+
+ @Override
boolean isRestrictedBucketEnabled() {
return mIsRestrictedBucketEnabled;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ef4d5db2f32f..10976882048a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -385,7 +385,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
MockitoAnnotations.initMocks(this);
DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
- when(deviceIdleInternal.getNotificationWhitelistDuration()).thenReturn(3000L);
+ when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
@@ -1339,14 +1339,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @Ignore
public void testPostCancelPostNotifiesListeners() throws Exception {
// WHEN a notification is posted
final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
sbn.getNotification(), sbn.getUserId());
+ Thread.sleep(1); // make sure the system clock advances before the next step
// THEN it is canceled
mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
+ Thread.sleep(1); // here too
// THEN it is posted again (before the cancel has a chance to finish)
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
sbn.getNotification(), sbn.getUserId());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index e10dfeedae56..0be1bf3fe5c2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -2686,6 +2686,96 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testLockChannelsForOEM_onlyGivenPkg_appDoesNotExistYet() {
+ mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, 30, b, false, false);
+
+ assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+ .isImportanceLockedByOEM());
+ assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 30, b.getId(), false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
+ public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet() {
+ mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
+
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+ NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+ // different uids, same package
+ mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+ mHelper.createNotificationChannel(PKG_O, 3, b, false, false);
+ mHelper.createNotificationChannel(PKG_O, 30, c, true, true);
+
+ assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+ .isImportanceLockedByOEM());
+ assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
+ .isImportanceLockedByOEM());
+ assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
+ public void testLockChannelsForOEM_onlyGivenPkg_appDoesNotExistYet_restoreData()
+ throws Exception {
+ mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
+ + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
+ + "</package>"
+ + "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1 + "\" >\n"
+ + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
+ + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, "a", false)
+ .isImportanceLockedByOEM());
+ assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "b", false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
+ public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet_restoreData()
+ throws Exception {
+ mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + 3 + "\" >\n"
+ + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
+ + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
+ + "</package>"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + 30 + "\" >\n"
+ + "<channel id=\"c\" name=\"c\" importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertFalse(mHelper.getNotificationChannel(PKG_O, 3, "a", false)
+ .isImportanceLockedByOEM());
+ assertTrue(mHelper.getNotificationChannel(PKG_O, 3, "b", false)
+ .isImportanceLockedByOEM());
+ assertTrue(mHelper.getNotificationChannel(PKG_O, 30, "c", false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
public void testLockChannelsForOEM_channelSpecific_clearData() {
NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
mHelper.getImportance(PKG_O, UID_O);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
index c700a090fa2e..eca71b69ec0b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -45,6 +45,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -85,14 +86,19 @@ public class ShortcutHelperTest extends UiServiceTestCase {
mShortcutHelper = new ShortcutHelper(
mLauncherApps, mShortcutListener, mShortcutServiceInternal);
- when(mNr.getKey()).thenReturn(KEY);
- when(mNr.getSbn()).thenReturn(mSbn);
when(mSbn.getPackageName()).thenReturn(PKG);
- when(mNr.getNotification()).thenReturn(mNotif);
- when(mNr.getShortcutInfo()).thenReturn(mShortcutInfo);
when(mShortcutInfo.getId()).thenReturn(SHORTCUT_ID);
when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata);
when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
+
+ setUpMockNotificationRecord(mNr, KEY);
+ }
+
+ private void setUpMockNotificationRecord(NotificationRecord mockRecord, String key) {
+ when(mockRecord.getKey()).thenReturn(key);
+ when(mockRecord.getSbn()).thenReturn(mSbn);
+ when(mockRecord.getNotification()).thenReturn(mNotif);
+ when(mockRecord.getShortcutInfo()).thenReturn(mShortcutInfo);
}
private LauncherApps.Callback addShortcutBubbleAndVerifyListener() {
@@ -159,9 +165,31 @@ public class ShortcutHelperTest extends UiServiceTestCase {
// First set it up to listen
addShortcutBubbleAndVerifyListener();
- // Clear out shortcutId
- when(mNr.getShortcutInfo()).thenReturn(null);
- mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+ NotificationRecord validMock1 = Mockito.mock(NotificationRecord.class);
+ setUpMockNotificationRecord(validMock1, "KEY1");
+
+ NotificationRecord validMock2 = Mockito.mock(NotificationRecord.class);
+ setUpMockNotificationRecord(validMock2, "KEY2");
+
+ NotificationRecord validMock3 = Mockito.mock(NotificationRecord.class);
+ setUpMockNotificationRecord(validMock3, "KEY3");
+
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(validMock1,
+ false /* removed */,
+ null /* handler */);
+
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(validMock2,
+ false /* removed */,
+ null /* handler */);
+
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(validMock3,
+ false /* removed */,
+ null /* handler */);
+
+ // Clear out shortcutId of the bubble in the middle, to double check that we don't hit a
+ // concurrent modification exception (removing the last bubble would sidestep that check).
+ when(validMock2.getShortcutInfo()).thenReturn(null);
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(validMock2,
false /* removed */,
null /* handler */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index e3bb1b6ca9f3..f69d7c332382 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -31,6 +31,7 @@ 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.verify;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -65,9 +66,9 @@ public class ActivityDisplayTests extends ActivityTestsBase {
// Create a stack at bottom.
final TaskDisplayArea taskDisplayAreas =
mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea();
- final ActivityStack stack =
+ final Task stack =
new StackBuilder(mRootWindowContainer).setOnTop(!ON_TOP).build();
- final ActivityStack prevFocusedStack = taskDisplayAreas.getFocusedStack();
+ final Task prevFocusedStack = taskDisplayAreas.getFocusedStack();
stack.moveToFront("moveStackToFront");
// After moving the stack to front, the previous focused should be the last focused.
@@ -86,7 +87,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
@Test
public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
// Create a pinned stack and move to front.
- final ActivityStack pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea()
+ final Task pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
final Task pinnedTask = new TaskBuilder(mService.mStackSupervisor)
.setStack(pinnedStack).build();
@@ -98,7 +99,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
assertTrue(pinnedStack.isFocusedStackOnDisplay());
// Create a fullscreen stack and move to front.
- final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
+ final Task fullscreenStack = createFullscreenStackWithSimpleActivityAt(
mRootWindowContainer.getDefaultDisplay());
fullscreenStack.moveToFront("moveFullscreenStackToFront");
@@ -113,9 +114,9 @@ public class ActivityDisplayTests extends ActivityTestsBase {
@Test
public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
// Create a display which only contains 2 stacks.
- final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
- final ActivityStack stack1 = createFullscreenStackWithSimpleActivityAt(display);
- final ActivityStack stack2 = createFullscreenStackWithSimpleActivityAt(display);
+ final DisplayContent display = addNewDisplayContentAt(POSITION_TOP);
+ final Task stack1 = createFullscreenStackWithSimpleActivityAt(display);
+ final Task stack2 = createFullscreenStackWithSimpleActivityAt(display);
// Put stack1 and stack2 on top.
stack1.moveToFront("moveStack1ToFront");
@@ -143,11 +144,11 @@ public class ActivityDisplayTests extends ActivityTestsBase {
doReturn(false).when(display).shouldDestroyContentOnRemove();
// Put home stack on the display.
- final ActivityStack homeStack = new StackBuilder(mRootWindowContainer)
+ final Task homeStack = new StackBuilder(mRootWindowContainer)
.setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build();
// Put a finishing standard activity which will be reparented.
- final ActivityStack stack = createFullscreenStackWithSimpleActivityAt(display);
+ final Task stack = createFullscreenStackWithSimpleActivityAt(display);
stack.topRunningActivity().makeFinishingLocked();
clearInvocations(homeStack);
@@ -158,8 +159,8 @@ public class ActivityDisplayTests extends ActivityTestsBase {
verify(homeStack, never()).resumeTopActivityUncheckedLocked(any(), any());
}
- private ActivityStack createFullscreenStackWithSimpleActivityAt(DisplayContent display) {
- final ActivityStack fullscreenStack = display.getDefaultTaskDisplayArea().createStack(
+ private Task createFullscreenStackWithSimpleActivityAt(DisplayContent display) {
+ final Task fullscreenStack = display.getDefaultTaskDisplayArea().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
final Task fullscreenTask = new TaskBuilder(mService.mStackSupervisor)
.setStack(fullscreenStack).build();
@@ -174,11 +175,11 @@ public class ActivityDisplayTests extends ActivityTestsBase {
public void testTopRunningActivity() {
final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
+ final Task stack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord activity = stack.getTopNonFinishingActivity();
// Create empty stack on top.
- final ActivityStack emptyStack =
+ final Task emptyStack =
new StackBuilder(mRootWindowContainer).setCreateActivity(false).build();
// Make sure the top running activity is not affected when keyguard is not locked.
@@ -223,33 +224,35 @@ public class ActivityDisplayTests extends ActivityTestsBase {
@Test
public void testAlwaysOnTopStackLocation() {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final ActivityStack alwaysOnTopStack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM,
+ final Task alwaysOnTopStack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(alwaysOnTopStack).build();
alwaysOnTopStack.setAlwaysOnTop(true);
- taskDisplayArea.positionStackAtTop(alwaysOnTopStack, false /* includingParents */);
+ taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopStack,
+ false /* includingParents */);
assertTrue(alwaysOnTopStack.isAlwaysOnTop());
// Ensure always on top state is synced to the children of the stack.
assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
assertEquals(alwaysOnTopStack, taskDisplayArea.getTopStack());
- final ActivityStack pinnedStack = taskDisplayArea.createStack(
+ final Task pinnedStack = taskDisplayArea.createStack(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(pinnedStack, taskDisplayArea.getRootPinnedTask());
assertEquals(pinnedStack, taskDisplayArea.getTopStack());
- final ActivityStack anotherAlwaysOnTopStack = taskDisplayArea.createStack(
+ final Task anotherAlwaysOnTopStack = taskDisplayArea.createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
anotherAlwaysOnTopStack.setAlwaysOnTop(true);
- taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+ taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
+ false /* includingParents */);
assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
int topPosition = taskDisplayArea.getStackCount() - 1;
// Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
// existing alwaysOnTop stack.
assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1));
- final ActivityStack nonAlwaysOnTopStack = taskDisplayArea.createStack(
+ final Task nonAlwaysOnTopStack = taskDisplayArea.createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(taskDisplayArea, nonAlwaysOnTopStack.getDisplayArea());
topPosition = taskDisplayArea.getStackCount() - 1;
@@ -258,7 +261,8 @@ public class ActivityDisplayTests extends ActivityTestsBase {
assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 3));
anotherAlwaysOnTopStack.setAlwaysOnTop(false);
- taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+ taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
+ false /* includingParents */);
assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
// Ensure, when always on top is turned off for a stack, the stack is put just below all
// other always on top stacks.
@@ -273,7 +277,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1));
- final ActivityStack dreamStack = taskDisplayArea.createStack(
+ final Task dreamStack = taskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */);
assertEquals(taskDisplayArea, dreamStack.getDisplayArea());
assertTrue(dreamStack.isAlwaysOnTop());
@@ -282,7 +286,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
assertEquals(dreamStack, taskDisplayArea.getTopStack());
assertEquals(pinnedStack, taskDisplayArea.getStackAt(topPosition - 1));
- final ActivityStack assistStack = taskDisplayArea.createStack(
+ final Task assistStack = taskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
assertEquals(taskDisplayArea, assistStack.getDisplayArea());
assertFalse(assistStack.isAlwaysOnTop());
@@ -310,13 +314,13 @@ public class ActivityDisplayTests extends ActivityTestsBase {
private void removeStackTests(Runnable runnable) {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final ActivityStack stack1 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ final Task stack1 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
- final ActivityStack stack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ final Task stack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
- final ActivityStack stack3 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ final Task stack3 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
- final ActivityStack stack4 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ final Task stack4 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
final Task task1 = new TaskBuilder(mService.mStackSupervisor).setStack(stack1).build();
final Task task2 = new TaskBuilder(mService.mStackSupervisor).setStack(stack2).build();
@@ -325,7 +329,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
// Reordering stacks while removing stacks.
doAnswer(invocation -> {
- taskDisplayArea.positionStackAtTop(stack3, false);
+ taskDisplayArea.positionChildAt(POSITION_TOP, stack3, false /*includingParents*/);
return true;
}).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index e1ce431fc97c..feac6dbe1051 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -341,7 +341,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
public void testConsecutiveLaunchOnDifferentDisplay() {
onActivityLaunched(mTopActivity);
- final ActivityStack stack = new StackBuilder(mRootWindowContainer)
+ final Task stack = new StackBuilder(mRootWindowContainer)
.setDisplay(addNewDisplayContentAt(DisplayContent.POSITION_BOTTOM))
.setCreateActivity(false)
.build();
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 668f04785bbc..e45ced64c5b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
@@ -43,19 +45,19 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
-import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.Task.ActivityState.DESTROYED;
+import static com.android.server.wm.Task.ActivityState.DESTROYING;
+import static com.android.server.wm.Task.ActivityState.FINISHING;
+import static com.android.server.wm.Task.ActivityState.INITIALIZING;
+import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.Task.ActivityState.PAUSING;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.Task.ActivityState.STARTED;
+import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.Task.ActivityState.STOPPING;
+import static com.android.server.wm.Task.STACK_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.google.common.truth.Truth.assertThat;
@@ -65,9 +67,13 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import android.app.ActivityManager.TaskSnapshot;
@@ -100,7 +106,7 @@ import android.view.WindowManagerGlobal;
import androidx.test.filters.MediumTest;
import com.android.internal.R;
-import com.android.server.wm.ActivityStack.ActivityState;
+import com.android.server.wm.Task.ActivityState;
import org.junit.Before;
import org.junit.Test;
@@ -117,7 +123,7 @@ import org.mockito.invocation.InvocationOnMock;
@Presubmit
@RunWith(WindowTestRunner.class)
public class ActivityRecordTests extends ActivityTestsBase {
- private ActivityStack mStack;
+ private Task mStack;
private Task mTask;
private ActivityRecord mActivity;
@@ -127,8 +133,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
mTask = mStack.getBottomMostTask();
mActivity = mTask.getTopNonFinishingActivity();
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
+ setBooted(mService);
}
@Test
@@ -287,7 +292,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testSetsRelaunchReason_NotDragResizing() {
- mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+ mActivity.setState(Task.ActivityState.RESUMED, "Testing");
mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -310,7 +315,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testSetsRelaunchReason_DragResizing() {
- mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+ mActivity.setState(Task.ActivityState.RESUMED, "Testing");
mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -335,7 +340,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testSetsRelaunchReason_NonResizeConfigChanges() {
- mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+ mActivity.setState(Task.ActivityState.RESUMED, "Testing");
mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -361,7 +366,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
.setTask(mTask)
.setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
- mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+ mActivity.setState(Task.ActivityState.RESUMED, "Testing");
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
@@ -406,7 +411,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void ignoreRequestedOrientationInFreeformWindows() {
- mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
final Rect stableRect = new Rect();
mStack.getDisplay().mDisplayContent.getStableRect(stableRect);
@@ -484,7 +489,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testShouldMakeActive_deferredResume() {
- mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
+ mActivity.setState(Task.ActivityState.STOPPED, "Testing");
mSupervisor.beginDeferResume();
assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */));
@@ -498,14 +503,14 @@ public class ActivityRecordTests extends ActivityTestsBase {
ActivityRecord finishingActivity = new ActivityBuilder(mService).setTask(mTask).build();
finishingActivity.finishing = true;
ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
+ mActivity.setState(Task.ActivityState.STOPPED, "Testing");
assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */));
}
@Test
public void testShouldResume_stackVisibility() {
- mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
+ mActivity.setState(Task.ActivityState.STOPPED, "Testing");
spyOn(mStack);
doReturn(STACK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null);
@@ -520,7 +525,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testShouldResumeOrPauseWithResults() {
- mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
+ mActivity.setState(Task.ActivityState.STOPPED, "Testing");
spyOn(mStack);
ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
@@ -539,9 +544,9 @@ public class ActivityRecordTests extends ActivityTestsBase {
.setLaunchTaskBehind(true)
.setConfigChanges(CONFIG_ORIENTATION)
.build();
- mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
+ mActivity.setState(Task.ActivityState.STOPPED, "Testing");
- final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
+ final Task stack = new StackBuilder(mRootWindowContainer).build();
try {
doReturn(false).when(stack).isTranslucent(any());
assertFalse(mStack.shouldBeVisible(null /* starting */));
@@ -580,7 +585,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
public void testShouldStartWhenMakeClientActive() {
ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
topActivity.setOccludesParent(false);
- mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
+ mActivity.setState(Task.ActivityState.STOPPED, "Testing");
mActivity.setVisibility(true);
mActivity.makeActiveIfNeeded(null /* activeActivity */);
assertEquals(STARTED, mActivity.getState());
@@ -749,14 +754,14 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testFinishActivityIfPossible_adjustStackOrder() {
// Prepare the stacks with order (top to bottom): mStack, stack1, stack2.
- final ActivityStack stack1 = new StackBuilder(mRootWindowContainer).build();
+ final Task stack1 = new StackBuilder(mRootWindowContainer).build();
mStack.moveToFront("test");
// The stack2 is needed here for moving back to simulate the
// {@link DisplayContent#mPreferredTopFocusableStack} is cleared, so
// {@link DisplayContent#getFocusedStack} will rely on the order of focusable-and-visible
// stacks. Then when mActivity is finishing, its stack will be invisible (no running
// activities in the stack) that is the key condition to verify.
- final ActivityStack stack2 = new StackBuilder(mRootWindowContainer).build();
+ final Task stack2 = new StackBuilder(mRootWindowContainer).build();
stack2.moveToBack("test", stack2.getBottomMostTask());
assertTrue(mStack.isTopStackInDisplayArea());
@@ -782,7 +787,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
.setCreateTask(true)
.setStack(mStack)
.build();
- ActivityStack topRootableTask = (ActivityStack) topActivity.getTask();
+ Task topRootableTask = topActivity.getTask();
topRootableTask.moveToFront("test");
assertTrue(mStack.isTopStackInDisplayArea());
@@ -802,7 +807,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
public void testFinishActivityIfPossible_PreferredTopStackChanged() {
final ActivityRecord topActivityOnNonTopDisplay =
createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
- ActivityStack topRootableTask = topActivityOnNonTopDisplay.getRootTask();
+ Task topRootableTask = topActivityOnNonTopDisplay.getRootTask();
topRootableTask.moveToFront("test");
assertTrue(topRootableTask.isTopStackInDisplayArea());
assertEquals(topRootableTask, topActivityOnNonTopDisplay.getDisplayArea()
@@ -966,7 +971,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Simulates that {@code currentTop} starts an existing activity from background (so its
// state is stopped) and the starting flow just goes to place it at top.
- final ActivityStack nextStack = new StackBuilder(mRootWindowContainer).build();
+ final Task nextStack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord nextTop = nextStack.getTopNonFinishingActivity();
nextTop.setState(STOPPED, "test");
@@ -1088,7 +1093,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Add another stack to become focused and make the activity there visible. This way it
// simulates finishing in non-focused stack in split-screen.
- final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
+ final Task stack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord focusedActivity = stack.getTopMostActivity();
focusedActivity.nowVisible = true;
focusedActivity.mVisibleRequested = true;
@@ -1194,7 +1199,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
// Empty the home stack.
- final ActivityStack homeStack = mActivity.getDisplayArea().getRootHomeTask();
+ final Task homeStack = mActivity.getDisplayArea().getRootHomeTask();
homeStack.forAllLeafTasks((t) -> {
homeStack.removeChild(t, "test");
}, true /* traverseTopToBottom */);
@@ -1220,7 +1225,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() {
// Empty the home stack.
- final ActivityStack homeStack = mActivity.getDisplayArea().getRootHomeTask();
+ final Task homeStack = mActivity.getDisplayArea().getRootHomeTask();
homeStack.forAllLeafTasks((t) -> {
homeStack.removeChild(t, "test");
}, true /* traverseTopToBottom */);
@@ -1320,7 +1325,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testRemoveFromHistory() {
- final ActivityStack stack = mActivity.getRootTask();
+ final Task stack = mActivity.getRootTask();
final Task task = mActivity.getTask();
mActivity.removeFromHistory("test");
@@ -1329,7 +1334,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
assertNull(mActivity.app);
assertNull(mActivity.getTask());
assertEquals(0, task.getChildCount());
- assertEquals(task.getStack(), task);
+ assertEquals(task.getRootTask(), task);
assertEquals(0, stack.getChildCount());
}
@@ -1535,7 +1540,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
* Sets orientation without notifying the parent to simulate that the display has not applied
* the requested orientation yet.
*/
- private static void setRotatedScreenOrientationSilently(ActivityRecord r) {
+ static void setRotatedScreenOrientationSilently(ActivityRecord r) {
final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT
? SCREEN_ORIENTATION_LANDSCAPE
: SCREEN_ORIENTATION_PORTRAIT;
@@ -1571,7 +1576,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Create a new task with custom config to reparent the activity to.
final Task newTask =
- new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build();
+ new TaskBuilder(mSupervisor).setStack(initialTask.getRootTask()).build();
final Configuration newConfig = newTask.getConfiguration();
newConfig.densityDpi += 100;
newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -1603,7 +1608,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Create a new task with custom config to reparent the second activity to.
final Task newTask =
- new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build();
+ new TaskBuilder(mSupervisor).setStack(initialTask.getRootTask()).build();
final Configuration newConfig = newTask.getConfiguration();
newConfig.densityDpi += 100;
newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -1658,6 +1663,26 @@ public class ActivityRecordTests extends ActivityTestsBase {
.diff(wpc.getRequestedOverrideConfiguration()));
}
+ @Test
+ public void testCanTurnScreenOn() {
+ mStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ doReturn(true).when(mStack).checkKeyguardVisibility(
+ same(mActivity), eq(true) /* shouldBeVisible */, anyBoolean());
+ doReturn(true).when(mActivity).getTurnScreenOnFlag();
+
+ assertTrue(mActivity.canTurnScreenOn());
+ }
+
+ @Test
+ public void testFreeformWindowCantTurnScreenOn() {
+ mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ doReturn(true).when(mStack).checkKeyguardVisibility(
+ same(mActivity), eq(true) /* shouldBeVisible */, anyBoolean());
+ doReturn(true).when(mActivity).getTurnScreenOnFlag();
+
+ assertFalse(mActivity.canTurnScreenOn());
+ }
+
/**
* Creates an activity on display. For non-default display request it will also create a new
* display with custom DisplayInfo.
@@ -1671,7 +1696,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
display = new TestDisplayContent.Builder(mService, 2000, 1000).setDensityDpi(300)
.setPosition(DisplayContent.POSITION_TOP).build();
}
- final ActivityStack stack = display.getDefaultTaskDisplayArea()
+ final Task stack = display.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 5c6906cfa942..197c89a2d479 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -56,7 +56,7 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(WindowTestRunner.class)
public class ActivityStackSupervisorTests extends ActivityTestsBase {
- private ActivityStack mFullscreenStack;
+ private Task mFullscreenStack;
@Before
public void setUp() throws Exception {
@@ -113,7 +113,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
public void testHandleNonResizableTaskOnSecondaryDisplay() {
// Create an unresizable task on secondary display.
final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
- final ActivityStack stack = new StackBuilder(mRootWindowContainer)
+ final Task stack = new StackBuilder(mRootWindowContainer)
.setDisplay(newDisplay).build();
final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
final Task task = unresizableActivity.getTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 37882bb2ba76..775df74f88e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -34,20 +34,21 @@ 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.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.Task.ActivityState.DESTROYING;
+import static com.android.server.wm.Task.ActivityState.FINISHING;
+import static com.android.server.wm.Task.ActivityState.PAUSING;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.wm.Task.STACK_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.TaskDisplayArea.getStackAbove;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -90,7 +91,7 @@ import java.util.function.Consumer;
@RunWith(WindowTestRunner.class)
public class ActivityStackTests extends ActivityTestsBase {
private TaskDisplayArea mDefaultTaskDisplayArea;
- private ActivityStack mStack;
+ private Task mStack;
private Task mTask;
@Before
@@ -119,7 +120,7 @@ public class ActivityStackTests extends ActivityTestsBase {
r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
assertEquals(r, mStack.getResumedActivity());
- final ActivityStack destStack = mDefaultTaskDisplayArea.createStack(
+ final Task destStack = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mTask.reparent(destStack, true /* toTop */, Task.REPARENT_KEEP_STACK_AT_FRONT,
@@ -137,7 +138,7 @@ public class ActivityStackTests extends ActivityTestsBase {
r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
assertEquals(r, mStack.getResumedActivity());
- final ActivityStack destStack = mDefaultTaskDisplayArea.createStack(
+ final Task destStack = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_STACK_TO_FRONT, false, false,
"testResumedActivityFromActivityReparenting");
@@ -153,7 +154,7 @@ public class ActivityStackTests extends ActivityTestsBase {
organizer.setMoveToSecondaryOnEnter(false);
// Create primary splitscreen stack.
- final ActivityStack primarySplitScreen = mDefaultTaskDisplayArea.createStack(
+ final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Assert windowing mode.
@@ -178,10 +179,10 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testMoveToPrimarySplitScreenThenMoveToBack() {
TestSplitOrganizer organizer = new TestSplitOrganizer(mService);
// This time, start with a fullscreen activitystack
- final ActivityStack primarySplitScreen = mDefaultTaskDisplayArea.createStack(
+ final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- primarySplitScreen.reparent((ActivityStack) organizer.mPrimary, POSITION_TOP,
+ primarySplitScreen.reparent(organizer.mPrimary, POSITION_TOP,
false /*moveParents*/, "test");
// Assert windowing mode.
@@ -204,13 +205,13 @@ public class ActivityStackTests extends ActivityTestsBase {
TestSplitOrganizer organizer = new TestSplitOrganizer(mService);
// Set up split-screen with primary on top and secondary containing the home task below
// another stack.
- final ActivityStack primaryTask = mDefaultTaskDisplayArea.createStack(
+ final Task primaryTask = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityStack homeRoot = mDefaultTaskDisplayArea.getStack(
+ final Task homeRoot = mDefaultTaskDisplayArea.getStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
- final ActivityStack secondaryTask = mDefaultTaskDisplayArea.createStack(
+ final Task secondaryTask = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mDefaultTaskDisplayArea.positionStackAtTop((ActivityStack) organizer.mPrimary,
+ mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary,
false /* includingParents */);
// Move primary to back.
@@ -228,7 +229,7 @@ public class ActivityStackTests extends ActivityTestsBase {
assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, primaryTask.getWindowingMode());
// Move secondary to back via parent (should be equivalent)
- ((ActivityStack) organizer.mSecondary).moveToBack("test", secondaryTask);
+ organizer.mSecondary.moveToBack("test", secondaryTask);
// Assert that it is now in back but still in secondary split
assertEquals(1, homeRoot.compareTo(primaryTask));
@@ -239,12 +240,12 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testRemoveOrganizedTask_UpdateStackReference() {
- final ActivityStack rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask();
+ final Task rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask();
final ActivityRecord homeActivity = new ActivityBuilder(mService)
.setStack(rootHomeTask)
.setCreateTask(true)
.build();
- final ActivityStack secondaryStack = (ActivityStack) WindowContainer.fromBinder(
+ final Task secondaryStack = (Task) WindowContainer.fromBinder(
mService.mTaskOrganizerController.createRootTask(rootHomeTask.getDisplayId(),
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token.asBinder());
@@ -258,7 +259,7 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testStackInheritsDisplayWindowingMode() {
- final ActivityStack primarySplitScreen = mDefaultTaskDisplayArea.createStack(
+ final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
@@ -273,7 +274,7 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testStackOverridesDisplayWindowingMode() {
- final ActivityStack primarySplitScreen = mDefaultTaskDisplayArea.createStack(
+ final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
@@ -354,9 +355,9 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testMoveStackToBackIncludingParent() {
final TaskDisplayArea taskDisplayArea = addNewDisplayContentAt(DisplayContent.POSITION_TOP)
.getDefaultTaskDisplayArea();
- final ActivityStack stack1 = createStackForShouldBeVisibleTest(taskDisplayArea,
+ final Task stack1 = createStackForShouldBeVisibleTest(taskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityStack stack2 = createStackForShouldBeVisibleTest(taskDisplayArea,
+ final Task stack2 = createStackForShouldBeVisibleTest(taskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Do not move display to back because there is still another stack.
@@ -371,9 +372,9 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testShouldBeVisible_Fullscreen() {
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add an activity to the pinned stack so it isn't considered empty for visibility check.
final ActivityRecord pinnedActivity = new ActivityBuilder(mService)
@@ -384,7 +385,7 @@ public class ActivityStackTests extends ActivityTestsBase {
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
- final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
// Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
@@ -402,14 +403,14 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testShouldBeVisible_SplitScreen() {
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
// Home stack should always be fullscreen for this test.
doReturn(false).when(homeStack).supportsSplitScreenWindowingMode();
- final ActivityStack splitScreenPrimary =
+ final Task splitScreenPrimary =
createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityStack splitScreenSecondary =
+ final Task splitScreenSecondary =
createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -437,7 +438,7 @@ public class ActivityStackTests extends ActivityTestsBase {
assertEquals(STACK_VISIBILITY_VISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- final ActivityStack splitScreenSecondary2 =
+ final Task splitScreenSecondary2 =
createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// First split-screen secondary shouldn't be visible behind another opaque split-split
@@ -460,7 +461,7 @@ public class ActivityStackTests extends ActivityTestsBase {
assertEquals(STACK_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
- final ActivityStack assistantStack = createStackForShouldBeVisibleTest(
+ final Task assistantStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT,
true /* onTop */);
@@ -531,13 +532,13 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testGetVisibility_MultiLevel() {
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(
+ final Task homeStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME,
true /* onTop */);
- final ActivityStack splitPrimary = createStackForShouldBeVisibleTest(
+ final Task splitPrimary = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
- final ActivityStack splitSecondary = createStackForShouldBeVisibleTest(
+ final Task splitSecondary = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
@@ -556,7 +557,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// Add fullscreen translucent task that partially occludes split tasks
- final ActivityStack translucentStack = createStandardStackForVisibilityTest(
+ final Task translucentStack = createStandardStackForVisibilityTest(
WINDOWING_MODE_FULLSCREEN, true /* translucent */);
// Fullscreen translucent task should be visible
assertEquals(STACK_VISIBILITY_VISIBLE, translucentStack.getVisibility(null /* starting */));
@@ -580,10 +581,10 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testGetVisibility_FullscreenBehindTranslucent() {
- final ActivityStack bottomStack =
+ final Task bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final ActivityStack translucentStack =
+ final Task translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
@@ -595,13 +596,13 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() {
- final ActivityStack bottomStack =
+ final Task bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final ActivityStack translucentStack =
+ final Task translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final ActivityStack opaqueStack =
+ final Task opaqueStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
@@ -613,13 +614,13 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() {
- final ActivityStack bottomStack =
+ final Task bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final ActivityStack opaqueStack =
+ final Task opaqueStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final ActivityStack translucentStack =
+ final Task translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
@@ -632,10 +633,10 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testGetVisibility_FullscreenTranslucentBehindTranslucent() {
- final ActivityStack bottomTranslucentStack =
+ final Task bottomTranslucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final ActivityStack translucentStack =
+ final Task translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
@@ -647,10 +648,10 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testGetVisibility_FullscreenTranslucentBehindOpaque() {
- final ActivityStack bottomTranslucentStack =
+ final Task bottomTranslucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final ActivityStack opaqueStack =
+ final Task opaqueStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
@@ -661,13 +662,13 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testGetVisibility_FullscreenBehindTranslucentAndPip() {
- final ActivityStack bottomStack =
+ final Task bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final ActivityStack translucentStack =
+ final Task translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -684,7 +685,7 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testShouldBeVisible_Finishing() {
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
if (topRunningHomeActivity == null) {
@@ -694,7 +695,7 @@ public class ActivityStackTests extends ActivityTestsBase {
.build();
}
- final ActivityStack translucentStack = createStackForShouldBeVisibleTest(
+ final Task translucentStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
doReturn(true).when(translucentStack).isTranslucent(any());
@@ -717,7 +718,7 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testShouldBeVisible_FullscreenBehindTranslucentInHomeStack() {
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final ActivityRecord firstActivity = new ActivityBuilder(mService)
@@ -740,9 +741,9 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
mDefaultTaskDisplayArea.removeStack(mStack);
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
@@ -760,9 +761,9 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
mDefaultTaskDisplayArea.removeStack(mStack);
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -780,10 +781,10 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
mDefaultTaskDisplayArea.removeStack(mStack);
- final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
doReturn(false).when(homeStack).isTranslucent(any());
@@ -800,15 +801,15 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
mDefaultTaskDisplayArea.removeStack(mStack);
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
doReturn(false).when(homeStack).isTranslucent(any());
@@ -827,12 +828,12 @@ public class ActivityStackTests extends ActivityTestsBase {
testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
mDefaultTaskDisplayArea.removeStack(mStack);
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
@@ -851,13 +852,13 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testMoveHomeStackBehindStack_BehindHomeStack() {
mDefaultTaskDisplayArea.removeStack(mStack);
- final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
doReturn(false).when(homeStack).isTranslucent(any());
@@ -874,19 +875,19 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testMoveHomeStackBehindStack() {
mDefaultTaskDisplayArea.removeStack(mStack);
- final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack3 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack4 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack1);
@@ -901,13 +902,13 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testSetAlwaysOnTop() {
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(pinnedStack, getStackAbove(homeStack));
- final ActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
+ final Task alwaysOnTopStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
alwaysOnTopStack.setAlwaysOnTop(true);
@@ -915,13 +916,13 @@ public class ActivityStackTests extends ActivityTestsBase {
// Ensure (non-pinned) always on top stack is put below pinned stack.
assertEquals(pinnedStack, getStackAbove(alwaysOnTopStack));
- final ActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+ final Task nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
// Ensure non always on top stack is put below always on top stacks.
assertEquals(alwaysOnTopStack, getStackAbove(nonAlwaysOnTopStack));
- final ActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
+ final Task alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
alwaysOnTopStack2.setAlwaysOnTop(true);
@@ -946,13 +947,13 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testSplitScreenMoveToFront() {
- final ActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
+ final Task splitScreenPrimary = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
+ final Task splitScreenSecondary = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityStack assistantStack = createStackForShouldBeVisibleTest(
+ final Task assistantStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT,
true /* onTop */);
@@ -977,29 +978,25 @@ public class ActivityStackTests extends ActivityTestsBase {
}
}
- private ActivityStack createStandardStackForVisibilityTest(int windowingMode,
+ private Task createStandardStackForVisibilityTest(int windowingMode,
boolean translucent) {
- final ActivityStack stack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task stack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */);
doReturn(translucent).when(stack).isTranslucent(any());
return stack;
}
@SuppressWarnings("TypeParameterUnusedInFormals")
- private ActivityStack createStackForShouldBeVisibleTest(
+ private Task createStackForShouldBeVisibleTest(
TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop) {
- final ActivityStack stack;
+ final Task task;
if (activityType == ACTIVITY_TYPE_HOME) {
// Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
- stack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
- if (onTop) {
- mDefaultTaskDisplayArea.positionStackAtTop(stack,
- false /* includingParents */);
- } else {
- mDefaultTaskDisplayArea.positionStackAtBottom(stack);
- }
+ task = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ mDefaultTaskDisplayArea.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, task,
+ false /* includingParents */);
} else {
- stack = new StackBuilder(mRootWindowContainer)
+ task = new StackBuilder(mRootWindowContainer)
.setTaskDisplayArea(taskDisplayArea)
.setWindowingMode(windowingMode)
.setActivityType(activityType)
@@ -1007,7 +1004,7 @@ public class ActivityStackTests extends ActivityTestsBase {
.setCreateActivity(true)
.build();
}
- return stack;
+ return task;
}
@Test
@@ -1021,7 +1018,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// Note the activities have non-null ActivityRecord.app, so it won't remove directly.
mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process(
firstActivity.packageName, null /* filterByClasses */, true /* doit */,
- true /* evenPersistent */, UserHandle.USER_ALL);
+ true /* evenPersistent */, UserHandle.USER_ALL, false /* onlyRemoveNoProcess */);
// If the activity is disabled with {@link android.content.pm.PackageManager#DONT_KILL_APP}
// the activity should still follow the normal flow to finish and destroy.
@@ -1050,7 +1047,7 @@ public class ActivityStackTests extends ActivityTestsBase {
mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process(
activity.packageName, null /* filterByClasses */, true /* doit */,
- true /* evenPersistent */, UserHandle.USER_ALL);
+ true /* evenPersistent */, UserHandle.USER_ALL, false /* onlyRemoveNoProcess */);
// Although the overlay activity is in another package, the non-overlay activities are
// removed from the task. Since the overlay activity should be removed as well, the task
@@ -1152,7 +1149,7 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testWontFinishHomeStackImmediately() {
- final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
ActivityRecord activity = homeStack.topRunningActivity();
@@ -1172,10 +1169,10 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testFinishCurrentActivity() {
// Create 2 activities on a new display.
final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
- final ActivityStack stack1 = createStackForShouldBeVisibleTest(
+ final Task stack1 = createStackForShouldBeVisibleTest(
display.getDefaultTaskDisplayArea(),
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityStack stack2 = createStackForShouldBeVisibleTest(
+ final Task stack2 = createStackForShouldBeVisibleTest(
display.getDefaultTaskDisplayArea(),
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -1194,7 +1191,7 @@ public class ActivityStackTests extends ActivityTestsBase {
eq(display.mDisplayId), anyBoolean(), anyBoolean());
}
- private ActivityRecord finishTopActivity(ActivityStack stack) {
+ private ActivityRecord finishTopActivity(Task stack) {
final ActivityRecord activity = stack.topRunningActivity();
assertNotNull(activity);
activity.setState(STOPPED, "finishTopActivity");
@@ -1252,11 +1249,12 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testStackOrderChangedOnPositionStack() {
StackOrderChangedListener listener = new StackOrderChangedListener();
try {
- final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
- mDefaultTaskDisplayArea.positionStackAtBottom(fullscreenStack1);
+ mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenStack1,
+ false /*includingParents*/);
} finally {
mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
}
@@ -1331,42 +1329,40 @@ public class ActivityStackTests extends ActivityTestsBase {
}
@Test
- public void testCheckBehindFullscreenActivity() {
+ public void testIterateOccludedActivity() {
final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>();
- final Consumer<ActivityRecord> handleBehindFullscreenActivity = occludedActivities::add;
+ final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add;
final ActivityRecord bottomActivity =
new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build();
final ActivityRecord topActivity =
new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build();
+ // Top activity occludes bottom activity.
doReturn(true).when(mStack).shouldBeVisible(any());
- assertTrue(mStack.checkBehindFullscreenActivity(bottomActivity,
- null /* handleBehindFullscreenActivity */));
- assertFalse(mStack.checkBehindFullscreenActivity(topActivity,
- null /* handleBehindFullscreenActivity */));
+ assertTrue(topActivity.shouldBeVisible());
+ assertFalse(bottomActivity.shouldBeVisible());
- // Top activity occludes bottom activity.
- mStack.checkBehindFullscreenActivity(null /* toCheck */, handleBehindFullscreenActivity);
+ mStack.forAllOccludedActivities(handleOccludedActivity);
assertThat(occludedActivities).containsExactly(bottomActivity);
+ // Top activity doesn't occlude parent, so the bottom activity is not occluded.
doReturn(false).when(topActivity).occludesParent();
- assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity,
- null /* handleBehindFullscreenActivity */));
- assertFalse(mStack.checkBehindFullscreenActivity(topActivity,
- null /* handleBehindFullscreenActivity */));
+ assertTrue(bottomActivity.shouldBeVisible());
occludedActivities.clear();
- // Top activity doesn't occlude parent, so the bottom activity is not occluded.
- mStack.checkBehindFullscreenActivity(null /* toCheck */, handleBehindFullscreenActivity);
+ mStack.forAllOccludedActivities(handleOccludedActivity);
assertThat(occludedActivities).isEmpty();
+ // A finishing activity should not occlude other activities behind.
final ActivityRecord finishingActivity =
new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build();
finishingActivity.finishing = true;
doCallRealMethod().when(finishingActivity).occludesParent();
- assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity,
- null /* handleBehindFullscreenActivity */));
- assertFalse(mStack.checkBehindFullscreenActivity(topActivity,
- null /* handleBehindFullscreenActivity */));
+ assertTrue(topActivity.shouldBeVisible());
+ assertTrue(bottomActivity.shouldBeVisible());
+
+ occludedActivities.clear();
+ mStack.forAllOccludedActivities(handleOccludedActivity);
+ assertThat(occludedActivities).isEmpty();
}
@Test
@@ -1384,7 +1380,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activities[i] = r;
doReturn(null).when(mService).getProcessController(
eq(r.processName), eq(r.info.applicationInfo.uid));
- r.setState(ActivityStack.ActivityState.INITIALIZING, "test");
+ r.setState(Task.ActivityState.INITIALIZING, "test");
// Ensure precondition that the activity is opaque.
assertTrue(r.occludesParent());
mSupervisor.startSpecificActivity(r, false /* andResume */,
@@ -1392,8 +1388,7 @@ public class ActivityStackTests extends ActivityTestsBase {
}
mSupervisor.endDeferResume();
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
+ setBooted(mService);
// 2 activities are started while keyguard is locked, so they are waiting to be resolved.
assertFalse(unknownAppVisibilityController.allResolved());
@@ -1446,7 +1441,7 @@ public class ActivityStackTests extends ActivityTestsBase {
public boolean mChanged = false;
@Override
- public void onStackOrderChanged(ActivityStack stack) {
+ public void onStackOrderChanged(Task stack) {
mChanged = true;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index ca4456b7b926..c9a927901a37 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -77,7 +77,7 @@ public class ActivityStartControllerTests extends ActivityTestsBase {
.setCreateTask(true)
.build();
final int startFlags = random.nextInt();
- final ActivityStack stack = mService.mRootWindowContainer.getDefaultTaskDisplayArea()
+ final Task stack = mService.mRootWindowContainer.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final WindowProcessController wpc = new WindowProcessController(mService,
mService.mContext.getApplicationInfo(), "name", 12345,
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 4a19684e5554..3772e2500c1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -200,7 +200,8 @@ public class ActivityStarterTests extends ActivityTestsBase {
ai.packageName = "com.android.test.package";
final WindowProcessController wpc =
containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
- ? null : new WindowProcessController(service, ai, null, 0, -1, null, listener);
+ ? null
+ : new WindowProcessController(service, ai, null, 0, -1, null, listener);
doReturn(wpc).when(service).getProcessController(any());
final Intent intent = new Intent();
@@ -211,7 +212,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
IVoiceInteractionSession voiceSession =
containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
- ? mock(IVoiceInteractionSession.class) : null;
+ ? mock(IVoiceInteractionSession.class) : null;
// Create source token
final ActivityBuilder builder = new ActivityBuilder(service).setTask(
@@ -320,7 +321,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
if (mockGetLaunchStack) {
// Instrument the stack and task used.
- final ActivityStack stack = mRootWindowContainer.getDefaultTaskDisplayArea()
+ final Task stack = mRootWindowContainer.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
@@ -489,13 +490,12 @@ public class ActivityStarterTests extends ActivityTestsBase {
}
private void assertNoTasks(DisplayContent display) {
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ display.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
assertFalse(stack.hasChild());
}
- }
+ });
}
/**
@@ -618,7 +618,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
false, true, false, false, false);
runAndVerifyBackgroundActivityStartsSubtest(
- "disallowed_callerIsWhitelisted_notAborted", false,
+ "disallowed_callerIsAllowed_notAborted", false,
UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
false, false, true, false, false);
@@ -639,7 +639,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
boolean hasForegroundActivities, boolean callerIsRecents,
- boolean callerIsTempWhitelisted,
+ boolean callerIsTempAllowed,
boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges,
boolean isCallingUidDeviceOwner) {
// window visibility
@@ -664,8 +664,8 @@ public class ActivityStarterTests extends ActivityTestsBase {
RecentTasks recentTasks = mock(RecentTasks.class);
mService.mStackSupervisor.setRecentTasks(recentTasks);
doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
- // caller is temp whitelisted
- callerApp.setAllowBackgroundActivityStarts(callerIsTempWhitelisted);
+ // caller is temp allowed
+ callerApp.setAllowBackgroundActivityStarts(callerIsTempAllowed);
// caller is instrumenting with background activity starts privileges
callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,
callerIsInstrumentingWithBackgroundActivityStartPrivileges);
@@ -741,7 +741,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
new TestDisplayContent.Builder(mService, 1000, 1500)
.setPosition(POSITION_BOTTOM).build();
final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea();
- final ActivityStack stack = secondaryTaskContainer.createStack(
+ final Task stack = secondaryTaskContainer.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Create an activity record on the top of secondary display.
@@ -787,7 +787,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
ACTIVITY_TYPE_STANDARD, false /* onTop */));
// Create another activity on top of the secondary display.
- final ActivityStack topStack = secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN,
+ final Task topStack = secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
final Task topTask = new TaskBuilder(mSupervisor).setStack(topStack).build();
new ActivityBuilder(mService).setTask(topTask).build();
@@ -826,7 +826,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
Task task = topActivity.getTask();
starter.postStartActivityProcessing(
- task.getTopNonFinishingActivity(), START_DELIVERED_TO_TOP, task.getStack());
+ task.getTopNonFinishingActivity(), START_DELIVERED_TO_TOP, task.getRootTask());
verify(taskChangeNotifier).notifyActivityRestartAttempt(
any(), anyBoolean(), anyBoolean(), anyBoolean());
@@ -835,14 +835,14 @@ public class ActivityStarterTests extends ActivityTestsBase {
Task task2 = reusableActivity.getTask();
starter.postStartActivityProcessing(
- task2.getTopNonFinishingActivity(), START_TASK_TO_FRONT, task.getStack());
+ task2.getTopNonFinishingActivity(), START_TASK_TO_FRONT, task.getRootTask());
verify(taskChangeNotifier, times(2)).notifyActivityRestartAttempt(
any(), anyBoolean(), anyBoolean(), anyBoolean());
verify(taskChangeNotifier).notifyActivityRestartAttempt(
any(), anyBoolean(), anyBoolean(), eq(false));
}
- private ActivityRecord createSingleTaskActivityOn(ActivityStack stack) {
+ private ActivityRecord createSingleTaskActivityOn(Task stack) {
final ComponentName componentName = ComponentName.createRelative(
DEFAULT_COMPONENT_PACKAGE_NAME,
DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
@@ -1046,7 +1046,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
targetRecord.setVisibility(false);
final ActivityRecord sourceRecord = new ActivityBuilder(mService).build();
- final ActivityStack stack = spy(
+ final Task stack = spy(
mRootWindowContainer.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
/* onTop */true));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 64b5eca1beb8..f8faae66c704 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -67,14 +67,13 @@ public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
@Before
public void setUp() throws Exception {
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
+ setBooted(mService);
}
/** Verify that activity is finished correctly upon request. */
@Test
public void testActivityFinish() {
- final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
+ final Task stack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
assertTrue("Activity must be finished", mService.finishActivity(activity.appToken,
0 /* resultCode */, null /* resultData */,
@@ -88,7 +87,7 @@ public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
@Test
public void testOnPictureInPictureRequested() throws RemoteException {
- final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
+ final Task stack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class);
doReturn(mockLifecycleManager).when(mService).getLifecycleManager();
@@ -107,7 +106,7 @@ public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
@Test(expected = IllegalStateException.class)
public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException {
- final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
+ final Task stack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
doReturn(false).when(activity).inPinnedWindowingMode();
@@ -121,7 +120,7 @@ public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
@Test(expected = IllegalStateException.class)
public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException {
- final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
+ final Task stack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
doReturn(true).when(activity).inPinnedWindowingMode();
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 1fefb0c481d1..5be2f0453bf4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -112,7 +112,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
private String mAffinity;
private int mUid = 12345;
private boolean mCreateTask;
- private ActivityStack mStack;
+ private Task mStack;
private int mActivityFlags;
private int mLaunchMode;
private int mResizeMode = RESIZE_MODE_RESIZEABLE;
@@ -164,7 +164,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
return this;
}
- ActivityBuilder setStack(ActivityStack stack) {
+ ActivityBuilder setStack(Task stack) {
mStack = stack;
return this;
}
@@ -307,7 +307,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
wpc = mWpc;
} else {
wpc = new WindowProcessController(mService,
- mService.mContext.getApplicationInfo(), mProcessName, mUid,
+ aInfo.applicationInfo, mProcessName, mUid,
UserHandle.getUserId(12345), mock(Object.class),
mock(WindowProcessListener.class));
wpc.setThread(mock(IApplicationThread.class));
@@ -338,7 +338,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
private IVoiceInteractionSession mVoiceSession;
private boolean mCreateStack = true;
- private ActivityStack mStack;
+ private Task mStack;
private TaskDisplayArea mTaskDisplayArea;
TaskBuilder(ActivityStackSupervisor supervisor) {
@@ -384,7 +384,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
return this;
}
- TaskBuilder setStack(ActivityStack stack) {
+ TaskBuilder setStack(Task stack) {
mStack = stack;
return this;
}
@@ -418,7 +418,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
intent.setComponent(mComponent);
intent.setFlags(mFlags);
- final Task task = new ActivityStack(mSupervisor.mService, mTaskId, aInfo,
+ final Task task = new Task(mSupervisor.mService, mTaskId, aInfo,
intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/,
null /*taskDescription*/, mStack);
spyOn(task);
@@ -503,11 +503,11 @@ class ActivityTestsBase extends SystemServiceTestsBase {
return this;
}
- ActivityStack build() {
+ Task build() {
SystemServicesTestRule.checkHoldsLock(mRootWindowContainer.mWmService.mGlobalLock);
final int stackId = mStackId >= 0 ? mStackId : mTaskDisplayArea.getNextStackId();
- final ActivityStack stack = mTaskDisplayArea.createStackUnchecked(
+ final Task stack = mTaskDisplayArea.createStackUnchecked(
mWindowingMode, mActivityType, stackId, mOnTop, mInfo, mIntent,
false /* createdByOrganizer */);
final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor;
@@ -591,15 +591,14 @@ class ActivityTestsBase extends SystemServiceTestsBase {
mService.mTaskOrganizerController.setLaunchRoot(mDisplayId,
mSecondary.mRemoteToken.toWindowContainerToken());
DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
- for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+ dc.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ final Task stack = taskDisplayArea.getStackAt(sNdx);
if (!WindowConfiguration.isSplitScreenWindowingMode(stack.getWindowingMode())) {
stack.reparent(mSecondary, POSITION_BOTTOM);
}
}
- }
+ });
}
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
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 e8fab2b0243b..673feb260dd4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -53,14 +53,14 @@ import org.junit.runner.RunWith;
@RunWith(WindowTestRunner.class)
public class AppChangeTransitionTests extends WindowTestsBase {
- private ActivityStack mStack;
+ private Task mStack;
private Task mTask;
private ActivityRecord mActivity;
public void setUpOnDisplay(DisplayContent dc) {
mActivity = createTestActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
mTask = mActivity.getTask();
- mStack = mTask.getStack();
+ mStack = mTask.getRootTask();
// Set a remote animator with snapshot disabled. Snapshots don't work in wmtests.
RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index f6213bd94ddd..d7baf8d05bd6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -28,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doCallRealMethod;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
@@ -56,6 +57,14 @@ public class AppTransitionControllerTest extends WindowTestsBase {
mAppTransitionController = new AppTransitionController(mWm, mDisplayContent);
}
+ @Override
+ ActivityRecord createActivityRecord(DisplayContent dc, int windowingMode, int activityType) {
+ final ActivityRecord r = super.createActivityRecord(dc, windowingMode, activityType);
+ // Ensure that ActivityRecord#setOccludesParent takes effect.
+ doCallRealMethod().when(r).fillsParent();
+ return r;
+ }
+
@Test
@FlakyTest(bugId = 131005232)
public void testTranslucentOpen() {
@@ -125,12 +134,12 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (opening, invisible)
// +- [TaskStack2] - [Task2] - [ActivityRecord2] (closing, visible)
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1);
activity1.setVisible(false);
activity1.mVisibleRequested = true;
- final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2);
final ArraySet<ActivityRecord> opening = new ArraySet<>();
@@ -153,10 +162,10 @@ public class AppTransitionControllerTest extends WindowTestsBase {
public void testGetAnimationTargets_visibilityAlreadyUpdated() {
// [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (opening, visible)
// +- [TaskStack2] - [Task2] - [ActivityRecord2] (closing, invisible)
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1);
- final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2);
activity2.setVisible(false);
activity2.mVisibleRequested = false;
@@ -190,8 +199,45 @@ public class AppTransitionControllerTest extends WindowTestsBase {
}
@Test
+ public void testGetAnimationTargets_visibilityAlreadyUpdated_butForcedTransitionRequested() {
+ // [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (closing, invisible)
+ // +- [TaskStack2] - [Task2] - [ActivityRecord2] (opening, visible)
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1);
+ activity1.setVisible(true);
+ activity1.mVisibleRequested = true;
+ activity1.mRequestForceTransition = true;
+
+ final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2);
+ activity2.setVisible(false);
+ activity2.mVisibleRequested = false;
+ activity2.mRequestForceTransition = true;
+
+ final ArraySet<ActivityRecord> opening = new ArraySet<>();
+ opening.add(activity1);
+ final ArraySet<ActivityRecord> closing = new ArraySet<>();
+ closing.add(activity2);
+
+ // The visibility are already updated, but since forced transition is requested, it will
+ // be included.
+ WindowManagerService.sHierarchicalAnimations = false;
+ assertEquals(
+ new ArraySet<>(new WindowContainer[]{activity1}),
+ AppTransitionController.getAnimationTargets(
+ opening, closing, true /* visible */));
+ assertEquals(
+ new ArraySet<>(new WindowContainer[]{activity2}),
+ AppTransitionController.getAnimationTargets(
+ opening, closing, false /* visible */));
+ }
+
+ @Test
public void testGetAnimationTargets_exitingBeforeTransition() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ // Create another non-empty task so the animation target won't promote to task display area.
+ WindowTestUtils.createTestActivityRecord(
+ mDisplayContent.getDefaultTaskDisplayArea().getOrCreateRootHomeTask());
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(stack);
activity.setVisible(false);
activity.mIsExiting = true;
@@ -220,7 +266,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// +- [AppWindow1] (being-replaced)
// +- [TaskStack2] - [Task2] - [ActivityRecord2] (closing, invisible)
// +- [AppWindow2] (being-replaced)
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
@@ -229,7 +275,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
appWindow1.mWillReplaceWindow = true;
activity1.addWindow(appWindow1);
- final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2);
activity2.setVisible(false);
activity2.mVisibleRequested = false;
@@ -276,7 +322,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// |
// +- [TaskStack2] - [Task2] -+- [ActivityRecord3] (closing, visible)
// +- [ActivityRecord4] (invisible)
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
mDisplayContent, task1);
@@ -287,7 +333,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
activity2.setVisible(false);
activity2.mVisibleRequested = false;
- final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
final Task task2 = createTaskInStack(stack2, 0 /* userId */);
final ActivityRecord activity3 = WindowTestUtils.createActivityRecordInTask(
mDisplayContent, task2);
@@ -319,7 +365,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// [DisplayContent] - [TaskStack] - [Task] -+- [ActivityRecord1] (opening, invisible)
// +- [ActivityRecord2] (closing, visible)
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
mDisplayContent, task);
@@ -355,7 +401,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// +- [TaskStack2] - [Task2] -+- [ActivityRecord3] (closing, visible)
// +- [ActivityRecord4] (visible)
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
mDisplayContent, task1);
@@ -366,7 +412,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
final ActivityRecord activity2 = WindowTestUtils.createActivityRecordInTask(
mDisplayContent, task1);
- final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
final Task task2 = createTaskInStack(stack2, 0 /* userId */);
final ActivityRecord activity3 = WindowTestUtils.createActivityRecordInTask(
mDisplayContent, task2);
@@ -401,7 +447,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// +- [TaskStack2] - [Task2] -+- [ActivityRecord3] (closing, visible)
// +- [ActivityRecord4] (closing, visible)
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
mDisplayContent, task1);
@@ -414,7 +460,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
activity2.setVisible(false);
activity2.mVisibleRequested = true;
- final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
final Task task2 = createTaskInStack(stack2, 0 /* userId */);
final ActivityRecord activity3 = WindowTestUtils.createActivityRecordInTask(
mDisplayContent, task2);
@@ -447,7 +493,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// [DisplayContent] - [TaskStack] -+- [Task1] - [ActivityRecord1] (opening, invisible)
// +- [Task2] - [ActivityRecord2] (closing, visible)
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack, 0 /* userId */);
final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(
mDisplayContent, task1);
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 8c8fd0516623..17914e7fb68c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -47,7 +47,6 @@ import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.WindowManager;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -150,7 +149,7 @@ public class AppTransitionTests extends WindowTestsBase {
final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
- final ActivityStack stack1 = createTaskStackOnDisplay(dc1);
+ final Task stack1 = createTaskStackOnDisplay(dc1);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
final ActivityRecord activity1 =
WindowTestUtils.createTestActivityRecord(dc1);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 1d13788bc523..97a2ebe98abb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -82,7 +82,7 @@ import java.util.ArrayList;
@RunWith(WindowTestRunner.class)
public class AppWindowTokenTests extends WindowTestsBase {
- ActivityStack mStack;
+ Task mStack;
Task mTask;
ActivityRecord mActivity;
@@ -158,6 +158,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
mActivity.removeImmediately();
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
@FlakyTest(bugId = 131005232)
public void testLandscapeSeascapeRotationByApp() {
@@ -188,6 +189,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
appWindow.removeImmediately();
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testLandscapeSeascapeRotationByPolicy() {
// This instance has been spied in {@link TestDisplayContent}.
@@ -295,6 +297,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
mWm.mDisplayFrozen = false;
}
+ @UseTestDisplay
@Test
public void testRespectTopFullscreenOrientation() {
final Configuration displayConfig = mActivity.mDisplayContent.getConfiguration();
@@ -316,6 +319,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
assertEquals(Configuration.ORIENTATION_LANDSCAPE, activityConfig.orientation);
}
+ @UseTestDisplay
@Test
public void testReportOrientationChange() {
mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -343,7 +347,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testAddRemoveRace() {
// There was once a race condition between adding and removing starting windows
- final ActivityRecord appToken = mAppWindow.mActivityRecord;
+ final ActivityRecord appToken = createIsolatedTestActivityRecord();
for (int i = 0; i < 1000; i++) {
appToken.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
@@ -480,7 +484,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
}
private ActivityRecord createIsolatedTestActivityRecord() {
- final ActivityStack taskStack = createTaskStackOnDisplay(mDisplayContent);
+ final Task taskStack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(taskStack, 0 /* userId */);
return createTestActivityRecordForGivenTask(task);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 4bf0bb9eee8c..4abb6059cc59 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -16,15 +16,21 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
+import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
@@ -33,13 +39,18 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
import static java.util.stream.Collectors.toList;
import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
+import com.google.android.collect.Lists;
+
import org.hamcrest.CustomTypeSafeMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
@@ -69,11 +80,15 @@ public class DisplayAreaPolicyBuilderTest {
private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null);
private WindowManagerService mWms;
- private DisplayArea.Root mRoot;
+ private RootDisplayArea mRoot;
private DisplayArea<WindowContainer> mImeContainer;
private DisplayContent mDisplayContent;
private TaskDisplayArea mDefaultTaskDisplayArea;
private List<TaskDisplayArea> mTaskDisplayAreaList;
+ private RootDisplayArea mGroupRoot1;
+ private RootDisplayArea mGroupRoot2;
+ private TaskDisplayArea mTda1;
+ private TaskDisplayArea mTda2;
@Before
public void setup() {
@@ -85,33 +100,39 @@ public class DisplayAreaPolicyBuilderTest {
FEATURE_DEFAULT_TASK_CONTAINER);
mTaskDisplayAreaList = new ArrayList<>();
mTaskDisplayAreaList.add(mDefaultTaskDisplayArea);
+ mGroupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1", FEATURE_VENDOR_FIRST + 1);
+ mGroupRoot2 = new SurfacelessDisplayAreaRoot(mWms, "group2", FEATURE_VENDOR_FIRST + 2);
+ mTda1 = new TaskDisplayArea(mDisplayContent, mWms, "tda1", FEATURE_VENDOR_FIRST + 3);
+ mTda2 = new TaskDisplayArea(mDisplayContent, mWms, "tda2", FEATURE_VENDOR_FIRST + 4);
}
@Test
public void testBuilder() {
final Feature foo;
final Feature bar;
-
+ DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 0)
+ .upTo(TYPE_STATUS_BAR)
+ .and(TYPE_NAVIGATION_BAR)
+ .build())
+ .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 1)
+ .all()
+ .except(TYPE_STATUS_BAR)
+ .build())
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList);
DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
- .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 0)
- .upTo(TYPE_STATUS_BAR)
- .and(TYPE_NAVIGATION_BAR)
- .build())
- .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 1)
- .all()
- .except(TYPE_STATUS_BAR)
- .build())
- .build(mWms, mDisplayContent, mRoot, mImeContainer, mTaskDisplayAreaList);
+ .setRootHierarchy(rootHierarchy)
+ .build(mWms);
- policy.attachDisplayAreas();
-
- assertThat(policy.getDisplayAreas(foo)).isNotEmpty();
- assertThat(policy.getDisplayAreas(bar)).isNotEmpty();
+ assertThat(policy.getDisplayAreas(foo.getId())).isNotEmpty();
+ assertThat(policy.getDisplayAreas(bar.getId())).isNotEmpty();
Matcher<WindowContainer> fooDescendantMatcher = descendantOfOneOf(
- policy.getDisplayAreas(foo));
+ policy.getDisplayAreas(foo.getId()));
Matcher<WindowContainer> barDescendantMatcher = descendantOfOneOf(
- policy.getDisplayAreas(bar));
+ policy.getDisplayAreas(bar.getId()));
// There is a DA of TYPE_STATUS_BAR below foo, but not below bar
assertThat(fooDescendantMatcher.matches(
@@ -128,7 +149,7 @@ public class DisplayAreaPolicyBuilderTest {
assertThat(barDescendantMatcher.matches(mImeContainer)).isTrue();
List<DisplayArea<?>> actualOrder = collectLeafAreas(mRoot);
- Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, mRoot, mImeContainer,
+ Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, mImeContainer,
mDefaultTaskDisplayArea);
actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList());
@@ -159,21 +180,57 @@ public class DisplayAreaPolicyBuilderTest {
}
@Test
+ public void testBuilder_defaultPolicy_hasWindowedMagnificationFeature() {
+ final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
+ resourcesWithProvider(""));
+ final DisplayAreaPolicyBuilder.Result defaultPolicy =
+ (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
+ mRoot, mImeContainer);
+ final List<Feature> features = defaultPolicy.getFeatures();
+ boolean hasWindowedMagnificationFeature = false;
+ for (Feature feature : features) {
+ hasWindowedMagnificationFeature |= feature.getId() == FEATURE_WINDOWED_MAGNIFICATION;
+ }
+
+ assertThat(hasWindowedMagnificationFeature).isTrue();
+ }
+
+ @Test
+ public void testBuilder_defaultPolicy_hasFullscreenMagnificationFeature() {
+ final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
+ resourcesWithProvider(""));
+ final DisplayAreaPolicyBuilder.Result defaultPolicy =
+ (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
+ mRoot, mImeContainer);
+ final List<Feature> features = defaultPolicy.getFeatures();
+ boolean hasFullscreenMagnificationFeature = false;
+ for (Feature feature : features) {
+ hasFullscreenMagnificationFeature |=
+ feature.getId() == FEATURE_FULLSCREEN_MAGNIFICATION;
+ }
+
+ assertThat(hasFullscreenMagnificationFeature).isTrue();
+ }
+
+ @Test
public void testBuilder_createCustomizedDisplayAreaForFeature() {
final Feature dimmable;
final Feature other;
+ DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", 0)
+ .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+ .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+ .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
+ .build())
+ .addFeature(other = new Feature.Builder(mPolicy, "Other", 1)
+ .all()
+ .build())
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList);
DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
- .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", 0)
- .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
- .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
- .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
- .build())
- .addFeature(other = new Feature.Builder(mPolicy, "Other", 1)
- .all()
- .build())
- .build(mWms, mDisplayContent, mRoot, mImeContainer, mTaskDisplayAreaList);
-
- policy.attachDisplayAreas();
+ .setRootHierarchy(rootHierarchy)
+ .build(mWms);
List<DisplayArea<? extends WindowContainer>> dimmableDAs =
policy.getDisplayAreas(dimmable.getId());
@@ -186,6 +243,271 @@ public class DisplayAreaPolicyBuilderTest {
}
}
+ @Test
+ public void testBuilder_singleRoot_validateSettings() {
+ final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder();
+
+ // Root must be set.
+ assertThrows(IllegalStateException.class, () -> builder.build(mWms));
+
+ // IME must be set.
+ builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+
+ assertThrows(IllegalStateException.class, () -> builder.build(mWms));
+
+ // Default TaskDisplayArea must be set.
+ builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(
+ new TaskDisplayArea(mDisplayContent, mWms, "testTda",
+ FEATURE_VENDOR_FIRST + 1))));
+
+ assertThrows(IllegalStateException.class, () -> builder.build(mWms));
+
+ // No exception
+ builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+
+ builder.build(mWms);
+ }
+
+ @Test
+ public void testBuilder_displayAreaGroup_validateSettings() {
+ final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder();
+
+ // IME must be set to one of the roots.
+ builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
+ builder1.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+
+ assertThrows(IllegalStateException.class, () -> builder1.build(mWms));
+
+ // Default TaskDisplayArea must be set.
+ final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
+ builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
+ builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(
+ new TaskDisplayArea(mDisplayContent, mWms, "testTda",
+ FEATURE_VENDOR_FIRST + 1))));
+
+ assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
+
+ // Each DisplayAreaGroup must have at least one TaskDisplayArea.
+ final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder();
+ builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
+ builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+ builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2));
+
+ assertThrows(IllegalStateException.class, () -> builder3.build(mWms));
+
+ // No exception
+ final DisplayAreaPolicyBuilder builder4 = new DisplayAreaPolicyBuilder();
+ builder4.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
+ builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+ builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(
+ new TaskDisplayArea(mDisplayContent, mWms, "testTda",
+ FEATURE_VENDOR_FIRST + 1))));
+
+ builder4.build(mWms);
+ }
+
+ @Test
+ public void testBuilder_displayAreaGroup_attachDisplayAreas() {
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1)))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2)))
+ .build(mWms);
+
+ assertThat(mDefaultTaskDisplayArea.isDescendantOf(mRoot)).isTrue();
+ assertThat(mGroupRoot1.isDescendantOf(mRoot)).isTrue();
+ assertThat(mGroupRoot2.isDescendantOf(mRoot)).isTrue();
+ assertThat(mImeContainer.isDescendantOf(mGroupRoot1)).isTrue();
+ assertThat(mTda1.isDescendantOf(mGroupRoot1)).isTrue();
+ assertThat(mTda2.isDescendantOf(mGroupRoot2)).isTrue();
+ assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot1)).isTrue();
+ assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot2)).isTrue();
+ }
+
+ @Test
+ public void testBuilder_displayAreaGroup_createFeatureOnGroup() {
+ final Feature feature1 = new Feature.Builder(mWms.mPolicy, "feature1",
+ FEATURE_VENDOR_FIRST + 5)
+ .all()
+ .except(TYPE_STATUS_BAR)
+ .build();
+ final Feature feature2 = new Feature.Builder(mWms.mPolicy, "feature2",
+ FEATURE_VENDOR_FIRST + 6)
+ .upTo(TYPE_STATUS_BAR)
+ .and(TYPE_NAVIGATION_BAR)
+ .build();
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1))
+ .addFeature(feature1))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2))
+ .addFeature(feature2))
+ .build(mWms);
+
+ List<DisplayArea<? extends WindowContainer>> feature1DAs =
+ policy.getDisplayAreas(feature1.getId());
+ List<DisplayArea<? extends WindowContainer>> feature2DAs =
+ policy.getDisplayAreas(feature2.getId());
+ for (DisplayArea<? extends WindowContainer> da : feature1DAs) {
+ assertThat(da.isDescendantOf(mGroupRoot1)).isTrue();
+ }
+ for (DisplayArea<? extends WindowContainer> da : feature2DAs) {
+ assertThat(da.isDescendantOf(mGroupRoot2)).isTrue();
+ }
+ }
+
+ @Test
+ public void testBuilder_addWindow_selectContainerForWindowFunc_defaultFunc() {
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1)))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2)))
+ .build(mWms);
+
+ final WindowToken token = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+ policy.addWindow(token);
+
+ // By default, window are always added to the root.
+ assertThat(token.isDescendantOf(mRoot)).isTrue();
+ assertThat(token.isDescendantOf(mGroupRoot1)).isFalse();
+ assertThat(token.isDescendantOf(mGroupRoot2)).isFalse();
+ }
+
+ @Test
+ public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnType() {
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList))
+ .addDisplayAreaGroupHierarchy(hierarchy1)
+ .addDisplayAreaGroupHierarchy(hierarchy2)
+ .setSelectRootForWindowFunc((token, options) -> {
+ if (token.windowType == TYPE_STATUS_BAR) {
+ return mGroupRoot1;
+ }
+ return mGroupRoot2;
+ })
+ .build(mWms);
+
+ final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+ final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_WALLPAPER, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+ policy.addWindow(token1);
+ policy.addWindow(token2);
+
+ assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue();
+ assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue();
+ }
+
+ @Test
+ public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnOptions() {
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList);
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(hierarchy0)
+ .addDisplayAreaGroupHierarchy(hierarchy1)
+ .addDisplayAreaGroupHierarchy(hierarchy2)
+ .setSelectRootForWindowFunc((token, options) -> {
+ if (options == null) {
+ return mRoot;
+ }
+ if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot1.mFeatureId) {
+ return mGroupRoot1;
+ }
+ if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot2.mFeatureId) {
+ return mGroupRoot2;
+ }
+ return mRoot;
+ })
+ .build(mWms);
+
+ final Bundle options1 = new Bundle();
+ options1.putInt("HIERARCHY_ROOT_ID", mGroupRoot1.mFeatureId);
+ final Bundle options2 = new Bundle();
+ options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId);
+ final WindowToken token0 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+ final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, options1);
+ final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, options2);
+
+ policy.addWindow(token0);
+ policy.addWindow(token1);
+ policy.addWindow(token2);
+
+ assertThat(token0.isDescendantOf(mRoot)).isTrue();
+ assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue();
+ assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue();
+ }
+
private static Resources resourcesWithProvider(String provider) {
Resources mock = mock(Resources.class);
when(mock.getString(
@@ -207,9 +529,9 @@ public class DisplayAreaPolicyBuilderTest {
}
private Map<DisplayArea<?>, Set<Integer>> calculateZSets(
- DisplayAreaPolicyBuilder.Result policy, DisplayArea.Root root,
+ DisplayAreaPolicyBuilder.Result policy,
DisplayArea<WindowContainer> ime,
- DisplayArea<ActivityStack> tasks) {
+ DisplayArea<Task> tasks) {
Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>();
int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION,
TYPE_APPLICATION_OVERLAY};
@@ -255,6 +577,10 @@ public class DisplayAreaPolicyBuilderTest {
};
}
+ private boolean isSibling(WindowContainer da1, WindowContainer da2) {
+ return da1.getParent() != null && da1.getParent() == da2.getParent();
+ }
+
private WindowToken tokenOfType(int type) {
WindowToken m = mock(WindowToken.class);
when(m.getWindowLayerFromType()).thenReturn(
@@ -285,10 +611,14 @@ public class DisplayAreaPolicyBuilderTest {
}
}
- private static class SurfacelessDisplayAreaRoot extends DisplayArea.Root {
+ static class SurfacelessDisplayAreaRoot extends RootDisplayArea {
SurfacelessDisplayAreaRoot(WindowManagerService wms) {
- super(wms);
+ this(wms, "SurfacelessDisplayAreaRoot", FEATURE_ROOT);
+ }
+
+ SurfacelessDisplayAreaRoot(WindowManagerService wms, String name, int featureId) {
+ super(wms, name, featureId);
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
new file mode 100644
index 000000000000..39bf8eb857b0
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for the {@link DisplayAreaPolicy}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplayAreaPolicyTests
+ */
+@SmallTest
+@Presubmit
+public class DisplayAreaPolicyTests {
+
+ @Rule
+ public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule();
+
+ private DisplayAreaPolicyBuilder.Result mPolicy;
+ private TaskDisplayArea mTaskDisplayArea1;
+ private TaskDisplayArea mTaskDisplayArea2;
+ private RootDisplayArea mRoot;
+
+ @Before
+ public void setUp() throws Exception {
+ WindowManagerService wms = mSystemServices.getWindowManagerService();
+ mRoot = new SurfacelessDisplayAreaRoot(wms);
+ spyOn(mRoot);
+ DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
+ DisplayContent displayContent = mock(DisplayContent.class);
+ doReturn(true).when(displayContent).isTrusted();
+ mTaskDisplayArea1 = new TaskDisplayArea(displayContent, wms, "Tasks1",
+ FEATURE_DEFAULT_TASK_CONTAINER);
+ mTaskDisplayArea2 = new TaskDisplayArea(displayContent, wms, "Tasks2",
+ FEATURE_VENDOR_FIRST);
+ List<TaskDisplayArea> taskDisplayAreaList = new ArrayList<>();
+ taskDisplayAreaList.add(mTaskDisplayArea1);
+ taskDisplayAreaList.add(mTaskDisplayArea2);
+
+ mPolicy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(ime)
+ .setTaskDisplayAreas(taskDisplayAreaList))
+ .build(wms);
+ }
+
+ @Test
+ public void testGetDefaultTaskDisplayArea() {
+ assertEquals(mTaskDisplayArea1, mPolicy.getDefaultTaskDisplayArea());
+ }
+
+ @Test
+ public void testTaskDisplayArea_taskPositionChanged_updatesTaskDisplayAreaPosition() {
+ final Task stack1 = mTaskDisplayArea1.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task stack2 = mTaskDisplayArea2.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ // Initial order
+ assertTaskDisplayAreasOrder(mPolicy, mTaskDisplayArea1, mTaskDisplayArea2);
+
+ // Move stack in tda1 to top
+ stack1.getParent().positionChildAt(POSITION_TOP, stack1, true /* includingParents */);
+
+ assertTaskDisplayAreasOrder(mPolicy, mTaskDisplayArea2, mTaskDisplayArea1);
+
+ // Move stack in tda2 to top, but not including parents
+ stack2.getParent().positionChildAt(POSITION_TOP, stack2, false /* includingParents */);
+
+ assertTaskDisplayAreasOrder(mPolicy, mTaskDisplayArea2, mTaskDisplayArea1);
+
+ // Move stack in tda1 to bottom
+ stack1.getParent().positionChildAt(POSITION_BOTTOM, stack1, true /* includingParents */);
+
+ assertTaskDisplayAreasOrder(mPolicy, mTaskDisplayArea1, mTaskDisplayArea2);
+
+ // Move stack in tda2 to bottom, but not including parents
+ stack2.getParent().positionChildAt(POSITION_BOTTOM, stack2, false /* includingParents */);
+
+ assertTaskDisplayAreasOrder(mPolicy, mTaskDisplayArea1, mTaskDisplayArea2);
+ }
+
+ @Test
+ public void testDisplayAreaGroup_taskPositionChanged_updatesDisplayAreaGroupPosition() {
+ final WindowManagerService wms = mSystemServices.getWindowManagerService();
+ final DisplayContent displayContent = mock(DisplayContent.class);
+ doReturn(true).when(displayContent).isTrusted();
+ final RootDisplayArea root = new SurfacelessDisplayAreaRoot(wms);
+ final RootDisplayArea group1 = new SurfacelessDisplayAreaRoot(wms, "group1",
+ FEATURE_VENDOR_FIRST + 1);
+ final RootDisplayArea group2 = new SurfacelessDisplayAreaRoot(wms, "group2",
+ FEATURE_VENDOR_FIRST + 2);
+ final TaskDisplayArea taskDisplayArea1 = new TaskDisplayArea(displayContent, wms, "Tasks1",
+ FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea taskDisplayArea2 = new TaskDisplayArea(displayContent, wms, "Tasks2",
+ FEATURE_VENDOR_FIRST + 3);
+ final TaskDisplayArea taskDisplayArea3 = new TaskDisplayArea(displayContent, wms, "Tasks3",
+ FEATURE_VENDOR_FIRST + 4);
+ final TaskDisplayArea taskDisplayArea4 = new TaskDisplayArea(displayContent, wms, "Tasks4",
+ FEATURE_VENDOR_FIRST + 5);
+ final TaskDisplayArea taskDisplayArea5 = new TaskDisplayArea(displayContent, wms, "Tasks5",
+ FEATURE_VENDOR_FIRST + 6);
+ final DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
+ final DisplayAreaPolicy policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
+ .setImeContainer(ime)
+ .setTaskDisplayAreas(Lists.newArrayList(taskDisplayArea1, taskDisplayArea2))
+ )
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(group1)
+ .setTaskDisplayAreas(Lists.newArrayList(taskDisplayArea3, taskDisplayArea4))
+ )
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(group2)
+ .setTaskDisplayAreas(Lists.newArrayList(taskDisplayArea5)))
+ .build(wms);
+ final Task stack1 = taskDisplayArea1.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task stack3 = taskDisplayArea3.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task stack4 = taskDisplayArea4.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ // Initial order
+ assertTaskDisplayAreasOrder(policy, taskDisplayArea1, taskDisplayArea2, taskDisplayArea3,
+ taskDisplayArea4, taskDisplayArea5);
+
+ // Move bottom stack in tda1 to top
+ stack1.getParent().positionChildAt(POSITION_TOP, stack1, true /* includingParents */);
+
+ assertTaskDisplayAreasOrder(policy, taskDisplayArea2, taskDisplayArea3, taskDisplayArea4,
+ taskDisplayArea5, taskDisplayArea1);
+
+ // Move bottom stack in tda2 to top
+ stack3.getParent().positionChildAt(POSITION_TOP, stack3, true /* includingParents */);
+
+ assertTaskDisplayAreasOrder(policy, taskDisplayArea2, taskDisplayArea5, taskDisplayArea1,
+ taskDisplayArea4, taskDisplayArea3);
+
+ // Move bottom stack in tda2 to top
+ stack4.getParent().positionChildAt(POSITION_TOP, stack4, true /* includingParents */);
+
+ assertTaskDisplayAreasOrder(policy, taskDisplayArea2, taskDisplayArea5, taskDisplayArea1,
+ taskDisplayArea3, taskDisplayArea4);
+
+ // Move top stack in tda2 to bottom
+ stack4.getParent().positionChildAt(POSITION_BOTTOM, stack4, true /* includingParents */);
+
+ assertTaskDisplayAreasOrder(policy, taskDisplayArea4, taskDisplayArea3, taskDisplayArea2,
+ taskDisplayArea5, taskDisplayArea1);
+ }
+
+ private void assertTaskDisplayAreasOrder(DisplayAreaPolicy policy,
+ TaskDisplayArea... expectTdaOrder) {
+ List<TaskDisplayArea> expectOrder = new ArrayList<>();
+ Collections.addAll(expectOrder, expectTdaOrder);
+
+ // Verify hierarchy
+ List<TaskDisplayArea> actualOrder = new ArrayList<>();
+ policy.mRoot.forAllTaskDisplayAreas(taskDisplayArea -> {
+ actualOrder.add(taskDisplayArea);
+ }, false /* traverseTopToBottom */);
+ assertEquals(expectOrder, actualOrder);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
index 6834ee5f3950..351426ae78b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -28,6 +28,10 @@ import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
+/**
+ * Build/Install/Run:
+ * atest WmTests:DisplayAreaProviderTest
+ */
@Presubmit
public class DisplayAreaProviderTest {
@@ -77,7 +81,7 @@ public class DisplayAreaProviderTest {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
- DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer) {
+ RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer) {
throw new RuntimeException("test stub");
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 880c486c15af..c8ed87db9d3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -19,47 +19,74 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayArea.Type.ANY;
import static com.android.server.wm.DisplayArea.Type.BELOW_TASKS;
import static com.android.server.wm.DisplayArea.Type.checkChild;
import static com.android.server.wm.DisplayArea.Type.checkSiblings;
import static com.android.server.wm.DisplayArea.Type.typeOf;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.testing.Assert.assertThrows;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
-import android.view.SurfaceControl;
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Tests for the {@link DisplayArea} container.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplayAreaTest
+ */
@Presubmit
public class DisplayAreaTest {
@Rule
public SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
+ private WindowManagerService mWms;
+
+ @Before
+ public void setup() {
+ mWms = mWmsRule.getWindowManagerService();
+ }
+
@Test
public void testDisplayArea_positionChanged_throwsIfIncompatibleChild() {
- WindowManagerService wms = mWmsRule.getWindowManagerService();
- DisplayArea<WindowContainer> parent = new DisplayArea<>(wms, BELOW_TASKS, "Parent");
- DisplayArea<WindowContainer> child = new DisplayArea<>(wms, ANY, "Child");
+ DisplayArea<WindowContainer> parent = new DisplayArea<>(mWms, BELOW_TASKS, "Parent");
+ DisplayArea<WindowContainer> child = new DisplayArea<>(mWms, ANY, "Child");
assertThrows(IllegalStateException.class, () -> parent.addChild(child, 0));
}
@Test
public void testType_typeOf() {
- WindowManagerService wms = mWmsRule.getWindowManagerService();
-
- assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(wms, ABOVE_TASKS, "test")));
- assertEquals(ANY, typeOf(new DisplayArea<>(wms, ANY, "test")));
- assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(wms, BELOW_TASKS, "test")));
+ assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(mWms, ABOVE_TASKS, "test")));
+ assertEquals(ANY, typeOf(new DisplayArea<>(mWms, ANY, "test")));
+ assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(mWms, BELOW_TASKS, "test")));
assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_APPLICATION_OVERLAY)));
assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_PRESENTATION)));
@@ -97,21 +124,264 @@ public class DisplayAreaTest {
assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ANY));
}
- private WindowToken createWindowToken(int type) {
- return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
- type, false /* persist */, null /* displayContent */,
- false /* canManageTokens */);
+ @Test
+ public void testAsDisplayArea() {
+ final WindowContainer windowContainer = new WindowContainer(mWms);
+ final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(mWms, ANY, "DA");
+ final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA", FEATURE_DEFAULT_TASK_CONTAINER);
+
+ assertThat(windowContainer.asDisplayArea()).isNull();
+ assertThat(displayArea.asDisplayArea()).isEqualTo(displayArea);
+ assertThat(taskDisplayArea.asDisplayArea()).isEqualTo(taskDisplayArea);
}
- private static class SurfacelessDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
+ @Test
+ public void testForAllTaskDisplayAreas_onlyTraversesDisplayAreaOfTypeAny() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final Function<TaskDisplayArea, Boolean> callback0 = tda -> false;
+ final Consumer<TaskDisplayArea> callback1 = tda -> { };
+ final BiFunction<TaskDisplayArea, Integer, Integer> callback2 = (tda, result) -> result;
+ final Function<TaskDisplayArea, TaskDisplayArea> callback3 = tda -> null;
+
+ // Don't traverse the child if the current DA has type BELOW_TASKS
+ final DisplayArea<WindowContainer> da1 = new DisplayArea<>(mWms, BELOW_TASKS, "DA1");
+ final DisplayArea<WindowContainer> da2 = new DisplayArea<>(mWms, BELOW_TASKS, "DA2");
+ root.addChild(da1, POSITION_BOTTOM);
+ da1.addChild(da2, POSITION_TOP);
+ spyOn(da2);
+
+ da1.forAllTaskDisplayAreas(callback0);
+ da1.forAllTaskDisplayAreas(callback1);
+ da1.reduceOnAllTaskDisplayAreas(callback2, 0);
+ da1.getItemFromTaskDisplayAreas(callback3);
+
+ verifyZeroInteractions(da2);
+
+ // Traverse the child if the current DA has type ANY
+ final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWms, ANY, "DA3");
+ final DisplayArea<WindowContainer> da4 = new DisplayArea<>(mWms, ANY, "DA4");
+ root.addChild(da3, POSITION_TOP);
+ da3.addChild(da4, POSITION_TOP);
+ spyOn(da4);
+
+ da3.forAllTaskDisplayAreas(callback0);
+ da3.forAllTaskDisplayAreas(callback1);
+ da3.reduceOnAllTaskDisplayAreas(callback2, 0);
+ da3.getItemFromTaskDisplayAreas(callback3);
+
+ verify(da4).forAllTaskDisplayAreas(callback0, true /* traverseTopToBottom */);
+ verify(da4).forAllTaskDisplayAreas(callback1, true /* traverseTopToBottom */);
+ verify(da4).reduceOnAllTaskDisplayAreas(callback2, 0 /* initValue */,
+ true /* traverseTopToBottom */);
+ verify(da4).getItemFromTaskDisplayAreas(
+ callback3, true /* traverseTopToBottom */);
+
+ // Don't traverse the child if the current DA has type ABOVE_TASKS
+ final DisplayArea<WindowContainer> da5 = new DisplayArea<>(mWms, ABOVE_TASKS, "DA5");
+ final DisplayArea<WindowContainer> da6 = new DisplayArea<>(mWms, ABOVE_TASKS, "DA6");
+ root.addChild(da5, POSITION_TOP);
+ da5.addChild(da6, POSITION_TOP);
+ spyOn(da6);
+
+ da5.forAllTaskDisplayAreas(callback0);
+ da5.forAllTaskDisplayAreas(callback1);
+ da5.reduceOnAllTaskDisplayAreas(callback2, 0);
+ da5.getItemFromTaskDisplayAreas(callback3);
+
+ verifyZeroInteractions(da6);
+ }
+
+ @Test
+ public void testForAllTaskDisplayAreas_appliesOnTaskDisplayAreaInOrder() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final DisplayArea<DisplayArea> da1 =
+ new DisplayArea<>(mWms, ANY, "DA1");
+ final DisplayArea<DisplayArea> da2 =
+ new DisplayArea<>(mWms, ANY, "DA2");
+ final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA2", FEATURE_VENDOR_FIRST);
+ final TaskDisplayArea tda3 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA3", FEATURE_VENDOR_FIRST + 1);
+ root.addChild(da1, POSITION_TOP);
+ root.addChild(da2, POSITION_TOP);
+ da1.addChild(tda1, POSITION_TOP);
+ da2.addChild(tda2, POSITION_TOP);
+ da2.addChild(tda3, POSITION_TOP);
+
+ /* The hierarchy looks like this
+ Root
+ - DA1
+ - TDA1 ------ bottom
+ - DA2
+ - TDA2
+ - TDA3 ------ top
+ */
+
+ // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>)
+ List<TaskDisplayArea> actualOrder = new ArrayList<>();
+ root.forAllTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ });
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
+
+ // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>, boolean)
+ actualOrder.clear();
+ root.forAllTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ }, false /* traverseTopToBottom */);
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
+
+ // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>)
+ actualOrder.clear();
+ root.forAllTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ return false;
+ });
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
- SurfacelessDisplayArea(WindowManagerService wms, Type type, String name) {
- super(wms, type, name);
- }
+ // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>, boolean)
+ actualOrder.clear();
+ root.forAllTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ return false;
+ }, false /* traverseTopToBottom */);
- @Override
- SurfaceControl.Builder makeChildSurface(WindowContainer child) {
- return new MockSurfaceControlBuilder();
- }
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
+
+ // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R)
+ actualOrder.clear();
+ root.reduceOnAllTaskDisplayAreas((tda, result) -> {
+ actualOrder.add(tda);
+ return result;
+ }, 0 /* initValue */);
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
+
+ // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R, boolean)
+ actualOrder.clear();
+ root.reduceOnAllTaskDisplayAreas((tda, result) -> {
+ actualOrder.add(tda);
+ return result;
+ }, 0 /* initValue */, false /* traverseTopToBottom */);
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
+
+ // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback)
+ actualOrder.clear();
+ root.getItemFromTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ return null;
+ });
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
+
+ // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, boolean)
+ actualOrder.clear();
+ root.getItemFromTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ return null;
+ }, false /* traverseTopToBottom */);
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
+ }
+
+ @Test
+ public void testForAllTaskDisplayAreas_returnsWhenCallbackReturnTrue() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA2", FEATURE_VENDOR_FIRST);
+ root.addChild(tda1, POSITION_TOP);
+ root.addChild(tda2, POSITION_TOP);
+
+ /* The hierarchy looks like this
+ Root
+ - TDA1 ------ bottom
+ - TDA2 ------ top
+ */
+
+ root.forAllTaskDisplayAreas(tda -> {
+ assertThat(tda).isEqualTo(tda2);
+ return true;
+ });
+
+ root.forAllTaskDisplayAreas(tda -> {
+ assertThat(tda).isEqualTo(tda1);
+ return true;
+ }, false /* traverseTopToBottom */);
+ }
+
+ @Test
+ public void testReduceOnAllTaskDisplayAreas_returnsTheAccumulativeResult() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA2", FEATURE_VENDOR_FIRST);
+ root.addChild(tda1, POSITION_TOP);
+ root.addChild(tda2, POSITION_TOP);
+
+ /* The hierarchy looks like this
+ Root
+ - TDA1 ------ bottom
+ - TDA2 ------ top
+ */
+
+ String accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) ->
+ result + tda.getName(), "" /* initValue */);
+ assertThat(accumulativeName).isEqualTo("TDA2TDA1");
+
+ accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) ->
+ result + tda.getName(), "" /* initValue */, false /* traverseTopToBottom */);
+ assertThat(accumulativeName).isEqualTo("TDA1TDA2");
+ }
+
+ @Test
+ public void testGetItemFromTaskDisplayAreas_returnsWhenCallbackReturnNotNull() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA2", FEATURE_VENDOR_FIRST);
+ root.addChild(tda1, POSITION_TOP);
+ root.addChild(tda2, POSITION_TOP);
+
+ /* The hierarchy looks like this
+ Root
+ - TDA1 ------ bottom
+ - TDA2 ------ top
+ */
+
+ TaskDisplayArea result = root.getItemFromTaskDisplayAreas(tda -> {
+ assertThat(tda).isEqualTo(tda2);
+ return tda;
+ });
+
+ assertThat(result).isEqualTo(tda2);
+
+ result = root.getItemFromTaskDisplayAreas(tda -> {
+ assertThat(tda).isEqualTo(tda1);
+ return tda;
+ }, false /* traverseTopToBottom */);
+
+ assertThat(result).isEqualTo(tda1);
+ }
+
+ private WindowToken createWindowToken(int type) {
+ return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
+ type, false /* persist */, null /* displayContent */,
+ false /* canManageTokens */);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 3041bc973a15..96ea64667f6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -43,6 +43,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
@@ -95,13 +96,10 @@ import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.Gravity;
-import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowRotationCallback;
import android.view.IDisplayWindowRotationController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
-import android.view.InsetsSourceControl;
-import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl.Transaction;
@@ -135,6 +133,7 @@ import java.util.List;
@RunWith(WindowTestRunner.class)
public class DisplayContentTests extends WindowTestsBase {
+ @UseTestDisplay(addAllCommonWindows = true)
@Test
public void testForAllWindows() {
final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
@@ -145,7 +144,7 @@ public class DisplayContentTests extends WindowTestsBase {
waitUntilHandlersIdle();
exitingApp.mIsExiting = true;
- exitingApp.getTask().getStack().mExitingActivities.add(exitingApp);
+ exitingApp.getTask().getRootTask().mExitingActivities.add(exitingApp);
assertForAllWindowsOrder(Arrays.asList(
mWallpaperWindow,
@@ -161,6 +160,7 @@ public class DisplayContentTests extends WindowTestsBase {
mNavBarWindow));
}
+ @UseTestDisplay(addAllCommonWindows = true)
@Test
public void testForAllWindows_WithAppImeTarget() {
final WindowState imeAppTarget =
@@ -182,6 +182,7 @@ public class DisplayContentTests extends WindowTestsBase {
mNavBarWindow));
}
+ @UseTestDisplay(addAllCommonWindows = true)
@Test
public void testForAllWindows_WithChildWindowImeTarget() throws Exception {
mDisplayContent.mInputMethodTarget = mChildAppWindowAbove;
@@ -199,6 +200,7 @@ public class DisplayContentTests extends WindowTestsBase {
mNavBarWindow));
}
+ @UseTestDisplay(addAllCommonWindows = true)
@Test
public void testForAllWindows_WithStatusBarImeTarget() throws Exception {
mDisplayContent.mInputMethodTarget = mStatusBarWindow;
@@ -216,6 +218,7 @@ public class DisplayContentTests extends WindowTestsBase {
mNavBarWindow));
}
+ @UseTestDisplay(addAllCommonWindows = true)
@Test
public void testForAllWindows_WithNotificationShadeImeTarget() throws Exception {
mDisplayContent.mInputMethodTarget = mNotificationShadeWindow;
@@ -233,6 +236,7 @@ public class DisplayContentTests extends WindowTestsBase {
mNavBarWindow));
}
+ @UseTestDisplay(addAllCommonWindows = true)
@Test
public void testForAllWindows_WithInBetweenWindowToken() {
// This window is set-up to be z-ordered between some windows that go in the same token like
@@ -254,6 +258,7 @@ public class DisplayContentTests extends WindowTestsBase {
mNavBarWindow));
}
+ @UseTestDisplay(addAllCommonWindows = true)
@Test
public void testComputeImeTarget() {
// Verify that an app window can be an ime target.
@@ -273,6 +278,39 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(childWin, imeTarget);
}
+ @UseTestDisplay(addAllCommonWindows = true)
+ @Test
+ public void testComputeImeTarget_startingWindow() {
+ ActivityRecord activity = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+
+ final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
+ "startingWin");
+ startingWin.setHasSurface(true);
+ assertTrue(startingWin.canBeImeTarget());
+
+ WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
+ assertEquals(startingWin, imeTarget);
+ startingWin.mHidden = false;
+
+ // Verify that an app window launching behind the starting window becomes the target
+ final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "appWin");
+ appWin.setHasSurface(true);
+ assertTrue(appWin.canBeImeTarget());
+
+ imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
+ assertEquals(appWin, imeTarget);
+ appWin.mHidden = false;
+
+ // Verify that an child window can be an ime target even behind a launching app window
+ final WindowState childWin = createWindow(appWin,
+ TYPE_APPLICATION_ATTACHED_DIALOG, "childWin");
+ childWin.setHasSurface(true);
+ assertTrue(childWin.canBeImeTarget());
+ imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
+ assertEquals(childWin, imeTarget);
+ }
+
/**
* This tests stack movement between displays and proper stack's, task's and app token's display
* container references updates.
@@ -283,7 +321,7 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayContent dc = createNewDisplay();
// Add stack with activity.
- final ActivityStack stack = createTaskStackOnDisplay(dc);
+ final Task stack = createTaskStackOnDisplay(dc);
assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId());
assertEquals(dc, stack.getDisplayContent());
@@ -357,7 +395,7 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayContent dc1 = createNewDisplay();
// Add stack with activity.
- final ActivityStack stack0 = createTaskStackOnDisplay(dc0);
+ final Task stack0 = createTaskStackOnDisplay(dc0);
final Task task0 = createTaskInStack(stack0, 0 /* userId */);
final ActivityRecord activity =
WindowTestUtils.createTestActivityRecord(dc0);
@@ -365,7 +403,7 @@ public class DisplayContentTests extends WindowTestsBase {
dc0.configureDisplayPolicy();
assertNotNull(dc0.mTapDetector);
- final ActivityStack stack1 = createTaskStackOnDisplay(dc1);
+ final Task stack1 = createTaskStackOnDisplay(dc1);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
final ActivityRecord activity1 =
WindowTestUtils.createTestActivityRecord(dc0);
@@ -449,12 +487,17 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
TYPE_WALLPAPER, TYPE_APPLICATION);
+ final WindowState wallpaper = windows[0];
+ assertTrue(wallpaper.mIsWallpaper);
+ // By default WindowState#mWallpaperVisible is false.
+ assertFalse(wallpaper.isVisible());
// Verify waiting for windows to be drawn.
assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
- // Verify not waiting for drawn windows.
- makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
+ // Verify not waiting for drawn window and invisible wallpaper.
+ setDrawnState(WindowStateAnimator.READY_TO_SHOW, wallpaper);
+ setDrawnState(WindowStateAnimator.HAS_DRAWN, windows[1]);
assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
}
@@ -475,26 +518,10 @@ public class DisplayContentTests extends WindowTestsBase {
assertTrue(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
// Verify not waiting for drawn windows on display with system decorations.
- makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
+ setDrawnState(WindowStateAnimator.HAS_DRAWN, windows);
assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
}
- @Test
- public void testShouldWaitForSystemDecorWindowsOnBoot_OnWindowReadyToShowAndDrawn() {
- mWm.mSystemBooted = true;
- final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
- final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
- TYPE_WALLPAPER, TYPE_APPLICATION);
-
- // Verify waiting for windows to be drawn.
- makeWindowsDrawnState(windows, WindowStateAnimator.READY_TO_SHOW);
- assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
-
- // Verify not waiting for drawn windows.
- makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
- assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
- }
-
private WindowState[] createNotDrawnWindowsOn(DisplayContent displayContent, int... types) {
final WindowState[] windows = new WindowState[types.length];
for (int i = 0; i < types.length; i++) {
@@ -505,9 +532,9 @@ public class DisplayContentTests extends WindowTestsBase {
return windows;
}
- private static void makeWindowsDrawnState(WindowState[] windows, int state) {
+ private static void setDrawnState(int state, WindowState... windows) {
for (WindowState window : windows) {
- window.mHasSurface = true;
+ window.mHasSurface = state != WindowStateAnimator.NO_SURFACE;
window.mWinAnimator.mDrawState = state;
}
}
@@ -550,6 +577,58 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testSetForcedSize() {
+ // Prevent base display metrics for test from being updated to the value of real display.
+ final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
+ final int baseWidth = 1280;
+ final int baseHeight = 720;
+ final int baseDensity = 320;
+
+ displayContent.mInitialDisplayWidth = baseWidth;
+ displayContent.mInitialDisplayHeight = baseHeight;
+ displayContent.mInitialDisplayDensity = baseDensity;
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+
+ final int forcedWidth = 1920;
+ final int forcedHeight = 1080;
+
+ // Verify that forcing the size is honored and the density doesn't change.
+ displayContent.setForcedSize(forcedWidth, forcedHeight);
+ verifySizes(displayContent, forcedWidth, forcedHeight, baseDensity);
+
+ // Verify that forcing the size is idempotent.
+ displayContent.setForcedSize(forcedWidth, forcedHeight);
+ verifySizes(displayContent, forcedWidth, forcedHeight, baseDensity);
+ }
+
+ @Test
+ public void testSetForcedSize_WithMaxUiWidth() {
+ // Prevent base display metrics for test from being updated to the value of real display.
+ final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
+ final int baseWidth = 1280;
+ final int baseHeight = 720;
+ final int baseDensity = 320;
+
+ displayContent.mInitialDisplayWidth = baseWidth;
+ displayContent.mInitialDisplayHeight = baseHeight;
+ displayContent.mInitialDisplayDensity = baseDensity;
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+
+ displayContent.setMaxUiWidth(baseWidth);
+
+ final int forcedWidth = 1920;
+ final int forcedHeight = 1080;
+
+ // Verify that forcing bigger size doesn't work and density doesn't change.
+ displayContent.setForcedSize(forcedWidth, forcedHeight);
+ verifySizes(displayContent, baseWidth, baseHeight, baseDensity);
+
+ // Verify that forcing the size is idempotent.
+ displayContent.setForcedSize(forcedWidth, forcedHeight);
+ verifySizes(displayContent, baseWidth, baseHeight, baseDensity);
+ }
+
+ @Test
public void testDisplayCutout_rot0() {
final DisplayContent dc = createNewDisplay();
dc.mInitialDisplayWidth = 200;
@@ -704,6 +783,7 @@ public class DisplayContentTests extends WindowTestsBase {
.setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null);
}
+ @UseTestDisplay
@Test
public void testClearLastFocusWhenReparentingFocusedWindow() {
final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
@@ -737,6 +817,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(isOptionsPanelAtRight(landscapeDisplay.getDisplayId()));
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
@Test
public void testInputMethodTargetUpdateWhenSwitchingOnDisplays() {
final DisplayContent newDisplay = createNewDisplay();
@@ -768,13 +849,13 @@ public class DisplayContentTests extends WindowTestsBase {
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
- final ActivityStack stack =
+ final Task stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
.setDisplay(dc)
.build();
doReturn(true).when(stack).isVisible();
- final ActivityStack freeformStack =
+ final Task freeformStack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
.setDisplay(dc)
.setWindowingMode(WINDOWING_MODE_FREEFORM)
@@ -800,7 +881,7 @@ public class DisplayContentTests extends WindowTestsBase {
IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
final int newOrientation = getRotatedOrientation(dc);
- final ActivityStack stack =
+ final Task stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
.setDisplay(dc).build();
final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity();
@@ -820,7 +901,7 @@ public class DisplayContentTests extends WindowTestsBase {
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
final int newOrientation = getRotatedOrientation(dc);
- final ActivityStack stack =
+ final Task stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
.setDisplay(dc).build();
final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity();
@@ -836,6 +917,7 @@ public class DisplayContentTests extends WindowTestsBase {
public void testComputeImeParent_app() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ dc.mInputMethodInputTarget = dc.mInputMethodTarget;
assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(),
dc.computeImeParent());
}
@@ -849,6 +931,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testComputeImeParent_app_notMatchParentBounds() {
spyOn(mAppWindow.mActivityRecord);
@@ -867,6 +950,21 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+
+ WindowState app = createWindow(null, TYPE_BASE_APPLICATION, dc, "app");
+
+ dc.mInputMethodInputTarget = app;
+ assertEquals(app, dc.computeImeControlTarget());
+
+ app.removeImmediately();
+
+ assertNull(dc.mInputMethodInputTarget);
+ assertNull(dc.computeImeControlTarget());
+ }
+
+ @Test
public void testComputeImeControlTarget() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.setRemoteInsetsController(createDisplayWindowInsetsController());
@@ -886,6 +984,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertNotEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testComputeImeControlTarget_notMatchParentBounds() throws Exception {
spyOn(mAppWindow.mActivityRecord);
@@ -896,28 +995,6 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
}
- private IDisplayWindowInsetsController createDisplayWindowInsetsController() {
- return new IDisplayWindowInsetsController.Stub() {
-
- @Override
- public void insetsChanged(InsetsState insetsState) throws RemoteException {
- }
-
- @Override
- public void insetsControlChanged(InsetsState insetsState,
- InsetsSourceControl[] insetsSourceControls) throws RemoteException {
- }
-
- @Override
- public void showInsets(int i, boolean b) throws RemoteException {
- }
-
- @Override
- public void hideInsets(int i, boolean b) throws RemoteException {
- }
- };
- }
-
@Test
public void testUpdateSystemGestureExclusion() throws Exception {
final DisplayContent dc = createNewDisplay();
@@ -1026,6 +1103,7 @@ public class DisplayContentTests extends WindowTestsBase {
win.setHasSurface(false);
}
+ @UseTestDisplay(addWindows = { W_ABOVE_ACTIVITY, W_ACTIVITY})
@Test
public void testRequestResizeForEmptyFrames() {
final WindowState win = mChildAppWindowAbove;
@@ -1065,6 +1143,7 @@ public class DisplayContentTests extends WindowTestsBase {
is(Configuration.ORIENTATION_PORTRAIT));
}
+ @UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR })
@Test
public void testApplyTopFixedRotationTransform() {
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
@@ -1164,7 +1243,9 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testFinishFixedRotationNoAppTransitioningTask() {
- final ActivityRecord app = mAppWindow.mActivityRecord;
+ unblockDisplayRotation(mDisplayContent);
+ final ActivityRecord app = createActivityRecord(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
final Task task = app.getTask();
final ActivityRecord app2 = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
.setTask(task).build();
@@ -1185,6 +1266,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp());
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testRotateSeamlesslyWithFixedRotation() {
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
@@ -1205,14 +1287,14 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testNoFixedRotationWithPip() {
+ final DisplayContent displayContent = mDefaultDisplay;
+ unblockDisplayRotation(displayContent);
// Make resume-top really update the activity state.
- doReturn(false).when(mWm.mAtmService).isBooting();
- doReturn(true).when(mWm.mAtmService).isBooted();
+ setBooted(mWm.mAtmService);
// Speed up the test by a few seconds.
mWm.mAtmService.deferWindowLayout();
doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
- final DisplayContent displayContent = mWm.mRoot.getDefaultDisplay();
final Configuration displayConfig = displayContent.getConfiguration();
final ActivityRecord pinnedActivity = createActivityRecord(displayContent,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
@@ -1235,7 +1317,7 @@ public class DisplayContentTests extends WindowTestsBase {
// Leave PiP to fullscreen. The orientation can be updated from
// ActivityRecord#reportDescendantOrientationChangeIfNeeded.
pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- homeActivity.setState(ActivityStack.ActivityState.STOPPED, "test");
+ homeActivity.setState(Task.ActivityState.STOPPED, "test");
assertFalse(displayContent.hasTopFixedRotationLaunchingApp());
verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
@@ -1255,11 +1337,13 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testRecentsNotRotatingWithFixedRotation() {
+ unblockDisplayRotation(mDisplayContent);
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
- doCallRealMethod().when(displayRotation).updateRotationUnchecked(anyBoolean());
// Skip freezing so the unrelated conditions in updateRotationUnchecked won't disturb.
doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS);
recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
@@ -1276,12 +1360,12 @@ public class DisplayContentTests extends WindowTestsBase {
// Rotation can be updated if the recents animation is animating but it is not on top, e.g.
// switching activities in different orientations by quickstep gesture.
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
- mDisplayContent.setFixedRotationLaunchingAppUnchecked(mAppWindow.mActivityRecord);
+ mDisplayContent.setFixedRotationLaunchingAppUnchecked(activity);
displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
assertTrue(displayRotation.updateRotationUnchecked(false));
// The recents activity should not apply fixed rotation if the top activity is not opaque.
- mDisplayContent.mFocusedApp = mAppWindow.mActivityRecord;
+ mDisplayContent.mFocusedApp = activity;
doReturn(false).when(mDisplayContent.mFocusedApp).occludesParent();
doReturn(ROTATION_90).when(mDisplayContent).rotationForActivityInDifferentOrientation(
eq(recentsActivity));
@@ -1295,9 +1379,10 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayRotation dr = dc.getDisplayRotation();
doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
- Mockito.doReturn(ROTATION_90).when(dr).rotationForOrientation(anyInt(), anyInt());
+ // Rotate 180 degree so the display doesn't have configuration change. This condition is
+ // used for the later verification of stop-freezing (without setting mWaitingForConfig).
+ doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt());
final boolean[] continued = new boolean[1];
- // TODO(display-merge): Remove cast
doAnswer(
invocation -> {
continued[0] = true;
@@ -1323,9 +1408,16 @@ public class DisplayContentTests extends WindowTestsBase {
dc.setRotationAnimation(null);
mWm.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */);
+ // If remote rotation is not finished, the display should not be able to unfreeze.
+ mWm.stopFreezingDisplayLocked();
+ assertTrue(mWm.mDisplayFrozen);
+
assertTrue(called[0]);
waitUntilHandlersIdle();
assertTrue(continued[0]);
+
+ mWm.stopFreezingDisplayLocked();
+ assertFalse(mWm.mDisplayFrozen);
}
@Test
@@ -1333,7 +1425,7 @@ public class DisplayContentTests extends WindowTestsBase {
TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea();
// Remove the current home stack if it exists so a new one can be created below.
- ActivityStack homeTask = defaultTaskDisplayArea.getRootHomeTask();
+ Task homeTask = defaultTaskDisplayArea.getRootHomeTask();
if (homeTask != null) {
defaultTaskDisplayArea.removeChild(homeTask);
}
@@ -1349,7 +1441,7 @@ public class DisplayContentTests extends WindowTestsBase {
// Remove the current home stack if it exists so a new one can be created below.
TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
- ActivityStack homeTask = taskDisplayArea.getRootHomeTask();
+ Task homeTask = taskDisplayArea.getRootHomeTask();
if (homeTask != null) {
taskDisplayArea.removeChild(homeTask);
}
@@ -1381,7 +1473,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testFindScrollCaptureTargetWindow_behindWindow() {
DisplayContent display = createNewDisplay();
- ActivityStack stack = createTaskStackOnDisplay(display);
+ Task stack = createTaskStackOnDisplay(display);
Task task = createTaskInStack(stack, 0 /* userId */);
WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
@@ -1394,7 +1486,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testFindScrollCaptureTargetWindow_taskId() {
DisplayContent display = createNewDisplay();
- ActivityStack stack = createTaskStackOnDisplay(display);
+ Task stack = createTaskStackOnDisplay(display);
Task task = createTaskInStack(stack, 0 /* userId */);
WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
@@ -1406,7 +1498,6 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testEnsureActivitiesVisibleNotRecursive() {
final TaskDisplayArea mockTda = mock(TaskDisplayArea.class);
- doReturn(mockTda).when(mDisplayContent).getTaskDisplayAreaAt(anyInt());
final boolean[] called = { false };
doAnswer(invocation -> {
// The assertion will fail if DisplayArea#ensureActivitiesVisible is called twice.
@@ -1419,15 +1510,33 @@ public class DisplayContentTests extends WindowTestsBase {
mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
}
+ @Test
+ public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() {
+ final DisplayContent dc = createNewDisplay();
+ final Task stack =
+ new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
+ .setDisplay(dc)
+ .build();
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ final Configuration config = ((Configuration) args[0]);
+ assertEquals(config.windowConfiguration.getWindowingMode(),
+ config.windowConfiguration.getDisplayWindowingMode());
+ return null;
+ }).when(stack).onConfigurationChanged(any());
+ dc.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+
private boolean isOptionsPanelAtRight(int displayId) {
return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
}
private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
int expectedBaseHeight, int expectedBaseDensity) {
- assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth);
- assertEquals(displayContent.mBaseDisplayHeight, expectedBaseHeight);
- assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
+ assertEquals(expectedBaseWidth, displayContent.mBaseDisplayWidth);
+ assertEquals(expectedBaseHeight, displayContent.mBaseDisplayHeight);
+ assertEquals(expectedBaseDensity, displayContent.mBaseDisplayDensity);
}
private void updateFocusedWindow() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index 39cd76aeef9e..402fd22416fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -36,6 +36,8 @@ import org.junit.runner.RunWith;
@SmallTest
@Presubmit
+@WindowTestsBase.UseTestDisplay(
+ addWindows = { WindowTestsBase.W_STATUS_BAR, WindowTestsBase.W_NAVIGATION_BAR })
@RunWith(WindowTestRunner.class)
public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 1922351ac1eb..4ea5b97decf4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -40,14 +40,22 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DEC
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
+import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.testng.Assert.expectThrows;
@@ -60,6 +68,7 @@ import android.util.Pair;
import android.util.SparseArray;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
+import android.view.Gravity;
import android.view.InsetsState;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
@@ -67,7 +76,6 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Before;
@@ -85,6 +93,8 @@ import java.io.StringWriter;
*/
@SmallTest
@Presubmit
+@WindowTestsBase.UseTestDisplay(
+ addWindows = { WindowTestsBase.W_STATUS_BAR, WindowTestsBase.W_NAVIGATION_BAR })
@RunWith(WindowTestRunner.class)
public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@@ -95,6 +105,8 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
private boolean mIsLongEdgeDisplayCutout;
private static final int DECOR_WINDOW_INSET = 50;
+ private final Rect mDisplayBounds = new Rect();
+
@Before
public void setUp() throws Exception {
mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
@@ -107,12 +119,23 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
attrs.height = MATCH_PARENT;
attrs.format = PixelFormat.TRANSLUCENT;
+ spyOn(mStatusBarWindow);
+ spyOn(mNavBarWindow);
+
+ // Disabling this call for most tests since it can override the systemUiFlags when called.
+ doReturn(0).when(mDisplayPolicy).updateSystemUiVisibilityLw();
+
updateDisplayFrames();
}
- public void setRotation(int rotation) {
+ public void setRotation(int rotation, boolean includingWindows) {
mRotation = rotation;
updateDisplayFrames();
+ if (includingWindows) {
+ mNavBarWindow.getWindowConfiguration().setRotation(rotation);
+ mStatusBarWindow.getWindowConfiguration().setRotation(rotation);
+ mWindow.getWindowConfiguration().setRotation(rotation);
+ }
}
public void addDisplayCutout() {
@@ -128,7 +151,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
private void updateDisplayFrames() {
mFrames = createDisplayFrames();
+ mDisplayBounds.set(0, 0, mFrames.mDisplayWidth, mFrames.mDisplayHeight);
mDisplayContent.mDisplayFrames = mFrames;
+ mDisplayContent.setBounds(mDisplayBounds);
}
private DisplayFrames createDisplayFrames() {
@@ -148,6 +173,8 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void addingWindow_withInsetsTypes() {
+ mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one.
+
WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
win.getFrameLw().set(0, 0, 500, 100);
@@ -197,6 +224,47 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
}
@Test
+ public void addingWindow_variousGravities_alternateBarPosUpdated() {
+ mDisplayPolicy.removeWindowLw(mNavBarWindow); // Removes the existing one.
+
+ WindowState win1 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel1");
+ win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
+ win1.mAttrs.gravity = Gravity.TOP;
+ win1.getFrameLw().set(0, 0, 200, 500);
+ addWindow(win1);
+
+ assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_TOP);
+ mDisplayPolicy.removeWindowLw(win1);
+
+ WindowState win2 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel2");
+ win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
+ win2.mAttrs.gravity = Gravity.BOTTOM;
+ win2.getFrameLw().set(0, 0, 200, 500);
+ addWindow(win2);
+
+ assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_BOTTOM);
+ mDisplayPolicy.removeWindowLw(win2);
+
+ WindowState win3 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel3");
+ win3.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
+ win3.mAttrs.gravity = Gravity.LEFT;
+ win3.getFrameLw().set(0, 0, 200, 500);
+ addWindow(win3);
+
+ assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_LEFT);
+ mDisplayPolicy.removeWindowLw(win3);
+
+ WindowState win4 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel4");
+ win4.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
+ win4.mAttrs.gravity = Gravity.RIGHT;
+ win4.getFrameLw().set(0, 0, 200, 500);
+ addWindow(win4);
+
+ assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_RIGHT);
+ mDisplayPolicy.removeWindowLw(win4);
+ }
+
+ @Test
public void layoutWindowLw_fitStatusBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
addWindow(mWindow);
@@ -469,8 +537,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_withDisplayCutout_landscape() {
addDisplayCutout();
- setRotation(ROTATION_90);
-
+ setRotation(ROTATION_90, true /* includingWindows */);
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
@@ -490,7 +557,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_withDisplayCutout_seascape() {
addDisplayCutout();
- setRotation(ROTATION_270);
+ setRotation(ROTATION_270, true /* includingWindows */);
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -511,7 +578,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
addDisplayCutout();
- setRotation(ROTATION_90);
+ setRotation(ROTATION_90, true /* includingWindows */);
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -552,7 +619,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
addDisplayCutout();
- setRotation(ROTATION_90);
+ setRotation(ROTATION_90, true /* includingWindows */);
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -770,7 +837,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void testSimulateLayoutDisplay() {
assertSimulateLayoutSameDisplayFrames();
- setRotation(ROTATION_90);
+ setRotation(ROTATION_90, false /* includingWindows */);
assertSimulateLayoutSameDisplayFrames();
addDisplayCutout();
assertSimulateLayoutSameDisplayFrames();
@@ -807,34 +874,16 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
}
@Test
- public void forceShowSystemBars_clearsSystemUIFlags() {
- mDisplayPolicy.mLastSystemUiFlags |= SYSTEM_UI_FLAG_FULLSCREEN;
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mSystemUiVisibility = SYSTEM_UI_FLAG_FULLSCREEN;
- mDisplayPolicy.setForceShowSystemBars(true);
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- // triggers updateSystemUiVisibilityLw which will reset the flags as needed
- int finishPostLayoutPolicyLw = mDisplayPolicy.focusChangedLw(mWindow, mWindow);
-
- assertEquals(WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT, finishPostLayoutPolicyLw);
- assertEquals(0, mDisplayPolicy.mLastSystemUiFlags);
- assertEquals(0, mWindow.mAttrs.systemUiVisibility);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- }
-
- @Test
public void testScreenDecorWindows() {
- final WindowState decorWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow");
+ final WindowState decorWindow = spy(
+ createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow"));
mWindow.mAttrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR
| FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
decorWindow.mAttrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
addWindow(decorWindow);
addWindow(mWindow);
+ doReturn(new Rect(0, 0, mFrames.mDisplayWidth, mFrames.mDisplayHeight))
+ .when(decorWindow).getBounds();
// Decor on top
updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index a3f9b2e71cb5..4483f8c341cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -222,6 +222,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testComputeTopFullscreenOpaqueWindow() {
final WindowManager.LayoutParams attrs = mAppWindow.mAttrs;
@@ -320,6 +321,8 @@ public class DisplayPolicyTests extends WindowTestsBase {
return win;
}
+ @UseTestDisplay(
+ addWindows = { W_ACTIVITY, W_STATUS_BAR, W_NAVIGATION_BAR, W_NOTIFICATION_SHADE })
@Test
public void testUpdateHideNavInputEventReceiver() {
final InsetsPolicy insetsPolicy = mDisplayContent.getInsetsPolicy();
@@ -358,6 +361,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
assertNull(displayPolicy.mInputConsumer);
}
+ @UseTestDisplay(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
@Test
public void testImeMinimalSourceFrame() {
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 7ba3fd815b2d..b4e1c375993d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -27,7 +27,7 @@ import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
import static org.junit.Assert.assertEquals;
@@ -68,7 +68,12 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
@Before
public void setUpDisplayPolicy() {
- mDisplayPolicy = spy(mDisplayContent.getDisplayPolicy());
+ // Disable surface placement because it has no direct relation to layout policy and it also
+ // avoids some noises such as the display info is modified, screen frozen, config change.
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ mDisplayPolicy = mDisplayContent.getDisplayPolicy();
+ spyOn(mDisplayPolicy);
final TestContextWrapper context = new TestContextWrapper(
mDisplayPolicy.getContext(), mDisplayPolicy.getCurrentUserResources());
@@ -99,6 +104,9 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
addWindow(mNavBarWindow);
mDisplayPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
+
+ // Update source frame and visibility of insets providers.
+ mDisplayContent.getInsetsStateController().onPostLayout();
}
void addWindow(WindowState win) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 11c02c26ca97..a3d3739a9015 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -77,6 +77,7 @@ import java.nio.charset.StandardCharsets;
*/
@SmallTest
@Presubmit
+@WindowTestsBase.UseTestDisplay
@RunWith(WindowTestRunner.class)
public class DisplayWindowSettingsTests extends WindowTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 0eee3ca53c7d..e18d93d82686 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -40,7 +40,6 @@ import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.LocalServices;
@@ -98,7 +97,7 @@ public class DragDropControllerTests extends WindowTestsBase {
private WindowState createDropTargetWindow(String name, int ownerId) {
final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(
mDisplayContent);
- final ActivityStack stack = createTaskStackOnDisplay(
+ final Task stack = createTaskStackOnDisplay(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
final Task task = createTaskInStack(stack, ownerId);
task.addChild(activity, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index c794e1a3b328..555906d4c910 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -172,8 +172,9 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
@Test
- public void testControlsForDispatch_forceShowSystemBarsFromExternal_appHasNoControl() {
- mDisplayContent.getDisplayPolicy().setForceShowSystemBars(true);
+ public void testControlsForDispatch_remoteInsetsControllerControlsBars_appHasNoControl() {
+ mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
+ mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(true);
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
@@ -224,6 +225,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
assertEquals(ITYPE_STATUS_BAR, fullscreenAppControls[0].getType());
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() {
addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
@@ -258,6 +260,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
.getSource(ITYPE_NAVIGATION_BAR).isVisible());
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testShowTransientBars_statusBarCanBeTransient_appGetsStatusBarFakeControl() {
addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
@@ -287,6 +290,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() {
addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 0a27e1a1da68..5e83e66536ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -147,6 +147,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
@Test
public void testStripForDispatch_independentSources() {
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
@@ -162,6 +163,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
assertTrue(getController().getInsetsForDispatch(app1).getSource(ITYPE_IME).isVisible());
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
@Test
public void testStripForDispatch_belowIme() {
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
@@ -173,6 +175,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
@Test
public void testStripForDispatch_aboveIme() {
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
@@ -184,6 +187,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
@Test
public void testStripForDispatch_imeOrderChanged() {
// This can be the IME z-order target while app cannot be the IME z-order target.
@@ -232,6 +236,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
@Test
public void testStripForDispatch_childWindow_altFocusable() {
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
@@ -249,6 +254,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
@Test
public void testStripForDispatch_childWindow_splitScreen() {
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index 61de7d83fa1a..a7a8505e336d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -309,12 +309,12 @@ public class LaunchParamsControllerTests extends ActivityTestsBase {
mController.registerModifier(positioner);
- final int beforeWindowMode = task.getStack().getWindowingMode();
+ final int beforeWindowMode = task.getRootTask().getWindowingMode();
assertNotEquals(windowingMode, beforeWindowMode);
mController.layoutTask(task, null /* windowLayout */);
- final int afterWindowMode = task.getStack().getWindowingMode();
+ final int afterWindowMode = task.getRootTask().getWindowingMode();
assertEquals(windowingMode, afterWindowMode);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 9bf86d2c4704..e389a538f25d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -114,7 +114,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId)))
.thenReturn(mTestDisplay);
- ActivityStack stack = mTestDisplay.getDefaultTaskDisplayArea()
+ Task stack = mTestDisplay.getDefaultTaskDisplayArea()
.createStack(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).setStack(stack)
.build();
@@ -337,7 +337,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
public void testClearsRecordsOfTheUserOnUserCleanUp() {
mTarget.saveTask(mTestTask);
- ActivityStack stack = mTestDisplay.getDefaultTaskDisplayArea().createStack(
+ Task stack = mTestDisplay.getDefaultTaskDisplayArea().createStack(
TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
final Task anotherTaskOfTheSameUser = new TaskBuilder(mSupervisor)
.setComponent(ALTERNATIVE_COMPONENT)
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 2251ee047d29..2f3004bf6832 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -54,11 +54,16 @@ public class LetterboxTest {
mTransaction = spy(StubTransaction.class);
}
+ @Test
+ public void testOverlappingWith_usesGlobalCoordinates() {
+ mLetterbox.layout(new Rect(0, 0, 10, 50), new Rect(0, 2, 10, 45), new Point(1000, 2000));
+ assertTrue(mLetterbox.isOverlappingWith(new Rect(0, 0, 1, 1)));
+ }
+
private static final int TOP_BAR = 0x1;
private static final int BOTTOM_BAR = 0x2;
private static final int LEFT_BAR = 0x4;
private static final int RIGHT_BAR = 0x8;
-
@Test
public void testNotIntersectsOrFullyContains_usesGlobalCoordinates() {
final Rect outer = new Rect(0, 0, 10, 50);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 5005c07832ab..1724303633d9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -105,7 +105,7 @@ public class RecentTasksTest extends ActivityTestsBase {
private static final int INVALID_STACK_ID = 999;
private TaskDisplayArea mTaskContainer;
- private ActivityStack mStack;
+ private Task mStack;
private TestTaskPersister mTaskPersister;
private TestRecentTasks mRecentTasks;
private TestRunningTasks mRunningTasks;
@@ -464,15 +464,19 @@ public class RecentTasksTest extends ActivityTestsBase {
mRecentTasks.add(task1);
final Task task2 = taskBuilder.apply(true /* visible */);
mRecentTasks.add(task2);
- // Only the last task is kept in recents and the previous 2 tasks will becomes untracked
+ final Task task3 = createTaskBuilder(className).build();
+ mRecentTasks.add(task3);
+ // Only the last added task is kept in recents and the previous 2 tasks will become hidden
// tasks because their intents are identical.
- mRecentTasks.add(createTaskBuilder(className).build());
+ mRecentTasks.add(task1);
// Go home to trigger the removal of untracked tasks.
mRecentTasks.add(createTaskBuilder(".Home").setStack(mTaskContainer.getRootHomeTask())
.build());
+ // The task was added into recents again so it is not hidden and shouldn't be removed.
+ assertNotNull(task1.getTopNonFinishingActivity());
// All activities in the invisible task should be finishing or removed.
- assertNull(task1.getTopNonFinishingActivity());
+ assertNull(task3.getTopNonFinishingActivity());
// The visible task should not be affected.
assertNotNull(task2.getTopNonFinishingActivity());
}
@@ -825,7 +829,7 @@ public class RecentTasksTest extends ActivityTestsBase {
mRecentTasks.add(mTasks.get(2));
mRecentTasks.add(mTasks.get(1));
- ActivityStack stack = mTasks.get(2).getStack();
+ Task stack = mTasks.get(2).getRootTask();
stack.moveToFront("", mTasks.get(2));
doReturn(stack).when(mService.mRootWindowContainer).getTopDisplayFocusedStack();
@@ -846,8 +850,8 @@ public class RecentTasksTest extends ActivityTestsBase {
public void testBackStackTasks_expectNoTrim() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
- final ActivityStack homeStack = mTaskContainer.getRootHomeTask();
- final ActivityStack aboveHomeStack = mTaskContainer.createStack(
+ final Task homeStack = mTaskContainer.getRootHomeTask();
+ final Task aboveHomeStack = mTaskContainer.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
@@ -864,10 +868,10 @@ public class RecentTasksTest extends ActivityTestsBase {
public void testBehindHomeStackTasks_expectTaskTrimmed() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
- final ActivityStack behindHomeStack = mTaskContainer.createStack(
+ final Task behindHomeStack = mTaskContainer.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityStack homeStack = mTaskContainer.getRootHomeTask();
- final ActivityStack aboveHomeStack = mTaskContainer.createStack(
+ final Task homeStack = mTaskContainer.getRootHomeTask();
+ final Task aboveHomeStack = mTaskContainer.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
@@ -886,9 +890,9 @@ public class RecentTasksTest extends ActivityTestsBase {
public void testOtherDisplayTasks_expectNoTrim() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
- final ActivityStack homeStack = mTaskContainer.getRootHomeTask();
+ final Task homeStack = mTaskContainer.getRootHomeTask();
final DisplayContent otherDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
- final ActivityStack otherDisplayStack = otherDisplay.getDefaultTaskDisplayArea()
+ final Task otherDisplayStack = otherDisplay.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
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 8e85e7b96d1f..695a0e3881ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -92,15 +92,13 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Mock RecentsAnimationController.RecentsAnimationCallbacks mAnimationCallbacks;
@Mock TaskSnapshot mMockTaskSnapshot;
private RecentsAnimationController mController;
- private DisplayContent mDefaultDisplay;
- private ActivityStack mRootHomeTask;
+ private Task mRootHomeTask;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doNothing().when(mWm.mRoot).performSurfacePlacement();
when(mMockRunner.asBinder()).thenReturn(new Binder());
- mDefaultDisplay = mWm.mRoot.getDefaultDisplay();
mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
DEFAULT_DISPLAY));
mRootHomeTask = mDefaultDisplay.getDefaultTaskDisplayArea().getRootHomeTask();
@@ -321,6 +319,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testRecentViewInFixedPortraitWhenTopAppInLandscape() {
+ unblockDisplayRotation(mDefaultDisplay);
mWm.setRecentsAnimationController(mController);
final ActivityRecord homeActivity = createHomeActivity();
@@ -365,6 +364,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testClearFixedRotationLaunchingAppAfterCleanupAnimation() {
+ unblockDisplayRotation(mDefaultDisplay);
final ActivityRecord homeActivity = createHomeActivity();
homeActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
final ActivityRecord activity = createActivityRecord(mDefaultDisplay,
@@ -389,6 +389,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testWallpaperHasFixedRotationApplied() {
+ unblockDisplayRotation(mDefaultDisplay);
mWm.setRecentsAnimationController(mController);
// Create a portrait home activity, a wallpaper and a landscape activity displayed on top.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 44ca2cdcb142..e5d1e465d8ff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -31,8 +31,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -88,7 +89,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
@Test
public void testRecentsActivityVisiblility() {
TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_RECENTS, true /* onTop */);
ActivityRecord recentActivity = new ActivityBuilder(mService)
.setComponent(mRecentsComponent)
@@ -116,9 +117,10 @@ public class RecentsAnimationTest extends ActivityTestsBase {
@Test
public void testPreloadRecentsActivity() {
TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final ActivityStack homeStack =
+ final Task homeStack =
defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
- defaultTaskDisplayArea.positionStackAtTop(homeStack, false /* includingParents */);
+ defaultTaskDisplayArea.positionChildAt(POSITION_TOP, homeStack,
+ false /* includingParents */);
ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
if (topRunningHomeActivity == null) {
topRunningHomeActivity = new ActivityBuilder(mService)
@@ -148,7 +150,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */,
null /* recentsAnimationRunner */);
- ActivityStack recentsStack = defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN,
+ Task recentsStack = defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_RECENTS);
assertThat(recentsStack).isNotNull();
@@ -177,7 +179,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
public void testRestartRecentsActivity() throws Exception {
// Have a recents activity that is not attached to its process (ActivityRecord.app = null).
TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack recentsStack = defaultTaskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task recentsStack = defaultTaskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_RECENTS, true /* onTop */);
ActivityRecord recentActivity = new ActivityBuilder(mService).setComponent(
mRecentsComponent).setCreateTask(true).setStack(recentsStack).build();
@@ -206,7 +208,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
@Test
public void testSetLaunchTaskBehindOfTargetActivity() {
TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack homeStack = taskDisplayArea.getRootHomeTask();
+ Task homeStack = taskDisplayArea.getRootHomeTask();
// Assume the home activity support recents.
ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity();
if (targetActivity == null) {
@@ -248,21 +250,21 @@ public class RecentsAnimationTest extends ActivityTestsBase {
@Test
public void testCancelAnimationOnVisibleStackOrderChange() {
TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "App1"))
.setCreateTask(true)
.setStack(fullscreenStack)
.build();
- ActivityStack recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_RECENTS, true /* onTop */);
new ActivityBuilder(mService)
.setComponent(mRecentsComponent)
.setCreateTask(true)
.setStack(recentsStack)
.build();
- ActivityStack fullscreenStack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task fullscreenStack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "App2"))
@@ -289,21 +291,21 @@ public class RecentsAnimationTest extends ActivityTestsBase {
@Test
public void testKeepAnimationOnHiddenStackOrderChange() {
TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "App1"))
.setCreateTask(true)
.setStack(fullscreenStack)
.build();
- ActivityStack recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_RECENTS, true /* onTop */);
new ActivityBuilder(mService)
.setComponent(mRecentsComponent)
.setCreateTask(true)
.setStack(recentsStack)
.build();
- ActivityStack fullscreenStack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task fullscreenStack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "App2"))
@@ -326,7 +328,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
public void testMultipleUserHomeActivity_findUserHomeTask() {
TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay()
.getDefaultTaskDisplayArea();
- ActivityStack homeStack = taskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
+ Task homeStack = taskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
ACTIVITY_TYPE_HOME);
ActivityRecord otherUserHomeActivity = new ActivityBuilder(mService)
.setStack(homeStack)
@@ -335,7 +337,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
.build();
otherUserHomeActivity.getTask().mUserId = TEST_USER_ID;
- ActivityStack fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "App1"))
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 add2054d5638..13f04d23ccd3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -46,7 +46,6 @@ import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.testutils.OffsettableClock;
@@ -244,7 +243,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
@Test
- public void testChange() throws Exception {
+ public void testChangeToSmallerSize() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
@@ -279,8 +278,61 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
assertEquals(false, app.isTranslucent);
verify(mMockTransaction).setPosition(
mMockLeash, app.startBounds.left, app.startBounds.top);
- verify(mMockTransaction).setWindowCrop(mMockLeash, 200, 200);
+ verify(mMockTransaction).setWindowCrop(
+ mMockLeash, app.startBounds.width(), app.startBounds.height());
verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+ verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
+ eq(record.mAdapter));
+ verify(mThumbnailFinishedCallback).onAnimationFinished(
+ eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
+ } finally {
+ mDisplayContent.mChangingContainers.clear();
+ }
+ }
+
+ @Test
+ public void testChangeTolargerSize() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mChangingContainers.add(win.mActivityRecord);
+ try {
+ final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(0, 0), null, new Rect(0, 0, 200, 200),
+ new Rect(50, 100, 150, 150));
+ assertNotNull(record.mThumbnailAdapter);
+ ((AnimationAdapter) record.mAdapter)
+ .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
+ mFinishedCallback);
+ ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
+ mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
+ 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, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode);
+ assertEquals(new Point(0, 0), app.position);
+ assertEquals(new Rect(0, 0, 200, 200), app.sourceContainerBounds);
+ assertEquals(new Rect(50, 100, 150, 150), app.startBounds);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(mMockThumbnailLeash, app.startLeash);
+ assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setPosition(
+ mMockLeash, app.startBounds.left, app.startBounds.top);
+ verify(mMockTransaction).setWindowCrop(
+ mMockLeash, app.startBounds.width(), app.startBounds.height());
+ verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+ verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
finishedCaptor.getValue().onAnimationFinished();
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
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 5dba00455913..74be2c979668 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -39,9 +39,12 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -57,14 +60,16 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
import android.util.Pair;
import androidx.test.filters.MediumTest;
import com.android.internal.app.ResolverActivity;
-import com.android.server.wm.ActivityStack.ActivityState;
+import com.android.server.wm.Task.ActivityState;
import org.junit.Before;
import org.junit.Test;
@@ -85,7 +90,7 @@ import java.util.function.Consumer;
@Presubmit
@RunWith(WindowTestRunner.class)
public class RootActivityContainerTests extends ActivityTestsBase {
- private ActivityStack mFullscreenStack;
+ private Task mFullscreenStack;
@Before
public void setUp() throws Exception {
@@ -128,7 +133,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
mRootWindowContainer.moveActivityToPinnedStack(firstActivity, "initialMove");
final TaskDisplayArea taskDisplayArea = mFullscreenStack.getDisplayArea();
- ActivityStack pinnedStack = taskDisplayArea.getRootPinnedTask();
+ Task pinnedStack = taskDisplayArea.getRootPinnedTask();
// Ensure a task has moved over.
ensureStackPlacement(pinnedStack, firstActivity);
ensureStackPlacement(mFullscreenStack, secondActivity);
@@ -145,7 +150,30 @@ public class RootActivityContainerTests extends ActivityTestsBase {
ensureStackPlacement(mFullscreenStack, firstActivity);
}
- private static void ensureStackPlacement(ActivityStack stack, ActivityRecord... activities) {
+ @Test
+ public void testMovingBottomMostStackActivityToPinnedStack() {
+ final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final Task task = firstActivity.getTask();
+
+ final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(task)
+ .setStack(mFullscreenStack).build();
+
+ mFullscreenStack.moveTaskToBack(task);
+
+ // Ensure full screen stack has both tasks.
+ ensureStackPlacement(mFullscreenStack, firstActivity, secondActivity);
+ assertEquals(task.getTopMostActivity(), secondActivity);
+ firstActivity.setState(STOPPED, "testMovingBottomMostStackActivityToPinnedStack");
+
+
+ // Move first activity to pinned stack.
+ mRootWindowContainer.moveActivityToPinnedStack(secondActivity, "initialMove");
+
+ assertTrue(firstActivity.mRequestForceTransition);
+ }
+
+ private static void ensureStackPlacement(Task stack, ActivityRecord... activities) {
final Task task = stack.getBottomMostTask();
final ArrayList<ActivityRecord> stackActivities = new ArrayList<>();
@@ -167,7 +195,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
public void testApplySleepTokens() {
final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final ActivityStack stack = new StackBuilder(mRootWindowContainer)
+ final Task stack = new StackBuilder(mRootWindowContainer)
.setCreateActivity(false)
.setDisplay(display)
.setOnTop(false)
@@ -202,7 +230,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
}
private void verifySleepTokenBehavior(DisplayContent display, KeyguardController keyguard,
- ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
+ Task stack, boolean displaySleeping, boolean displayShouldSleep,
boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
boolean expectResumeTopActivity) {
reset(stack);
@@ -221,6 +249,35 @@ public class RootActivityContainerTests extends ActivityTestsBase {
null /* target */, null /* targetOptions */);
}
+ @Test
+ public void testAwakeFromSleepingWithAppConfiguration() {
+ final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+ final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true).build();
+ activity.moveFocusableActivityToTop("test");
+ assertTrue(activity.getStack().isFocusedStackOnDisplay());
+ ActivityRecordTests.setRotatedScreenOrientationSilently(activity);
+
+ final Configuration rotatedConfig = new Configuration();
+ display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation()
+ .rotationForOrientation(activity.getOrientation(), display.getRotation()));
+ assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
+ // Assume the activity was shown in different orientation. For example, the top activity is
+ // landscape and the portrait lockscreen is shown.
+ activity.setLastReportedConfiguration(
+ new MergedConfiguration(mService.getGlobalConfiguration(), rotatedConfig));
+ activity.setState(ActivityState.STOPPED, "sleep");
+
+ display.setIsSleeping(true);
+ doReturn(false).when(display).shouldSleep();
+ // Allow to resume when awaking.
+ setBooted(mService);
+ mRootWindowContainer.applySleepTokens(true);
+
+ // The display orientation should be changed by the activity so there is no relaunch.
+ verify(activity, never()).relaunchActivityLocked(anyBoolean());
+ assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
+ }
+
/**
* Verifies that removal of activity with task and stack is done correctly.
*/
@@ -229,7 +286,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
.getDefaultTaskDisplayArea();
final int originalStackCount = defaultTaskDisplayArea.getStackCount();
- final ActivityStack stack = defaultTaskDisplayArea.createStack(
+ final Task stack = defaultTaskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
@@ -253,21 +310,16 @@ public class RootActivityContainerTests extends ActivityTestsBase {
final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
.getDefaultTaskDisplayArea();
final int originalStackCount = defaultTaskDisplayArea.getStackCount();
- final ActivityStack stack = defaultTaskDisplayArea.createStack(
+ final Task stack = defaultTaskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getStackCount());
final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent();
- doReturn(2).when(dc).getTaskDisplayAreaCount();
- final TaskDisplayArea secondTaskDisplayArea = new TaskDisplayArea(dc,
- mRootWindowContainer.mWmService, "SecondaryTaskDisplayArea", FEATURE_VENDOR_FIRST);
- // Add second display area right above the default one
- defaultTaskDisplayArea.getParent().addChild(secondTaskDisplayArea,
- defaultTaskDisplayArea.getParent().mChildren.indexOf(defaultTaskDisplayArea) + 1);
- doReturn(secondTaskDisplayArea).when(dc).getTaskDisplayAreaAt(1);
- final ActivityStack secondStack = secondTaskDisplayArea.createStack(
+ final TaskDisplayArea secondTaskDisplayArea = WindowTestsBase.createTaskDisplayArea(dc,
+ mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST);
+ final Task secondStack = secondTaskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
new ActivityBuilder(mService).setCreateTask(true).setStack(secondStack)
.setUseProcess(firstActivity.app).build();
@@ -286,7 +338,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
public void testFocusability() {
final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
.getDefaultTaskDisplayArea();
- final ActivityStack stack = defaultTaskDisplayArea.createStack(
+ final Task stack = defaultTaskDisplayArea.createStack(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
@@ -300,7 +352,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
assertFalse(stack.isTopActivityFocusable());
assertFalse(activity.isFocusable());
- final ActivityStack pinnedStack = defaultTaskDisplayArea.createStack(
+ final Task pinnedStack = defaultTaskDisplayArea.createStack(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(pinnedStack).build();
@@ -329,7 +381,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
@Test
public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
// Create primary split-screen stack with a task and an activity.
- final ActivityStack primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea()
+ final Task primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
final Task task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
@@ -339,7 +391,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
// split-screen secondary.
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
- final ActivityStack result =
+ final Task result =
mRootWindowContainer.getLaunchStack(r, options, task, true /* onTop */);
// Assert that the primary stack is returned.
@@ -352,13 +404,13 @@ public class RootActivityContainerTests extends ActivityTestsBase {
@Test
public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
// Create stack/task on default display.
- final ActivityStack targetStack = new StackBuilder(mRootWindowContainer)
+ final Task targetStack = new StackBuilder(mRootWindowContainer)
.setOnTop(false)
.build();
final Task targetTask = targetStack.getBottomMostTask();
// Create Recents on top of the display.
- final ActivityStack stack = new StackBuilder(mRootWindowContainer).setActivityType(
+ final Task stack = new StackBuilder(mRootWindowContainer).setActivityType(
ACTIVITY_TYPE_RECENTS).build();
final String reason = "findTaskToMoveToFront";
@@ -377,14 +429,14 @@ public class RootActivityContainerTests extends ActivityTestsBase {
public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
// Create stack/task on default display.
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final ActivityStack targetStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ final Task targetStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, false /* onTop */);
final Task targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
// Create Recents on secondary display.
final TestDisplayContent secondDisplay = addNewDisplayContentAt(
DisplayContent.POSITION_TOP);
- final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea()
+ final Task stack = secondDisplay.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
new ActivityBuilder(mService).setTask(task).build();
@@ -404,11 +456,11 @@ public class RootActivityContainerTests extends ActivityTestsBase {
public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
// Create a stack at bottom.
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final ActivityStack targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
- taskDisplayArea.positionStackAtBottom(targetStack);
+ taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
// Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
// is the current top focused stack.
@@ -460,7 +512,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
// Create an activity on secondary display.
final TestDisplayContent secondDisplay = addNewDisplayContentAt(
DisplayContent.POSITION_TOP);
- final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea()
+ final Task stack = secondDisplay.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
new ActivityBuilder(mService).setTask(task).build();
@@ -484,7 +536,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
public void testResumeActivityLingeringTransition() {
// Create a stack at top.
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final ActivityStack targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
@@ -504,12 +556,12 @@ public class RootActivityContainerTests extends ActivityTestsBase {
public void testResumeActivityLingeringTransition_notExecuted() {
// Create a stack at bottom.
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final ActivityStack targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
activity.setState(ActivityState.RESUMED, "test");
- taskDisplayArea.positionStackAtBottom(targetStack);
+ taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
// Assume the stack is at the topmost position
assertFalse(targetStack.isTopStackInDisplayArea());
@@ -817,7 +869,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId,
300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info);
- final ActivityStack result = mRootWindowContainer.getLaunchStack(r, options,
+ final Task result = mRootWindowContainer.getLaunchStack(r, options,
null /* task */, true /* onTop */, null, 300 /* test realCallerPid */,
300 /* test realCallerUid */);
@@ -838,7 +890,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
.setTask(task).build();
// Make sure the root task is valid and can be reused on default display.
- final ActivityStack stack = mRootWindowContainer.getValidLaunchStackInTaskDisplayArea(
+ final Task stack = mRootWindowContainer.getValidLaunchStackInTaskDisplayArea(
mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, null,
null);
assertEquals(task, stack);
@@ -849,7 +901,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack();
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack homeStack = taskDisplayArea.getRootHomeTask();
+ Task homeStack = taskDisplayArea.getRootHomeTask();
if (homeStack != null) {
homeStack.removeImmediately();
}
@@ -864,6 +916,24 @@ public class RootActivityContainerTests extends ActivityTestsBase {
assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask());
}
+ @Test
+ public void testResumeFocusedStackOnSleepingDisplay() {
+ // Create an activity on secondary display.
+ final TestDisplayContent secondDisplay = addNewDisplayContentAt(
+ DisplayContent.POSITION_TOP);
+ final Task stack = secondDisplay.getDefaultTaskDisplayArea()
+ .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mService).setStack(stack).build();
+ spyOn(activity);
+ spyOn(stack);
+
+ // Cannot resumed activities on secondary display if the display should sleep.
+ doReturn(true).when(secondDisplay).shouldSleep();
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any());
+ verify(activity, never()).makeActiveIfNeeded(any());
+ }
+
/**
* Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
* info for test cases.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 35d1b17d5822..2e4c9ea747c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -25,11 +25,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.Task.ActivityState.FINISHING;
+import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.Task.ActivityState.PAUSING;
+import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.google.common.truth.Truth.assertThat;
@@ -148,8 +150,8 @@ public class RootWindowContainerTests extends WindowTestsBase {
@Test
public void testAllPausedActivitiesComplete() {
DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
- TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(0);
- ActivityStack stack = taskDisplayArea.getStackAt(0);
+ TaskDisplayArea taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
+ Task stack = taskDisplayArea.getStackAt(0);
ActivityRecord activity = createActivityRecord(displayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
stack.mPausingActivity = activity;
@@ -169,5 +171,27 @@ public class RootWindowContainerTests extends WindowTestsBase {
activity.setState(FINISHING, "test FINISHING");
assertThat(mWm.mRoot.allPausedActivitiesComplete()).isTrue();
}
+
+ @Test
+ public void testForceStopPackage() {
+ final Task task = new ActivityTestsBase.StackBuilder(mWm.mRoot).build();
+ final ActivityRecord activity1 = task.getTopMostActivity();
+ final ActivityRecord activity2 =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService).setStack(task).build();
+ final WindowProcessController wpc = activity1.app;
+ spyOn(wpc);
+ activity1.app = null;
+ activity2.setProcess(wpc);
+ doReturn(true).when(wpc).isRemoved();
+
+ mWm.mAtmService.mInternal.onForceStopPackage(wpc.mInfo.packageName, true /* doit */,
+ false /* evenPersistent */, wpc.mUserId);
+ // The activity without process should be removed.
+ assertEquals(1, task.getChildCount());
+
+ mWm.mRoot.handleAppDied(wpc);
+ // The activity with process should be removed because WindowProcessController#isRemoved.
+ assertFalse(task.hasChild());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 3d3a0f148db5..e51a133d5cce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -62,7 +62,7 @@ public class RunningTasksTest extends ActivityTestsBase {
final int numStacks = 2;
for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
- final ActivityStack stack = new StackBuilder(mRootWindowContainer)
+ final Task stack = new StackBuilder(mRootWindowContainer)
.setCreateActivity(false)
.setDisplay(display)
.setOnTop(false)
@@ -104,7 +104,7 @@ public class RunningTasksTest extends ActivityTestsBase {
final DisplayContent display = new TestDisplayContent.Builder(mService, 1000, 2500).build();
final int numTasks = 10;
for (int i = 0; i < numTasks; i++) {
- final ActivityStack stack = new StackBuilder(mRootWindowContainer)
+ final Task stack = new StackBuilder(mRootWindowContainer)
.setCreateActivity(false)
.setDisplay(display)
.setOnTop(true)
@@ -130,7 +130,7 @@ public class RunningTasksTest extends ActivityTestsBase {
/**
* Create a task with a single activity in it, with the given last active time.
*/
- private Task createTask(ActivityStack stack, String className, int taskId,
+ private Task createTask(Task stack, String className, int taskId,
int lastActiveTime, Bundle extras) {
final Task task = new TaskBuilder(mService.mStackSupervisor)
.setComponent(new ComponentName(mContext.getPackageName(), className))
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 31a102ae3bad..ef74861e9422 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -25,8 +25,8 @@ import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.TOP;
-import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
-import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -190,7 +190,7 @@ public class ScreenDecorWindowTests {
@Test
public void testProvidesInsetsTypes() {
- int[] providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
+ int[] providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR};
final View win = createWindow("StatusBarSubPanel", TOP, MATCH_PARENT, mDecorThickness, RED,
FLAG_LAYOUT_IN_SCREEN, 0, providesInsetsTypes);
@@ -199,7 +199,7 @@ public class ScreenDecorWindowTests {
private View createDecorWindow(int gravity, int width, int height) {
int[] providesInsetsTypes =
- new int[]{gravity == TOP ? ITYPE_STATUS_BAR : ITYPE_NAVIGATION_BAR};
+ new int[]{gravity == TOP ? ITYPE_CLIMATE_BAR : ITYPE_EXTRA_NAVIGATION_BAR};
return createWindow("decorWindow", gravity, width, height, RED,
FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR, providesInsetsTypes);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 3c98272311f7..250cf09e8547 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -29,7 +29,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.Task.ActivityState.STOPPED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -65,7 +65,7 @@ import java.util.ArrayList;
@Presubmit
@RunWith(WindowTestRunner.class)
public class SizeCompatTests extends ActivityTestsBase {
- private ActivityStack mStack;
+ private Task mStack;
private Task mTask;
private ActivityRecord mActivity;
@@ -86,7 +86,7 @@ public class SizeCompatTests extends ActivityTestsBase {
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
mActivity.mVisibleRequested = true;
mActivity.setSavedState(null /* savedState */);
- mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart");
+ mActivity.setState(Task.ActivityState.RESUMED, "testRestart");
prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
@@ -94,7 +94,7 @@ public class SizeCompatTests extends ActivityTestsBase {
// The visible activity should recompute configuration according to the last parent bounds.
mService.restartActivityProcessIfVisible(mActivity.appToken);
- assertEquals(ActivityStack.ActivityState.RESTARTING_PROCESS, mActivity.getState());
+ assertEquals(Task.ActivityState.RESTARTING_PROCESS, mActivity.getState());
assertNotEquals(originalOverrideBounds, mActivity.getBounds());
}
@@ -269,6 +269,8 @@ public class SizeCompatTests extends ActivityTestsBase {
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity);
+ mActivity.mDisplayContent.mInputMethodInputTarget =
+ mActivity.mDisplayContent.mInputMethodTarget;
// Because the aspect ratio of display doesn't exceed the max aspect ratio of activity.
// The activity should still fill its parent container and IME can attach to the activity.
assertTrue(mActivity.matchParentBounds());
@@ -447,7 +449,7 @@ public class SizeCompatTests extends ActivityTestsBase {
public void testHandleActivitySizeCompatMode() {
setUpDisplaySizeWithApp(1000, 2000);
ActivityRecord activity = mActivity;
- activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
+ activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -474,7 +476,7 @@ public class SizeCompatTests extends ActivityTestsBase {
activity.mVisibleRequested = true;
activity.restartProcessIfVisible();
// The full lifecycle isn't hooked up so manually set state to resumed
- activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
+ activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
mStack.getDisplay().handleActivitySizeCompatModeIfNeeded(activity);
// Expect null token when switching to non-size-compat mode activity.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index be9bf24a0325..ff753f21d01b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -34,6 +34,7 @@ import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.Point;
import android.hardware.power.Boost;
+import android.os.Handler;
import android.os.PowerManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.view.Choreographer;
@@ -46,11 +47,12 @@ import android.view.animation.TranslateAnimation;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import com.android.server.AnimationThread;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -64,8 +66,7 @@ import java.util.concurrent.CountDownLatch;
*/
@SmallTest
@Presubmit
-@RunWith(WindowTestRunner.class)
-public class SurfaceAnimationRunnerTest extends WindowTestsBase {
+public class SurfaceAnimationRunnerTest {
@Mock SurfaceControl mMockSurface;
@Mock Transaction mMockTransaction;
@@ -75,6 +76,9 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
private SurfaceAnimationRunner mSurfaceAnimationRunner;
private CountDownLatch mFinishCallbackLatch;
+ private final Handler mAnimationThreadHandler = AnimationThread.getHandler();
+ private final Handler mSurfaceAnimationHandler = SurfaceAnimationThread.getHandler();
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -84,6 +88,12 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
mMockTransaction, mMockPowerManager);
}
+ @After
+ public void tearDown() {
+ SurfaceAnimationThread.dispose();
+ AnimationThread.dispose();
+ }
+
private void finishedCallback() {
mFinishCallbackLatch.countDown();
}
@@ -101,8 +111,7 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any());
verify(mMockTransaction, atLeastOnce()).setAlpha(eq(mMockSurface), eq(1.0f));
- waitHandlerIdle(SurfaceAnimationThread.getHandler());
- mFinishCallbackLatch.await(1, SECONDS);
+ waitHandlerIdle(mSurfaceAnimationHandler);
assertFinishCallbackCalled();
m.setTranslate(10, 0);
@@ -120,7 +129,7 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
.startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
this::finishedCallback);
mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
- waitUntilHandlersIdle();
+ waitHandlerIdle(mAnimationThreadHandler);
assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty());
assertFinishCallbackNotCalled();
}
@@ -135,7 +144,7 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
- waitUntilHandlersIdle();
+ waitHandlerIdle(mAnimationThreadHandler);
assertFinishCallbackNotCalled();
}
@@ -180,9 +189,8 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
mSurfaceAnimationRunner.continueStartingAnimations();
waitUntilNextFrame();
- waitHandlerIdle(SurfaceAnimationThread.getHandler());
+ waitHandlerIdle(mSurfaceAnimationHandler);
assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
- mFinishCallbackLatch.await(1, SECONDS);
assertFinishCallbackCalled();
}
@@ -204,7 +212,15 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
latch.await();
}
+ private static void waitHandlerIdle(Handler handler) {
+ handler.runWithScissors(() -> { }, 0 /* timeout */);
+ }
+
private void assertFinishCallbackCalled() {
+ try {
+ assertTrue(mFinishCallbackLatch.await(5, SECONDS));
+ } catch (InterruptedException ignored) {
+ }
assertEquals(0, mFinishCallbackLatch.getCount());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index d7462f810bb7..53c2a5b8967d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
import android.os.Handler;
import android.testing.DexmakerShareClassLoaderRule;
@@ -67,6 +69,15 @@ class SystemServiceTestsBase {
}
/**
+ * Make the system booted, so that {@link ActivityStack#resumeTopActivityInnerLocked} can really
+ * be executed to update activity state and configuration when resuming the current top.
+ */
+ static void setBooted(ActivityTaskManagerService atmService) {
+ doReturn(false).when(atmService).isBooting();
+ doReturn(true).when(atmService).isBooted();
+ }
+
+ /**
* Utility class to compare the output of T#toString. It is convenient to have readable output
* of assertion if the string content can represent the expected states.
*/
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 218261b01029..eb7d9c2d3c32 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -102,6 +102,7 @@ public class SystemServicesTestRule implements TestRule {
private static final int[] TEST_USER_PROFILE_IDS = {};
+ private Description mDescription;
private Context mContext;
private StaticMockitoSession mMockitoSession;
private ActivityManagerService mAmService;
@@ -121,6 +122,7 @@ public class SystemServicesTestRule implements TestRule {
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ mDescription = description;
Throwable throwable = null;
try {
runWithDexmakerShareClassLoader(SystemServicesTestRule.this::setUp);
@@ -317,7 +319,7 @@ public class SystemServicesTestRule implements TestRule {
spyOn(display);
final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
spyOn(taskDisplayArea);
- final ActivityStack homeStack = taskDisplayArea.getStack(
+ final Task homeStack = taskDisplayArea.getStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
spyOn(homeStack);
}
@@ -373,6 +375,10 @@ public class SystemServicesTestRule implements TestRule {
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
}
+ Description getDescription() {
+ return mDescription;
+ }
+
WindowManagerService getWindowManagerService() {
return mWmService;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 786f8d8af024..27a8fc3c5943 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -28,14 +28,17 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -64,7 +67,7 @@ import org.junit.runner.RunWith;
@RunWith(WindowTestRunner.class)
public class TaskDisplayAreaTests extends WindowTestsBase {
- private ActivityStack mPinnedStack;
+ private Task mPinnedStack;
@Before
public void setUp() throws Exception {
@@ -86,7 +89,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
@Test
public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() {
- final ActivityStack stack = createTaskStackOnDisplay(
+ final Task stack = createTaskStackOnDisplay(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
@@ -106,8 +109,8 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
@Test
public void testStackPositionChildAt() {
// Test that always-on-top stack can't be moved to position other than top.
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
- final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
final WindowContainer taskStackContainer = stack1.getParent();
@@ -131,7 +134,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
@Test
public void testStackPositionBelowPinnedStack() {
// Test that no stack can be above pinned stack.
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final WindowContainer taskStackContainer = stack1.getParent();
@@ -155,7 +158,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
doReturn(true).when(mDisplayContent).isTrusted();
// The display contains pinned stack that was added in {@link #setUp}.
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
// Add another display at top.
@@ -207,10 +210,44 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
false /* reuseCandidate */);
}
+ @Test
+ public void testGetOrientation_nonResizableHomeStackWithHomeActivityPendingVisibilityChange() {
+ final RootWindowContainer rootWindowContainer = mWm.mAtmService.mRootWindowContainer;
+ final TaskDisplayArea defaultTaskDisplayArea =
+ rootWindowContainer.getDefaultTaskDisplayArea();
+
+ final Task rootHomeTask = defaultTaskDisplayArea.getRootHomeTask();
+ rootHomeTask.mResizeMode = RESIZE_MODE_UNRESIZEABLE;
+
+ final Task primarySplitTask =
+ new ActivityTestsBase.StackBuilder(rootWindowContainer)
+ .setTaskDisplayArea(defaultTaskDisplayArea)
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setOnTop(true)
+ .setCreateActivity(true)
+ .build();
+ ActivityRecord primarySplitActivity = primarySplitTask.getTopNonFinishingActivity();
+ assertNotNull(primarySplitActivity);
+ primarySplitActivity.setState(RESUMED,
+ "testGetOrientation_nonResizableHomeStackWithHomeActivityPendingVisibilityChange");
+
+ ActivityRecord homeActivity = rootHomeTask.getTopNonFinishingActivity();
+ if (homeActivity == null) {
+ homeActivity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(rootHomeTask).setCreateTask(true).build();
+ }
+ homeActivity.setVisible(false);
+ homeActivity.mVisibleRequested = true;
+ assertFalse(rootHomeTask.isVisible());
+
+ assertEquals(rootWindowContainer.getOrientation(), rootHomeTask.getOrientation());
+ }
+
private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask,
boolean reuseCandidate) {
final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
- final ActivityStack stack = taskDisplayArea.getOrCreateStack(windowingMode, activityType,
+ final Task stack = taskDisplayArea.getOrCreateStack(windowingMode, activityType,
false /* onTop */, null /* intent */, candidateTask /* candidateTask */);
assertEquals(reuseCandidate, stack == candidateTask);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index a69231b9e03a..a048526bb068 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -30,7 +30,6 @@ import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
@@ -1350,13 +1349,13 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
}
private ActivityRecord createSourceActivity(TestDisplayContent display) {
- final ActivityStack stack = display.getDefaultTaskDisplayArea()
+ final Task stack = display.getDefaultTaskDisplayArea()
.createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
return new ActivityBuilder(mService).setStack(stack).setCreateTask(true).build();
}
private void addFreeformTaskTo(TestDisplayContent display, Rect bounds) {
- final ActivityStack stack = display.getDefaultTaskDisplayArea()
+ final Task stack = display.getDefaultTaskDisplayArea()
.createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 93dcc9103640..0db3f94d1fc3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -37,7 +37,6 @@ import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.util.Log;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.After;
@@ -77,7 +76,7 @@ public class TaskPositionerTests extends WindowTestsBase {
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
removeGlobalMinSizeRestriction();
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(stack.mAtmService)
.setStack(stack)
// In real case, there is no additional level for freeform mode.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index abdbd5131fd9..bf76c8ee5f0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -173,7 +173,7 @@ public class TaskRecordTests extends ActivityTestsBase {
public void testFitWithinBounds() {
final Rect parentBounds = new Rect(10, 10, 200, 200);
TaskDisplayArea taskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack stack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM,
+ Task stack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
final Configuration parentConfig = stack.getConfiguration();
@@ -211,7 +211,7 @@ public class TaskRecordTests extends ActivityTestsBase {
@Test
public void testBoundsOnModeChangeFreeformToFullscreen() {
DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay();
- ActivityStack stack = new StackBuilder(mRootWindowContainer).setDisplay(display)
+ Task stack = new StackBuilder(mRootWindowContainer).setDisplay(display)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
Task task = stack.getBottomMostTask();
task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
@@ -252,7 +252,7 @@ public class TaskRecordTests extends ActivityTestsBase {
dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
- ActivityStack stack = new StackBuilder(mRootWindowContainer)
+ Task stack = new StackBuilder(mRootWindowContainer)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
Task task = stack.getBottomMostTask();
ActivityRecord root = task.getTopNonFinishingActivity();
@@ -313,7 +313,7 @@ public class TaskRecordTests extends ActivityTestsBase {
Configuration.ORIENTATION_LANDSCAPE;
display.onRequestedOverrideConfigurationChanged(
display.getRequestedOverrideConfiguration());
- ActivityStack stack = new StackBuilder(mRootWindowContainer)
+ Task stack = new StackBuilder(mRootWindowContainer)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
Task task = stack.getBottomMostTask();
ActivityRecord root = task.getTopNonFinishingActivity();
@@ -324,7 +324,7 @@ public class TaskRecordTests extends ActivityTestsBase {
parentWindowContainer.setBounds(fullScreenBounds);
doReturn(parentWindowContainer).when(task).getParent();
doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea();
- doReturn(stack).when(task).getStack();
+ doReturn(stack).when(task).getRootTask();
doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant();
// Setting app to fixed portrait fits within parent, but Task shouldn't adjust the
@@ -409,15 +409,15 @@ public class TaskRecordTests extends ActivityTestsBase {
assertTrue(task.getResolvedOverrideBounds().isEmpty());
int origScreenH = task.getConfiguration().screenHeightDp;
Configuration stackConfig = new Configuration();
- stackConfig.setTo(task.getStack().getRequestedOverrideConfiguration());
+ stackConfig.setTo(task.getRootTask().getRequestedOverrideConfiguration());
stackConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
// Set bounds on stack (not task) and verify that the task resource configuration changes
// despite it's override bounds being empty.
- Rect bounds = new Rect(task.getStack().getBounds());
+ Rect bounds = new Rect(task.getRootTask().getBounds());
bounds.bottom = (int) (bounds.bottom * 0.6f);
stackConfig.windowConfiguration.setBounds(bounds);
- task.getStack().onRequestedOverrideConfigurationChanged(stackConfig);
+ task.getRootTask().onRequestedOverrideConfigurationChanged(stackConfig);
assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp);
}
@@ -439,7 +439,7 @@ public class TaskRecordTests extends ActivityTestsBase {
@Test
public void testInsetDisregardedWhenFreeformOverlapsNavBar() {
TaskDisplayArea taskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack stack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
+ Task stack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
DisplayInfo displayInfo = new DisplayInfo();
mService.mContext.getDisplay().getDisplayInfo(displayInfo);
@@ -514,7 +514,7 @@ public class TaskRecordTests extends ActivityTestsBase {
info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
info.targetActivity = targetClassName;
- final Task task = new ActivityStack(mService, 1 /* taskId */, info, intent,
+ final Task task = new Task(mService, 1 /* taskId */, info, intent,
null /* voiceSession */, null /* voiceInteractor */, null /* taskDescriptor */,
null /*stack*/);
assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
@@ -880,7 +880,7 @@ public class TaskRecordTests extends ActivityTestsBase {
final Task task = getTestTask();
task.setHasBeenVisible(false);
task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
- task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.setHasBeenVisible(true);
task.onConfigurationChanged(task.getParent().getConfiguration());
@@ -896,7 +896,7 @@ public class TaskRecordTests extends ActivityTestsBase {
final Task task = getTestTask();
task.setHasBeenVisible(false);
task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
- task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final DisplayContent oldDisplay = task.getDisplayContent();
LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams();
@@ -920,7 +920,7 @@ public class TaskRecordTests extends ActivityTestsBase {
final Task task = getTestTask();
task.setHasBeenVisible(false);
- task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.setHasBeenVisible(true);
task.onConfigurationChanged(task.getParent().getConfiguration());
@@ -936,7 +936,7 @@ public class TaskRecordTests extends ActivityTestsBase {
final Task task = getTestTask();
task.setHasBeenVisible(false);
task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
- task.getStack().setWindowingMode(WINDOWING_MODE_PINNED);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
task.setHasBeenVisible(true);
task.onConfigurationChanged(task.getParent().getConfiguration());
@@ -976,7 +976,7 @@ public class TaskRecordTests extends ActivityTestsBase {
}
private Task getTestTask() {
- final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
+ final Task stack = new StackBuilder(mRootWindowContainer).build();
return stack.getBottomMostTask();
}
@@ -984,7 +984,7 @@ public class TaskRecordTests extends ActivityTestsBase {
Rect expectedConfigBounds) {
TaskDisplayArea taskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea();
- ActivityStack stack = taskDisplayArea.createStack(windowingMode, ACTIVITY_TYPE_STANDARD,
+ Task stack = taskDisplayArea.createStack(windowingMode, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
@@ -1024,7 +1024,7 @@ public class TaskRecordTests extends ActivityTestsBase {
}
private Task createTask(int taskId) {
- return new ActivityStack(mService, taskId, new Intent(), null, null, null,
+ return new Task(mService, taskId, new Intent(), null, null, null,
ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
0, false, null, 0, 0, 0, 0, null, null, 0, false, false, false, 0,
0, null /*ActivityInfo*/, null /*_voiceSession*/, null /*_voiceInteractor*/,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 0c4bb9f2ac2b..0b99e328b9bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -183,6 +183,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
}
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testPrepareTaskSnapshot() {
mAppWindow.mWinAnimator.mLastAlpha = 1f;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index b8a1c2b41454..bce1142c99be 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -74,6 +74,7 @@ public class TaskStackChangedListenerTest {
private IActivityManager mService;
private ITaskStackListener mTaskStackListener;
+ private static final int WAIT_TIMEOUT_MS = 5000;
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static boolean sTaskStackChangedCalled;
@@ -490,7 +491,8 @@ public class TaskStackChangedListenerTest {
SystemUtil.runWithShellPermissionIdentity(() -> context.startActivity(
new Intent(context, activityClass).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
options.toBundle()));
- final TestActivity activity = (TestActivity) monitor.waitForActivityWithTimeout(1000);
+ final TestActivity activity =
+ (TestActivity) monitor.waitForActivityWithTimeout(WAIT_TIMEOUT_MS);
if (activity == null) {
throw new RuntimeException("Timed out waiting for Activity");
}
@@ -508,7 +510,7 @@ public class TaskStackChangedListenerTest {
private void waitForCallback(CountDownLatch latch) {
try {
- final boolean result = latch.await(4, TimeUnit.SECONDS);
+ final boolean result = latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
if (!result) {
throw new RuntimeException("Timed out waiting for task stack change notification");
}
@@ -560,7 +562,7 @@ public class TaskStackChangedListenerTest {
if (mIsResumed == isResumed) {
return;
}
- wait(5000);
+ wait(WAIT_TIMEOUT_MS);
}
assertEquals("The activity resume state change timed out", isResumed, mIsResumed);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index f1dbde066125..205b842253b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -59,7 +59,7 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testStackPositionChildAt() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack, 0 /* userId */);
final Task task2 = createTaskInStack(stack, 1 /* userId */);
@@ -74,8 +74,8 @@ public class TaskStackTests extends WindowTestsBase {
assertEquals(stack.mChildren.get(1), task1);
// Non-leaf task should be moved to top regardless of the user id.
- createTaskInStack((ActivityStack) task2, 0 /* userId */);
- createTaskInStack((ActivityStack) task2, 1 /* userId */);
+ createTaskInStack(task2, 0 /* userId */);
+ createTaskInStack(task2, 1 /* userId */);
stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
assertEquals(stack.mChildren.get(0), task1);
assertEquals(stack.mChildren.get(1), task2);
@@ -83,7 +83,7 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testClosingAppDifferentStackOrientation() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack, 0 /* userId */);
ActivityRecord activity1 =
WindowTestUtils.createTestActivityRecord(mDisplayContent);
@@ -103,7 +103,7 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testMoveTaskToBackDifferentStackOrientation() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack, 0 /* userId */);
ActivityRecord activity1 =
WindowTestUtils.createTestActivityRecord(mDisplayContent);
@@ -120,9 +120,9 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testStackRemoveImmediately() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
- assertEquals(stack, task.getStack());
+ assertEquals(stack, task.getRootTask());
// Remove stack and check if its child is also removed.
stack.removeImmediately();
@@ -132,7 +132,7 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testRemoveContainer() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
assertNotNull(stack);
@@ -148,7 +148,7 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testRemoveContainer_deferRemoval() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
// Stack removal is deferred if one of its child is animating.
@@ -172,12 +172,12 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testReparent() {
// Create first stack on primary display.
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
// Create second display and put second stack on it.
final DisplayContent dc = createNewDisplay();
- final ActivityStack stack2 = createTaskStackOnDisplay(dc);
+ final Task stack2 = createTaskStackOnDisplay(dc);
// Reparent
clearInvocations(task1); // reset the number of onDisplayChanged for task.
@@ -191,7 +191,7 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testStackOutset() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final int stackOutset = 10;
spyOn(stack);
doReturn(stackOutset).when(stack).getTaskOutset();
@@ -219,7 +219,7 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testActivityAndTaskGetsProperType() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack, 0 /* userId */);
ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 1415c506a1c9..92b6e6ef8ec9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -28,6 +28,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.clearInvocations;
import android.graphics.Point;
@@ -52,7 +54,7 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testRemoveContainer() {
- final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ActivityRecord activity =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
@@ -66,7 +68,7 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testRemoveContainer_deferRemoval() {
- final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ActivityRecord activity =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
@@ -88,9 +90,9 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testReparent() {
- final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stackController1, 0 /* userId */);
- final ActivityStack stackController2 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stackController2 = createTaskStackOnDisplay(mDisplayContent);
final Task task2 = createTaskInStack(stackController2, 0 /* userId */);
boolean gotException = false;
@@ -118,13 +120,13 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testReparent_BetweenDisplays() {
// Create first stack on primary display.
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack1, 0 /* userId */);
assertEquals(mDisplayContent, stack1.getDisplayContent());
// Create second display and put second stack on it.
final DisplayContent dc = createNewDisplay();
- final ActivityStack stack2 = createTaskStackOnDisplay(dc);
+ final Task stack2 = createTaskStackOnDisplay(dc);
final Task task2 = createTaskInStack(stack2, 0 /* userId */);
// Reparent and check state
clearInvocations(task); // reset the number of onDisplayChanged for task.
@@ -137,7 +139,7 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testBounds() {
- final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack1, 0 /* userId */);
// Check that setting bounds also updates surface position
@@ -158,4 +160,45 @@ public class TaskTests extends WindowTestsBase {
assertEquals(activity1, task1.isInTask(activity1));
assertNull(task1.isInTask(activity2));
}
+
+ @Test
+ public void testRemoveChildForOverlayTask() {
+ final Task task = createTaskStackOnDisplay(mDisplayContent);
+ final int taskId = task.mTaskId;
+ final ActivityRecord activity1 =
+ WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+ final ActivityRecord activity2 =
+ WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+ final ActivityRecord activity3 =
+ WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+ activity1.setTaskOverlay(true);
+ activity2.setTaskOverlay(true);
+ activity3.setTaskOverlay(true);
+
+ assertEquals(3, task.getChildCount());
+ assertTrue(task.onlyHasTaskOverlayActivities(true));
+
+ task.removeChild(activity1);
+
+ verify(task.mStackSupervisor).removeTask(any(), anyBoolean(), anyBoolean(), anyString());
+ assertEquals(2, task.getChildCount());
+ task.forAllActivities((r) -> {
+ assertTrue(r.finishing);
+ });
+ }
+
+ @Test
+ public void testSwitchUser() {
+ final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
+ final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
+ final Task leafTask1 = createTaskInStack(childTask, 10 /* userId */);
+ final Task leafTask2 = createTaskInStack(childTask, 0 /* userId */);
+ assertEquals(1, rootTask.getChildCount());
+ assertEquals(leafTask2, childTask.getTopChild());
+
+ doReturn(true).when(leafTask1).showToCurrentUser();
+ rootTask.switchUser(10);
+ assertEquals(1, rootTask.getChildCount());
+ assertEquals(leafTask1, childTask.getTopChild());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d2a2732d60ab..213c1f52aa37 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -43,10 +43,9 @@ class TestDisplayContent extends DisplayContent {
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
spyOn(this);
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- spyOn(getTaskDisplayAreaAt(i));
- }
-
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ spyOn(taskDisplayArea);
+ });
final DisplayRotation displayRotation = getDisplayRotation();
spyOn(displayRotation);
doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 53ede60e9ac7..573e37a2d6b3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -270,6 +270,7 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertEquals(WINDOWING_MODE_FULLSCREEN, token.getWindowingMode());
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testFixedRotationRecentsAnimatingTask() {
final RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 87485eac3412..3ebc28886377 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -807,7 +808,7 @@ public class WindowContainerTests extends WindowTestsBase {
@Test
public void testOnDisplayChanged() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityRecord activity =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
@@ -825,8 +826,33 @@ public class WindowContainerTests extends WindowTestsBase {
}
@Test
+ public void testHandleCompleteDeferredRemoval() {
+ final DisplayContent displayContent = createNewDisplay();
+ // Do not reparent activity to default display when removing the display.
+ doReturn(true).when(displayContent).shouldDestroyContentOnRemove();
+ final ActivityRecord r = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ .setDisplay(displayContent).build().getTopMostActivity();
+ // Add a window and make the activity animating so the removal of activity is deferred.
+ createWindow(null, TYPE_BASE_APPLICATION, r, "win");
+ doReturn(true).when(r).isAnimating(anyInt(), anyInt());
+
+ displayContent.remove();
+ // Ensure that ActivityRecord#onRemovedFromDisplay is called.
+ r.destroyed("test");
+ // The removal is deferred, so the activity is still in the display.
+ assertEquals(r, displayContent.getTopMostActivity());
+
+ // Assume the animation is done so the deferred removal can continue.
+ doReturn(false).when(r).isAnimating(anyInt(), anyInt());
+
+ assertFalse(displayContent.handleCompleteDeferredRemoval());
+ assertFalse(displayContent.hasChild());
+ assertFalse(r.hasChild());
+ }
+
+ @Test
public void testTaskCanApplyAnimation() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityRecord activity2 =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
@@ -837,7 +863,7 @@ public class WindowContainerTests extends WindowTestsBase {
@Test
public void testStackCanApplyAnimation() {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
final ActivityRecord activity2 = WindowTestUtils.createActivityRecordInTask(mDisplayContent,
createTaskInStack(stack, 0 /* userId */));
final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(mDisplayContent,
@@ -853,7 +879,7 @@ public class WindowContainerTests extends WindowTestsBase {
assertNull(windowContainer.getDisplayArea());
// ActivityStack > WindowContainer
- final ActivityStack activityStack = createTaskStackOnDisplay(mDisplayContent);
+ final Task activityStack = createTaskStackOnDisplay(mDisplayContent);
activityStack.addChild(windowContainer, 0);
activityStack.setParent(null);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index 3c0dd1e897f5..47e4559cbb13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -46,6 +46,7 @@ import java.util.function.Consumer;
@RunWith(WindowTestRunner.class)
public class WindowContainerTraversalTests extends WindowTestsBase {
+ @UseTestDisplay(addWindows = { W_DOCK_DIVIDER, W_INPUT_METHOD })
@Test
public void testDockedDividerPosition() {
final WindowState splitScreenWindow = createWindowOnStack(null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index f52905ef6ae9..f97dff3162c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -60,24 +60,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
@Rule
public ExpectedException mExpectedException = ExpectedException.none();
- @Test
- public void testForceShowSystemBarsThrowsExceptionForNonAutomotive() {
- if (!isAutomotive()) {
- mExpectedException.expect(UnsupportedOperationException.class);
-
- mWm.setForceShowSystemBars(true);
- }
- }
-
- @Test
- public void testForceShowSystemBarsDoesNotThrowExceptionForAutomotiveWithStatusBarPermission() {
- if (isAutomotive()) {
- mExpectedException.none();
-
- mWm.setForceShowSystemBars(true);
- }
- }
-
private boolean isAutomotive() {
return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE);
@@ -123,13 +105,13 @@ public class WindowManagerServiceTests extends WindowTestsBase {
public void testTaskFocusChange_stackNotHomeType_focusChanges() throws RemoteException {
DisplayContent display = createNewDisplay();
// Current focused window
- ActivityStack focusedStack = createTaskStackOnDisplay(
+ Task focusedStack = createTaskStackOnDisplay(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */);
WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
mDisplayContent.mCurrentFocus = focusedWindow;
// Tapped task
- ActivityStack tappedStack = createTaskStackOnDisplay(
+ Task tappedStack = createTaskStackOnDisplay(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, display);
Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
spyOn(mWm.mActivityTaskManager);
@@ -144,13 +126,13 @@ public class WindowManagerServiceTests extends WindowTestsBase {
throws RemoteException {
DisplayContent display = createNewDisplay();
// Current focused window
- ActivityStack focusedStack = createTaskStackOnDisplay(
+ Task focusedStack = createTaskStackOnDisplay(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */);
WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
mDisplayContent.mCurrentFocus = focusedWindow;
// Tapped home task
- ActivityStack tappedStack = createTaskStackOnDisplay(
+ Task tappedStack = createTaskStackOnDisplay(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, display);
Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
spyOn(mWm.mActivityTaskManager);
@@ -163,19 +145,17 @@ public class WindowManagerServiceTests extends WindowTestsBase {
@Test
public void testTaskFocusChange_stackHomeTypeWithDifferentTaskDisplayArea_focusChanges()
throws RemoteException {
- DisplayContent display = createNewDisplay();
- TaskDisplayArea secondTda =
- new TaskDisplayArea(display, mWm, "Tapped TDA", FEATURE_VENDOR_FIRST);
- display.mDisplayAreaPolicy.mRoot.addChild(secondTda, 1);
- display.mDisplayAreaPolicy.mTaskDisplayAreas.add(secondTda);
+ final DisplayContent display = createNewDisplay();
+ final TaskDisplayArea secondTda = createTaskDisplayArea(
+ display, mWm, "Tapped TDA", FEATURE_VENDOR_FIRST);
// Current focused window
- ActivityStack focusedStack = createTaskStackOnDisplay(
+ Task focusedStack = createTaskStackOnDisplay(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */);
WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
mDisplayContent.mCurrentFocus = focusedWindow;
// Tapped home task on another task display area
- ActivityStack tappedStack = createTaskStackOnTaskDisplayArea(
+ Task tappedStack = createTaskStackOnTaskDisplayArea(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, secondTda);
Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
spyOn(mWm.mActivityTaskManager);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 7ce0c1edfb4c..800a5d42ce09 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -53,7 +53,6 @@ import static org.mockito.ArgumentMatchers.anyInt;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
-import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -103,7 +102,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
return registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
}
- Task createTask(ActivityStack stack, boolean fakeDraw) {
+ Task createTask(Task stack, boolean fakeDraw) {
final Task task = createTaskInStack(stack, 0);
if (fakeDraw) {
@@ -112,12 +111,12 @@ public class WindowOrganizerTests extends WindowTestsBase {
return task;
}
- Task createTask(ActivityStack stack) {
+ Task createTask(Task stack) {
// Fake draw notifications for most of our tests.
return createTask(stack, true);
}
- ActivityStack createStack() {
+ Task createStack() {
return createTaskStackOnDisplay(mDisplayContent);
}
@@ -130,7 +129,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testAppearVanish() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
@@ -144,7 +143,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testAppearWaitsForVisibility() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack, false);
final ITaskOrganizer organizer = registerMockOrganizer();
@@ -163,7 +162,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testNoVanishedIfNoAppear() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack, false /* hasBeenVisible */);
final ITaskOrganizer organizer = registerMockOrganizer();
@@ -179,7 +178,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testSwapOrganizer() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
@@ -193,7 +192,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testSwapWindowingModes() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
@@ -207,7 +206,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testTaskNoDraw() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack, false /* fakeDraw */);
final ITaskOrganizer organizer = registerMockOrganizer();
@@ -223,7 +222,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testClearOrganizer() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
@@ -238,7 +237,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testUnregisterOrganizer() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
@@ -253,11 +252,11 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
- final ActivityStack stack2 = createStack();
+ final Task stack2 = createStack();
final Task task2 = createTask(stack2);
- final ActivityStack stack3 = createStack();
+ final Task stack3 = createStack();
final Task task3 = createTask(stack3);
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
@@ -297,7 +296,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
final Task task2 = createTask(stack);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
@@ -310,7 +309,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
@@ -322,7 +321,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testTaskTransaction() {
removeGlobalMinSizeRestriction();
- final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ final Task stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
final Task task = stack.getTopMostTask();
testTransaction(task);
@@ -331,7 +330,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testStackTransaction() {
removeGlobalMinSizeRestriction();
- final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ final Task stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
StackInfo info =
mWm.mAtmService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
@@ -356,7 +355,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testSetWindowingMode() {
- final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ final Task stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
testSetWindowingMode(stack);
@@ -375,7 +374,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testSetActivityWindowingMode() {
final ActivityRecord record = makePipableActivity();
- final ActivityStack stack = record.getStack();
+ final Task stack = record.getStack();
final WindowContainerTransaction t = new WindowContainerTransaction();
t.setWindowingMode(stack.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED);
@@ -390,7 +389,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testContainerFocusableChanges() {
removeGlobalMinSizeRestriction();
- final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ final Task stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
final Task task = stack.getTopMostTask();
WindowContainerTransaction t = new WindowContainerTransaction();
@@ -406,7 +405,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testContainerHiddenChanges() {
removeGlobalMinSizeRestriction();
- final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ final Task stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
WindowContainerTransaction t = new WindowContainerTransaction();
assertTrue(stack.shouldBeVisible(null));
@@ -421,7 +420,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testOverrideConfigSize() {
removeGlobalMinSizeRestriction();
- final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ final Task stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
final Task task = stack.getTopMostTask();
WindowContainerTransaction t = new WindowContainerTransaction();
@@ -489,7 +488,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- final ActivityStack stack = createTaskStackOnDisplay(
+ final Task stack = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -521,6 +520,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
}
+ @UseTestDisplay
@Test
public void testTaskInfoCallback() {
final ArrayList<RunningTaskInfo> lastReportedTiles = new ArrayList<>();
@@ -549,7 +549,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
lastReportedTiles.clear();
called[0] = false;
- final ActivityStack stack = createTaskStackOnDisplay(
+ final Task stack = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -560,7 +560,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
lastReportedTiles.clear();
called[0] = false;
- final ActivityStack stack2 = createTaskStackOnDisplay(
+ final Task stack2 = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
wct = new WindowContainerTransaction();
wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
@@ -584,6 +584,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
}
+ @UseTestDisplay
@Test
public void testHierarchyTransaction() {
final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>();
@@ -615,9 +616,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
mDisplayContent.mDisplayId, null /* activityTypes */).size();
- final ActivityStack stack = createTaskStackOnDisplay(
+ final Task stack = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final ActivityStack stack2 = createTaskStackOnDisplay(
+ final Task stack2 = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
// Check getRootTasks works
@@ -669,23 +670,30 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertEquals(1, lastReportedTiles.size());
assertEquals(ACTIVITY_TYPE_HOME,
lastReportedTiles.get(info1.token.asBinder()).topActivityType);
+
+ // This just needs to not crash (ie. it should be possible to reparent to display twice)
+ wct = new WindowContainerTransaction();
+ wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ wct = new WindowContainerTransaction();
+ wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
}
private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
ArrayList<Task> out = new ArrayList<>();
- for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+ dc.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final Task t = taskDisplayArea.getStackAt(sNdx);
if (t.mCreatedByOrganizer) out.add(t);
}
- }
+ });
return out;
}
@Test
public void testTrivialBLASTCallback() throws RemoteException {
- final ActivityStack stackController1 = createStack();
+ final Task stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
@@ -707,7 +715,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testOverlappingBLASTCallback() throws RemoteException {
- final ActivityStack stackController1 = createStack();
+ final Task stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
@@ -729,14 +737,14 @@ public class WindowOrganizerTests extends WindowTestsBase {
// We should be rejected from the second sync since we are already
// in one.
assertEquals(false, bse.addToSyncSet(id2, task));
- finishAndNotifyDrawing(w);
+ w.immediatelyNotifyBlastSync();
assertEquals(true, bse.addToSyncSet(id2, task));
bse.setReady(id2);
}
@Test
public void testBLASTCallbackWithWindow() {
- final ActivityStack stackController1 = createStack();
+ final Task stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
@@ -753,14 +761,14 @@ public class WindowOrganizerTests extends WindowTestsBase {
// Since we have a window we have to wait for it to draw to finish sync.
verify(transactionListener, never())
.onTransactionReady(anyInt(), any());
- finishAndNotifyDrawing(w);
+ w.immediatelyNotifyBlastSync();
verify(transactionListener)
.onTransactionReady(anyInt(), any());
}
@Test
public void testBLASTCallbackNoDoubleAdd() {
- final ActivityStack stackController1 = createStack();
+ final Task stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
@@ -782,7 +790,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testBLASTCallbackWithInvisibleWindow() {
- final ActivityStack stackController1 = createStack();
+ final Task stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
@@ -804,7 +812,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testBLASTCallbackWithChildWindow() {
- final ActivityStack stackController1 = createStack();
+ final Task stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
@@ -821,14 +829,14 @@ public class WindowOrganizerTests extends WindowTestsBase {
int id = bse.startSyncSet(transactionListener);
assertEquals(true, bse.addToSyncSet(id, task));
bse.setReady(id);
- finishAndNotifyDrawing(w);
+ w.immediatelyNotifyBlastSync();
// Since we have a child window we still shouldn't be done.
verify(transactionListener, never())
.onTransactionReady(anyInt(), any());
reset(transactionListener);
- finishAndNotifyDrawing(child);
+ child.immediatelyNotifyBlastSync();
// Ah finally! Done
verify(transactionListener)
.onTransactionReady(anyInt(), any());
@@ -921,7 +929,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o,
WINDOWING_MODE_MULTI_WINDOW);
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
final ActivityRecord record = WindowTestUtils.createActivityRecordInTask(
stack.mDisplayContent, task);
@@ -934,7 +942,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testPreventDuplicateAppear() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
@@ -956,7 +964,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testInterceptBackPressedOnTaskRoot() throws RemoteException {
- final ActivityStack stack = createStack();
+ final Task stack = createStack();
final Task task = createTask(stack);
final ActivityRecord activity = WindowTestUtils.createActivityRecordInTask(
stack.mDisplayContent, task);
@@ -967,8 +975,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertTrue(stack.isOrganized());
// Verify a back pressed does not call the organizer
- mWm.mAtmService.onBackPressedOnTaskRoot(activity.token,
- new IRequestFinishCallback.Default());
+ mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
verify(organizer, never()).onBackPressedOnTaskRoot(any());
// Enable intercepting back
@@ -976,14 +983,13 @@ public class WindowOrganizerTests extends WindowTestsBase {
true);
// Verify now that the back press does call the organizer
- mWm.mAtmService.onBackPressedOnTaskRoot(activity.token,
- new IRequestFinishCallback.Default());
+ mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
}
@Test
public void testBLASTCallbackWithMultipleWindows() throws Exception {
- final ActivityStack stackController = createStack();
+ final Task stackController = createStack();
final Task task = createTask(stackController);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1");
@@ -1002,20 +1008,38 @@ public class WindowOrganizerTests extends WindowTestsBase {
verify(mockCallback, never()).onTransactionReady(anyInt(), any());
assertTrue(w1.useBLASTSync());
assertTrue(w2.useBLASTSync());
- finishAndNotifyDrawing(w1);
+ w1.immediatelyNotifyBlastSync();
// Even though one Window finished drawing, both windows should still be using blast sync
assertTrue(w1.useBLASTSync());
assertTrue(w2.useBLASTSync());
- finishAndNotifyDrawing(w2);
+ w2.immediatelyNotifyBlastSync();
verify(mockCallback).onTransactionReady(anyInt(), any());
assertFalse(w1.useBLASTSync());
assertFalse(w2.useBLASTSync());
}
- private void finishAndNotifyDrawing(WindowState ws) {
- ws.finishDrawing(null);
- ws.notifyBlastSyncTransaction();
+ @Test
+ public void testDisplayAreaHiddenTransaction() {
+ removeGlobalMinSizeRestriction();
+
+ WindowContainerTransaction trx = new WindowContainerTransaction();
+
+ TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+ trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), true);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx);
+
+ taskDisplayArea.forAllTasks(daTask -> {
+ assertTrue(daTask.isForceHidden());
+ });
+
+ trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), false);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx);
+
+ taskDisplayArea.forAllTasks(daTask -> {
+ assertFalse(daTask.isForceHidden());
+ });
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 4a0f48cf2ccb..3894a2eaa461 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -36,7 +36,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -67,7 +66,6 @@ import static org.mockito.Mockito.when;
import android.graphics.Insets;
import android.graphics.Matrix;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -229,21 +227,10 @@ public class WindowStateTests extends WindowTestsBase {
appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
- // Visible app window with flags FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM can't be IME
- // target while an IME window can never be an IME target regardless of its visibility
- // or flags.
- assertFalse(appWindow.canBeImeTarget());
- assertFalse(imeWindow.canBeImeTarget());
-
- // b/145812508: special legacy use-case for transparent/translucent windows.
- appWindow.mAttrs.format = PixelFormat.TRANSPARENT;
- assertTrue(appWindow.canBeImeTarget());
-
- appWindow.mAttrs.format = PixelFormat.OPAQUE;
- appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM;
- assertFalse(appWindow.canBeImeTarget());
- appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
+ // Visible app window with flags can be IME target while an IME window can never be an IME
+ // target regardless of its visibility or flags.
assertTrue(appWindow.canBeImeTarget());
+ assertFalse(imeWindow.canBeImeTarget());
// Verify PINNED windows can't be IME target.
int initialMode = appWindow.mActivityRecord.getWindowingMode();
@@ -263,7 +250,7 @@ public class WindowStateTests extends WindowTestsBase {
// minimized and home stack is resizable, so that we should ignore input for the stack.
final DockedStackDividerController controller =
mDisplayContent.getDockedDividerController();
- final ActivityStack stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ final Task stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
ACTIVITY_TYPE_STANDARD, mDisplayContent);
spyOn(appWindow);
spyOn(controller);
@@ -429,10 +416,11 @@ public class WindowStateTests extends WindowTestsBase {
assertFalse(app.canAffectSystemUiFlags());
}
+ @UseTestDisplay(addWindows = { W_ACTIVITY, W_STATUS_BAR })
@Test
public void testVisibleWithInsetsProvider() {
- final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState statusBar = mStatusBarWindow;
+ final WindowState app = mAppWindow;
statusBar.mHasSurface = true;
assertTrue(statusBar.isVisible());
mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
@@ -554,6 +542,7 @@ public class WindowStateTests extends WindowTestsBase {
assertTrue(window.isVisibleByPolicy());
}
+ @UseTestDisplay(addWindows = { W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE })
@Test
public void testRequestDrawIfNeeded() {
final WindowState startingApp = createWindow(null /* parent */,
@@ -579,6 +568,7 @@ public class WindowStateTests extends WindowTestsBase {
assertEquals(Arrays.asList(keyguardHostWindow, startingWindow), outWaitingForDrawn);
}
+ @UseTestDisplay(addWindows = W_ABOVE_ACTIVITY)
@Test
public void testReportResizedWithRemoteException() {
final WindowState win = mChildAppWindowAbove;
@@ -609,6 +599,7 @@ public class WindowStateTests extends WindowTestsBase {
assertFalse(win.getOrientationChanging());
}
+ @UseTestDisplay(addWindows = W_ABOVE_ACTIVITY)
@Test
public void testRequestResizeForBlastSync() {
final WindowState win = mChildAppWindowAbove;
@@ -689,6 +680,7 @@ public class WindowStateTests extends WindowTestsBase {
assertTrue(win0.cantReceiveTouchInput());
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testNeedsRelativeLayeringToIme_notAttached() {
WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken,
@@ -701,6 +693,7 @@ public class WindowStateTests extends WindowTestsBase {
assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
}
+ @UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
@Test
public void testNeedsRelativeLayeringToIme_startingWindow() {
WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING,
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 49993610bbed..2502932c5421 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -32,8 +32,8 @@ import com.android.server.wm.ActivityTestsBase.ActivityBuilder;
*/
class WindowTestUtils {
- /** Creates a {@link Task} and adds it to the specified {@link ActivityStack}. */
- static Task createTaskInStack(WindowManagerService service, ActivityStack stack, int userId) {
+ /** Creates a {@link Task} and adds it to the specified {@link Task}. */
+ static Task createTaskInStack(WindowManagerService service, Task stack, int userId) {
final Task task = new ActivityTestsBase.TaskBuilder(stack.mStackSupervisor)
.setUserId(userId)
.setStack(stack)
@@ -48,7 +48,7 @@ class WindowTestUtils {
return activity;
}
- static ActivityRecord createTestActivityRecord(ActivityStack stack) {
+ static ActivityRecord createTestActivityRecord(Task stack) {
final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(stack.mAtmService)
.setStack(stack)
.setCreateTask(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index a1e5b80eb2ed..dc388833f338 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -39,34 +39,51 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static org.mockito.Mockito.mock;
-import android.content.Context;
+import android.annotation.IntDef;
import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.IDisplayWindowInsetsController;
import android.view.IWindow;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.WindowManager;
+import com.android.internal.util.ArrayUtils;
import com.android.server.AttributeCache;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.runner.Description;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/** Common base class for window manager unit test classes. */
class WindowTestsBase extends SystemServiceTestsBase {
- private static final String TAG = WindowTestsBase.class.getSimpleName();
WindowManagerService mWm;
private final IWindow mIWindow = new TestIWindow();
private Session mMockSession;
static int sNextStackId = 1000;
- /** Non-default display. */
- DisplayContent mDisplayContent;
DisplayInfo mDisplayInfo = new DisplayInfo();
+ DisplayContent mDefaultDisplay;
+
+ /**
+ * It is {@link #mDefaultDisplay} by default. If the test class or method is annotated with
+ * {@link UseTestDisplay}, it will be an additional display.
+ */
+ DisplayContent mDisplayContent;
+
+ // The following fields are only available depending on the usage of annotation UseTestDisplay.
WindowState mWallpaperWindow;
WindowState mImeWindow;
WindowState mImeDialogWindow;
@@ -93,48 +110,82 @@ class WindowTestsBase extends SystemServiceTestsBase {
mWm = mSystemServicesTestRule.getWindowManagerService();
SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
+ mDefaultDisplay = mWm.mRoot.getDefaultDisplay();
mTransaction = mSystemServicesTestRule.mTransaction;
mMockSession = mock(Session.class);
- final Context context = getInstrumentation().getTargetContext();
- // If @Before throws an exception, the error isn't logged. This will make sure any failures
- // in the set up are clear. This can be removed when b/37850063 is fixed.
- try {
- beforeCreateDisplay();
- context.getDisplay().getDisplayInfo(mDisplayInfo);
- mDisplayContent = createNewDisplay(true /* supportIme */);
+ getInstrumentation().getTargetContext().getSystemService(DisplayManager.class)
+ .getDisplay(Display.DEFAULT_DISPLAY).getDisplayInfo(mDisplayInfo);
+
+ // Only create an additional test display for annotated test class/method because it may
+ // significantly increase the execution time.
+ final Description description = mSystemServicesTestRule.getDescription();
+ UseTestDisplay testDisplayAnnotation = description.getAnnotation(UseTestDisplay.class);
+ if (testDisplayAnnotation == null) {
+ testDisplayAnnotation = description.getTestClass().getAnnotation(UseTestDisplay.class);
+ }
+ if (testDisplayAnnotation != null) {
+ createTestDisplay(testDisplayAnnotation);
+ } else {
+ mDisplayContent = mDefaultDisplay;
+ }
+ }
+
+ private void createTestDisplay(UseTestDisplay annotation) {
+ beforeCreateTestDisplay();
+ mDisplayContent = createNewDisplay(true /* supportIme */);
+
+ final boolean addAll = annotation.addAllCommonWindows();
+ final @CommonTypes int[] requestedWindows = annotation.addWindows();
- // Set-up some common windows.
+ if (addAll || ArrayUtils.contains(requestedWindows, W_WALLPAPER)) {
mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow");
+ }
+ if (addAll || ArrayUtils.contains(requestedWindows, W_INPUT_METHOD)) {
mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow");
mDisplayContent.mInputMethodWindow = mImeWindow;
+ }
+ if (addAll || ArrayUtils.contains(requestedWindows, W_INPUT_METHOD_DIALOG)) {
mImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG,
"mImeDialogWindow");
+ }
+ if (addAll || ArrayUtils.contains(requestedWindows, W_STATUS_BAR)) {
mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow");
+ }
+ if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) {
mNotificationShadeWindow = createCommonWindow(null, TYPE_NOTIFICATION_SHADE,
"mNotificationShadeWindow");
+ }
+ if (addAll || ArrayUtils.contains(requestedWindows, W_NAVIGATION_BAR)) {
mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow");
+ }
+ if (addAll || ArrayUtils.contains(requestedWindows, W_DOCK_DIVIDER)) {
mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER,
"mDockedDividerWindow");
+ }
+ final boolean addAboveApp = ArrayUtils.contains(requestedWindows, W_ABOVE_ACTIVITY);
+ final boolean addBelowApp = ArrayUtils.contains(requestedWindows, W_BELOW_ACTIVITY);
+ if (addAll || addAboveApp || addBelowApp
+ || ArrayUtils.contains(requestedWindows, W_ACTIVITY)) {
mAppWindow = createCommonWindow(null, TYPE_BASE_APPLICATION, "mAppWindow");
- mChildAppWindowAbove = createCommonWindow(mAppWindow,
- TYPE_APPLICATION_ATTACHED_DIALOG,
+ }
+ if (addAll || addAboveApp) {
+ mChildAppWindowAbove = createCommonWindow(mAppWindow, TYPE_APPLICATION_ATTACHED_DIALOG,
"mChildAppWindowAbove");
- mChildAppWindowBelow = createCommonWindow(mAppWindow,
- TYPE_APPLICATION_MEDIA_OVERLAY,
+ }
+ if (addAll || addBelowApp) {
+ mChildAppWindowBelow = createCommonWindow(mAppWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
"mChildAppWindowBelow");
- mDisplayContent.getDisplayPolicy().setForceShowSystemBars(false);
-
- // Adding a display will cause freezing the display. Make sure to wait until it's
- // unfrozen to not run into race conditions with the tests.
- waitUntilHandlersIdle();
- } catch (Exception e) {
- Log.e(TAG, "Failed to set up test", e);
- throw e;
}
+
+ mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(false);
+
+ // Adding a display will cause freezing the display. Make sure to wait until it's
+ // unfrozen to not run into race conditions with the tests.
+ waitUntilHandlersIdle();
}
- void beforeCreateDisplay() {
+ void beforeCreateTestDisplay() {
// Called before display is created.
}
@@ -160,7 +211,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
ActivityRecord createTestActivityRecord(DisplayContent dc, int
windowingMode, int activityType) {
- final ActivityStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc);
+ final Task stack = createTaskStackOnDisplay(windowingMode, activityType, dc);
return WindowTestUtils.createTestActivityRecord(stack);
}
@@ -257,12 +308,26 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
}
- /** Creates a {@link ActivityStack} and adds it to the specified {@link DisplayContent}. */
- ActivityStack createTaskStackOnDisplay(DisplayContent dc) {
+ /** Creates a {@link TaskDisplayArea} right above the default one. */
+ static TaskDisplayArea createTaskDisplayArea(DisplayContent displayContent,
+ WindowManagerService service, String name, int displayAreaFeature) {
+ final TaskDisplayArea newTaskDisplayArea = new TaskDisplayArea(
+ displayContent, service, name, displayAreaFeature);
+ final TaskDisplayArea defaultTaskDisplayArea = displayContent.getDefaultTaskDisplayArea();
+
+ // Insert the new TDA to the correct position.
+ defaultTaskDisplayArea.getParent().addChild(newTaskDisplayArea,
+ defaultTaskDisplayArea.getParent().mChildren.indexOf(defaultTaskDisplayArea)
+ + 1);
+ return newTaskDisplayArea;
+ }
+
+ /** Creates a {@link Task} and adds it to the specified {@link DisplayContent}. */
+ Task createTaskStackOnDisplay(DisplayContent dc) {
return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
}
- ActivityStack createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) {
+ Task createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) {
return new ActivityTestsBase.StackBuilder(dc.mWmService.mRoot)
.setDisplay(dc)
.setWindowingMode(windowingMode)
@@ -272,7 +337,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
.build();
}
- ActivityStack createTaskStackOnTaskDisplayArea(int windowingMode, int activityType,
+ Task createTaskStackOnTaskDisplayArea(int windowingMode, int activityType,
TaskDisplayArea tda) {
return new ActivityTestsBase.StackBuilder(tda.mWmService.mRoot)
.setTaskDisplayArea(tda)
@@ -283,8 +348,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
.build();
}
- /** Creates a {@link Task} and adds it to the specified {@link ActivityStack}. */
- Task createTaskInStack(ActivityStack stack, int userId) {
+ /** Creates a {@link Task} and adds it to the specified {@link Task}. */
+ Task createTaskInStack(Task stack, int userId) {
return WindowTestUtils.createTaskInStack(mWm, stack, userId);
}
@@ -344,8 +409,96 @@ class WindowTestsBase extends SystemServiceTestsBase {
return createNewDisplay(displayInfo, false /* supportIme */);
}
+ IDisplayWindowInsetsController createDisplayWindowInsetsController() {
+ return new IDisplayWindowInsetsController.Stub() {
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) throws RemoteException {
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] insetsSourceControls) throws RemoteException {
+ }
+
+ @Override
+ public void showInsets(int i, boolean b) throws RemoteException {
+ }
+
+ @Override
+ public void hideInsets(int i, boolean b) throws RemoteException {
+ }
+
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ }
+ };
+ }
+
/** Sets the default minimum task size to 1 so that tests can use small task sizes */
void removeGlobalMinSizeRestriction() {
mWm.mAtmService.mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
}
+
+ /**
+ * Avoids rotating screen disturbed by some conditions. It is usually used for the default
+ * display that is not the instance of {@link TestDisplayContent} (it bypasses the conditions).
+ *
+ * @see DisplayRotation#updateRotationUnchecked
+ */
+ void unblockDisplayRotation(DisplayContent dc) {
+ mWm.stopFreezingDisplayLocked();
+ // The rotation animation won't actually play, it needs to be cleared manually.
+ dc.setRotationAnimation(null);
+ }
+
+ // The window definition for UseTestDisplay#addWindows. The test can declare to add only
+ // necessary windows, that avoids adding unnecessary overhead of unused windows.
+ static final int W_NOTIFICATION_SHADE = TYPE_NOTIFICATION_SHADE;
+ static final int W_STATUS_BAR = TYPE_STATUS_BAR;
+ static final int W_NAVIGATION_BAR = TYPE_NAVIGATION_BAR;
+ static final int W_INPUT_METHOD_DIALOG = TYPE_INPUT_METHOD_DIALOG;
+ static final int W_INPUT_METHOD = TYPE_INPUT_METHOD;
+ static final int W_DOCK_DIVIDER = TYPE_DOCK_DIVIDER;
+ static final int W_ABOVE_ACTIVITY = TYPE_APPLICATION_ATTACHED_DIALOG;
+ static final int W_ACTIVITY = TYPE_BASE_APPLICATION;
+ static final int W_BELOW_ACTIVITY = TYPE_APPLICATION_MEDIA_OVERLAY;
+ static final int W_WALLPAPER = TYPE_WALLPAPER;
+
+ /** The common window types supported by {@link UseTestDisplay}. */
+ @Retention(RetentionPolicy.RUNTIME)
+ @IntDef(value = {
+ W_NOTIFICATION_SHADE,
+ W_STATUS_BAR,
+ W_NAVIGATION_BAR,
+ W_INPUT_METHOD_DIALOG,
+ W_INPUT_METHOD,
+ W_DOCK_DIVIDER,
+ W_ABOVE_ACTIVITY,
+ W_ACTIVITY,
+ W_BELOW_ACTIVITY,
+ W_WALLPAPER,
+ })
+ @interface CommonTypes {
+ }
+
+ /**
+ * The annotation for class and method (higher priority) to create a non-default display that
+ * will be assigned to {@link #mDisplayContent}. It is used if the test needs
+ * <ul>
+ * <li>Pure empty display.</li>
+ * <li>Configured common windows.</li>
+ * <li>Independent and customizable orientation.</li>
+ * <li>Cross display operation.</li>
+ * </ul>
+ *
+ * @see TestDisplayContent
+ * @see #createTestDisplay
+ **/
+ @Target({ ElementType.METHOD, ElementType.TYPE })
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface UseTestDisplay {
+ boolean addAllCommonWindows() default false;
+ @CommonTypes int[] addWindows() default {};
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 23a097eb0c7c..f185da395754 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -20,16 +20,21 @@ import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
+import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
@@ -38,6 +43,8 @@ import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.function.BiFunction;
+
/**
* Tests for the {@link WindowToken} class.
*
@@ -77,6 +84,10 @@ public class WindowTokenTests extends WindowTestsBase {
assertFalse(token.hasWindow(window12));
assertTrue(token.hasWindow(window2));
assertTrue(token.hasWindow(window3));
+
+ // The child windows should have the same window token as their parents.
+ assertEquals(window1.mToken, window11.mToken);
+ assertEquals(window1.mToken, window12.mToken);
}
@Test
@@ -135,6 +146,7 @@ public class WindowTokenTests extends WindowTestsBase {
assertEquals(0, token.getWindowsCount());
}
+ @UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER })
@Test
public void testFinishFixedRotationTransform() {
final WindowToken appToken = mAppWindow.mToken;
@@ -205,4 +217,27 @@ public class WindowTokenTests extends WindowTestsBase {
false /* fromClientToken */);
assertNotNull(nonClientToken.mSurfaceControl);
}
+
+ @Test
+ public void testWindowAttachedWithOptions() {
+ BiFunction<WindowToken, Bundle, RootDisplayArea> selectFunc =
+ ((DisplayAreaPolicyBuilder.Result) mDisplayContent.mDisplayAreaPolicy)
+ .mSelectRootForWindowFunc;
+ spyOn(selectFunc);
+
+ final WindowToken token1 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+
+ verify(selectFunc).apply(token1, null);
+
+ final Bundle options = new Bundle();
+ final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+ false /* fromClientToken */, options /* options */);
+
+ verify(selectFunc).apply(token2, options);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index e6b4e0f4baf8..dfb7280e7e54 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -62,6 +62,7 @@ import java.util.function.Function;
*/
@SmallTest
@Presubmit
+@WindowTestsBase.UseTestDisplay(addAllCommonWindows = true)
@RunWith(WindowTestRunner.class)
public class ZOrderingTests extends WindowTestsBase {
@@ -152,7 +153,7 @@ public class ZOrderingTests extends WindowTestsBase {
private LayerRecordingTransaction mTransaction;
@Override
- void beforeCreateDisplay() {
+ void beforeCreateTestDisplay() {
// We can't use @Before here because it may happen after WindowTestsBase @Before
// which is after construction of the DisplayContent, meaning the HierarchyRecorder
// would miss construction of the top-level layers.
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 321657d5d626..9b18ec644ceb 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -215,8 +215,7 @@ public class UsageStatsService extends SystemService implements
mHandler = new H(BackgroundThread.get().getLooper());
mAppStandby = AppStandbyInternal.newAppStandbyController(
- UsageStatsService.class.getClassLoader(), getContext(),
- BackgroundThread.get().getLooper());
+ UsageStatsService.class.getClassLoader(), getContext());
mAppTimeLimit = new AppTimeLimitController(
new AppTimeLimitController.TimeLimitCallbackListener() {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7595e3f249ce..6cf0eecc6352 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -164,6 +164,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17;
private static final int MSG_GADGET_HAL_REGISTERED = 18;
private static final int MSG_RESET_USB_GADGET = 19;
+ private static final int MSG_ACCESSORY_HANDSHAKE_TIMEOUT = 20;
+ private static final int MSG_INCREASE_SENDSTRING_COUNT = 21;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -176,6 +178,13 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// Request is cancelled if host does not configure device within 10 seconds.
private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000;
+ /**
+ * Timeout for receiving USB accessory request
+ * Reset when receive next control request
+ * Broadcast intent if not receive next control request or enter next step.
+ */
+ private static final int ACCESSORY_HANDSHAKE_TIMEOUT = 10 * 1000;
+
private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
@@ -222,8 +231,19 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
String accessory = event.get("ACCESSORY");
if (state != null) {
mHandler.updateState(state);
+ } else if ("GETPROTOCOL".equals(accessory)) {
+ if (DEBUG) Slog.d(TAG, "got accessory get protocol");
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ mHandler.setAccessoryUEventTime(elapsedRealtime);
+ resetAccessoryHandshakeTimeoutHandler();
+ } else if ("SENDSTRING".equals(accessory)) {
+ if (DEBUG) Slog.d(TAG, "got accessory send string");
+ mHandler.sendEmptyMessage(MSG_INCREASE_SENDSTRING_COUNT);
+ resetAccessoryHandshakeTimeoutHandler();
} else if ("START".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory start");
+ mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ mHandler.setStartAccessoryTrue();
startAccessoryMode();
}
}
@@ -395,6 +415,23 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
mHandler.sendEmptyMessage(MSG_UPDATE_USER_RESTRICTIONS);
}
+ /*
+ * Start the timeout-timer upon receiving "get_protocol" uevent.
+ * Restart the timer every time if any succeeding uevnet received.
+ * (Only when USB is under accessory mode)
+ * <p>About the details of the related control request and sequence ordering, refer to
+ * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+ */
+ private void resetAccessoryHandshakeTimeoutHandler() {
+ long functions = getCurrentFunctions();
+ // some accesories send get_protocol after start accessory
+ if ((functions & UsbManager.FUNCTION_ACCESSORY) == 0) {
+ mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT),
+ ACCESSORY_HANDSHAKE_TIMEOUT);
+ }
+ }
+
private void startAccessoryMode() {
if (!mHasUsbAccessory) return;
@@ -416,6 +453,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
if (functions != UsbManager.FUNCTION_NONE) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_MODE_ENTER_TIMEOUT),
ACCESSORY_REQUEST_TIMEOUT);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT),
+ ACCESSORY_HANDSHAKE_TIMEOUT);
setCurrentFunctions(functions);
}
}
@@ -468,6 +507,15 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
private int mMidiCard;
private int mMidiDevice;
+ /**
+ * mAccessoryConnectionStartTime record the timing of start_accessory
+ * mSendStringCount count the number of receiving uevent send_string
+ * mStartAccessory record whether start_accessory is received
+ */
+ private long mAccessoryConnectionStartTime = 0L;
+ private int mSendStringCount = 0;
+ private boolean mStartAccessory = false;
+
private final Context mContext;
private final UsbAlsaManager mUsbAlsaManager;
private final UsbPermissionManager mPermissionManager;
@@ -645,6 +693,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// defer accessoryAttached if system is not ready
if (mBootCompleted) {
mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ broadcastUsbAccessoryHandshake();
} // else handle in boot completed
} else {
Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -701,6 +751,30 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
return false;
}
+ private void broadcastUsbAccessoryHandshake() {
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ long accessoryHandShakeEnd = elapsedRealtime;
+
+ // send a sticky broadcast containing USB accessory handshake information
+ Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
+ mAccessoryConnectionStartTime)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_STRING_COUNT,
+ mSendStringCount)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_START,
+ mStartAccessory)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END,
+ accessoryHandShakeEnd);
+
+ if (DEBUG) {
+ Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
+ }
+
+ sendStickyBroadcast(intent);
+ resetUsbAccessoryHandshakeDebuggingInfo();
+ accessoryHandShakeEnd = 0L;
+ }
+
protected void updateUsbStateBroadcastIfNeeded(long functions) {
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
@@ -998,6 +1072,16 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
break;
}
+ case MSG_ACCESSORY_HANDSHAKE_TIMEOUT: {
+ if (DEBUG) {
+ Slog.v(TAG, "Accessory handshake timeout");
+ }
+ broadcastUsbAccessoryHandshake();
+ break;
+ }
+ case MSG_INCREASE_SENDSTRING_COUNT: {
+ mSendStringCount = mSendStringCount + 1;
+ }
}
}
@@ -1015,6 +1099,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
if (mCurrentAccessory != null) {
mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ broadcastUsbAccessoryHandshake();
}
updateUsbNotification(false);
@@ -1282,6 +1367,9 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
try {
writeStringIfNotNull(dump, "kernel_state", UsbHandlerProto.KERNEL_STATE,
FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
+ } catch (FileNotFoundException exNotFound) {
+ Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: "
+ + "kernel state:" + STATE_PATH);
} catch (Exception e) {
Slog.e(TAG, "Could not read kernel state", e);
}
@@ -1290,6 +1378,9 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
writeStringIfNotNull(dump, "kernel_function_list",
UsbHandlerProto.KERNEL_FUNCTION_LIST,
FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim());
+ } catch (FileNotFoundException exNotFound) {
+ Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: "
+ + "kernel function list:" + FUNCTIONS_PATH);
} catch (Exception e) {
Slog.e(TAG, "Could not read kernel function list", e);
}
@@ -1301,6 +1392,20 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
* Evaluates USB function policies and applies the change accordingly.
*/
protected abstract void setEnabledFunctions(long functions, boolean forceRestart);
+
+ public void setAccessoryUEventTime(long accessoryConnectionStartTime) {
+ mAccessoryConnectionStartTime = accessoryConnectionStartTime;
+ }
+
+ public void setStartAccessoryTrue() {
+ mStartAccessory = true;
+ }
+
+ public void resetUsbAccessoryHandshakeDebuggingInfo() {
+ mAccessoryConnectionStartTime = 0L;
+ mSendStringCount = 0;
+ mStartAccessory = false;
+ }
}
private static final class UsbHandlerLegacy extends UsbHandler {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
index 7e8a90ccd4ad..dcaff26b79c6 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -96,7 +96,8 @@ import java.io.PrintWriter;
*/
public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
static final String TAG = "EventSequenceValidator";
-
+ /** $> adb shell 'setprop log.tag.EventSequenceValidator VERBOSE' */
+ public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private State state = State.INIT;
private long accIntentStartedEvents = 0;
@@ -255,10 +256,12 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
}
private void logWarningWithStackTrace(String log) {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw);
- Log.wtf(TAG, String.format("%s\n%s", log, sw));
+ if (DEBUG) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw);
+ Log.wtf(TAG, String.format("%s\n%s", log, sw));
+ }
}
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 285cf43cd3a1..dee5a98e33e9 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -277,6 +277,11 @@ interface ITelecomService {
boolean setDefaultDialer(in String packageName);
/**
+ * Stop suppressing blocked numbers after a call to emergency services. Shell only.
+ */
+ void stopBlockSuppression();
+
+ /**
* @see TelecomServiceImpl#createManageBlockedNumbersIntent
**/
Intent createManageBlockedNumbersIntent();
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index 4b425bd2dffa..ef94c7677a17 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -1718,7 +1718,6 @@ package android.telephony.ims.feature {
ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
ctor public MmTelFeature.MmTelCapabilities(int);
method public final void addCapabilities(int);
- method public final boolean isCapable(int);
method public final void removeCapabilities(int);
}
diff --git a/telephony/api/system-removed.txt b/telephony/api/system-removed.txt
index c7fd30438dff..ae46075c4829 100644
--- a/telephony/api/system-removed.txt
+++ b/telephony/api/system-removed.txt
@@ -1,11 +1,6 @@
// Signature format: 2.0
package android.telephony {
- public final class PreciseDataConnectionState implements android.os.Parcelable {
- method @Deprecated @Nullable public android.net.LinkProperties getDataConnectionLinkProperties();
- method @Deprecated public int getDataConnectionNetworkType();
- }
-
public class TelephonyManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void answerRingingCall();
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public boolean endCall();
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index 7d3fde61caa8..48794cb37853 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -306,11 +306,12 @@ public final class LocationAccessPolicy {
/** Check if location permissions have been granted */
public static LocationPermissionResult checkLocationPermission(
Context context, LocationPermissionQuery query) {
- // Always allow the phone process and system server to access location. This avoid
- // breaking legacy code that rely on public-facing APIs to access cell location, and
- // it doesn't create an info leak risk because the cell location is stored in the phone
+ // Always allow the phone process, system server, and network stack to access location.
+ // This avoid breaking legacy code that rely on public-facing APIs to access cell location,
+ // and it doesn't create an info leak risk because the cell location is stored in the phone
// process anyway, and the system server already has location access.
if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+ || query.callingUid == Process.NETWORK_STACK_UID
|| query.callingUid == Process.ROOT_UID) {
return LocationPermissionResult.ALLOWED;
}
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index bc987a6282c7..71a1964210b0 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -623,6 +623,10 @@ public final class TelephonyPermissions {
}
private static int getCarrierPrivilegeStatus(Context context, int subId, int uid) {
+ if (uid == Process.SYSTEM_UID || uid == Process.PHONE_UID) {
+ // Skip the check if it's one of these special uids
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ }
final long identity = Binder.clearCallingIdentity();
try {
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
diff --git a/telephony/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java
index 8450a9018634..92e419707970 100644
--- a/telephony/java/android/service/euicc/EuiccProfileInfo.java
+++ b/telephony/java/android/service/euicc/EuiccProfileInfo.java
@@ -29,6 +29,7 @@ import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -231,7 +232,9 @@ public final class EuiccProfileInfo implements Parcelable {
mState = baseProfile.mState;
mCarrierIdentifier = baseProfile.mCarrierIdentifier;
mPolicyRules = baseProfile.mPolicyRules;
- mAccessRules = Arrays.asList(baseProfile.mAccessRules);
+ mAccessRules = baseProfile.mAccessRules == null
+ ? Collections.emptyList()
+ : Arrays.asList(baseProfile.mAccessRules);
}
/** Builds the profile instance. */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 087b64336998..5ea4c7b5dac9 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2382,6 +2382,16 @@ public class CarrierConfigManager {
"call_forwarding_blocks_while_roaming_string_array";
/**
+ * Call forwarding number prefixes defined by {@link
+ * #KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY} which will be allowed while the
+ * device is reporting that it is roaming and IMS is registered over LTE or Wi-Fi.
+ * By default this value is {@code true}.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL =
+ "support_ims_call_forwarding_while_roaming_bool";
+
+ /**
* The day of the month (1-31) on which the data cycle rolls over.
* <p>
* If the current month does not have this day, the cycle will roll over at
@@ -2549,15 +2559,15 @@ public class CarrierConfigManager {
/**
* List of 4 customized 5G SS reference signal received quality (SSRSRQ) thresholds.
* <p>
- * Reference: 3GPP TS 38.215
+ * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10
* <p>
- * 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are:
+ * 4 threshold integers must be within the boundaries [-43 dB, 20 dB], and the levels are:
* <UL>
- * <LI>"NONE: [-20, threshold1]"</LI>
+ * <LI>"NONE: [-43, threshold1]"</LI>
* <LI>"POOR: (threshold1, threshold2]"</LI>
* <LI>"MODERATE: (threshold2, threshold3]"</LI>
* <LI>"GOOD: (threshold3, threshold4]"</LI>
- * <LI>"EXCELLENT: (threshold4, -3]"</LI>
+ * <LI>"EXCELLENT: (threshold4, 20]"</LI>
* </UL>
* <p>
* This key is considered invalid if the format is violated. If the key is invalid or
@@ -3216,6 +3226,17 @@ public class CarrierConfigManager {
"5g_icon_display_secondary_grace_period_string";
/**
+ * Whether device reset all of NR timers when device camped on a network that haven't 5G
+ * capability and RRC currently in IDLE state.
+ *
+ * The default value is false;
+ *
+ * @hide
+ */
+ public static final String KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL =
+ "nr_timers_reset_if_non_endc_and_rrc_idle_bool";
+
+ /**
* Controls time in milliseconds until DcTracker reevaluates 5G connection state.
* @hide
*/
@@ -3861,6 +3882,15 @@ public class CarrierConfigManager {
public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY =
"missed_incoming_call_sms_pattern_string_array";
+ /**
+ * Indicating whether DUN APN should be disabled when the device is roaming. In that case,
+ * the default APN (i.e. internet) will be used for tethering.
+ *
+ * @hide
+ */
+ public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING =
+ "disable_dun_apn_while_roaming";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4203,6 +4233,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
null);
+ sDefaults.putBoolean(KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
@@ -4275,12 +4306,12 @@ public class CarrierConfigManager {
-65, /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
- // Boundaries: [-20 dB, -3 dB]
+ // Boundaries: [-43 dB, 20 dB]
new int[] {
- -16, /* SIGNAL_STRENGTH_POOR */
- -12, /* SIGNAL_STRENGTH_MODERATE */
- -9, /* SIGNAL_STRENGTH_GOOD */
- -6 /* SIGNAL_STRENGTH_GREAT */
+ -31, /* SIGNAL_STRENGTH_POOR */
+ -19, /* SIGNAL_STRENGTH_MODERATE */
+ -7, /* SIGNAL_STRENGTH_GOOD */
+ 6 /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
// Boundaries: [-23 dB, 40 dB]
@@ -4317,6 +4348,7 @@ public class CarrierConfigManager {
+ "not_restricted_rrc_con:5G");
sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
+ sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false);
/* Default value is 1 hour. */
sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
@@ -4396,6 +4428,7 @@ public class CarrierConfigManager {
"ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3"
});
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
+ sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING, false);
}
/**
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 2d0bd52f84ee..aa1230165e13 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -16,7 +16,9 @@
package android.telephony;
+import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
import android.telephony.cdma.CdmaCellLocation;
@@ -31,11 +33,32 @@ import com.android.internal.telephony.PhoneConstants;
public abstract class CellLocation {
/**
- * Request an update of the current location. If the location has changed,
- * a broadcast will be sent to everyone registered with {@link
- * PhoneStateListener#LISTEN_CELL_LOCATION}.
+ * This method will not do anything.
+ *
+ * Whenever location changes, a callback will automatically be be sent to
+ * all registrants of {@link PhoneStateListener#LISTEN_CELL_LOCATION}.
+ *
+ * <p>This method is a no-op for callers targeting SDK level 31 or greater.
+ * <p>This method is a no-op for callers that target SDK level 29 or 30 and lack
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+ * <p>This method is a no-op for callers that target SDK level 28 or below and lack
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ *
+ * Callers wishing to request a single location update should use
+ * {@link TelephonyManager#requestCellInfoUpdate}.
+ *
+ * @deprecated this method has undesirable side-effects, and it calls into the OS without
+ * access to a {@link android.content.Context Context}, meaning that certain safety checks and
+ * attribution are error-prone. Given that this method has numerous downsides, and given that
+ * there are long-available superior alternatives, callers are strongly discouraged from using
+ * this method.
*/
+ @Deprecated
public static void requestLocationUpdate() {
+ // Since this object doesn't have a context, this is the best we can do.
+ final Context appContext = ActivityThread.currentApplication();
+ if (appContext == null) return; // should never happen
+
try {
ITelephony phone = ITelephony.Stub.asInterface(
TelephonyFrameworkInitializer
@@ -43,7 +66,7 @@ public abstract class CellLocation {
.getTelephonyServiceRegisterer()
.get());
if (phone != null) {
- phone.updateServiceLocation();
+ phone.updateServiceLocationWithPackageName(appContext.getOpPackageName());
}
} catch (RemoteException ex) {
// ignore it
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 95fe90a47654..8e50bba38e84 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -54,12 +54,12 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
};
// Lifted from Default carrier configs and max range of SSRSRQ
- // Boundaries: [-20 dB, -3 dB]
+ // Boundaries: [-43 dB, 20 dB]
private int[] mSsRsrqThresholds = new int[] {
- -16, /* SIGNAL_STRENGTH_POOR */
- -12, /* SIGNAL_STRENGTH_MODERATE */
- -9, /* SIGNAL_STRENGTH_GOOD */
- -6 /* SIGNAL_STRENGTH_GREAT */
+ -31, /* SIGNAL_STRENGTH_POOR */
+ -19, /* SIGNAL_STRENGTH_MODERATE */
+ -7, /* SIGNAL_STRENGTH_GOOD */
+ 6 /* SIGNAL_STRENGTH_GREAT */
};
// Lifted from Default carrier configs and max range of SSSINR
@@ -183,8 +183,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
}
/**
- * Reference: 3GPP TS 38.215.
- * Range: -20 dB to -3 dB.
+ * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10
+ * Range: -43 dB to 20 dB.
* @return SS reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported
* value.
*/
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index c667165e7a0e..e91d6fc9d801 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -72,28 +72,20 @@ public final class DataSpecificRegistrationInfo implements Parcelable {
/**
* Provides network support info for LTE VoPS and LTE Emergency bearer support
*/
+ @Nullable
private final LteVopsSupportInfo mLteVopsSupportInfo;
/**
- * Indicates if it's using carrier aggregation
- *
- * @hide
- */
- public boolean mIsUsingCarrierAggregation;
-
- /**
* @hide
*/
DataSpecificRegistrationInfo(
int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
- boolean isEnDcAvailable, LteVopsSupportInfo lteVops,
- boolean isUsingCarrierAggregation) {
+ boolean isEnDcAvailable, @Nullable LteVopsSupportInfo lteVops) {
this.maxDataCalls = maxDataCalls;
this.isDcNrRestricted = isDcNrRestricted;
this.isNrAvailable = isNrAvailable;
this.isEnDcAvailable = isEnDcAvailable;
this.mLteVopsSupportInfo = lteVops;
- this.mIsUsingCarrierAggregation = isUsingCarrierAggregation;
}
/**
@@ -102,32 +94,29 @@ public final class DataSpecificRegistrationInfo implements Parcelable {
* @param dsri another data specific registration info
* @hide
*/
- DataSpecificRegistrationInfo(DataSpecificRegistrationInfo dsri) {
+ DataSpecificRegistrationInfo(@NonNull DataSpecificRegistrationInfo dsri) {
maxDataCalls = dsri.maxDataCalls;
isDcNrRestricted = dsri.isDcNrRestricted;
isNrAvailable = dsri.isNrAvailable;
isEnDcAvailable = dsri.isEnDcAvailable;
mLteVopsSupportInfo = dsri.mLteVopsSupportInfo;
- mIsUsingCarrierAggregation = dsri.mIsUsingCarrierAggregation;
}
- private DataSpecificRegistrationInfo(Parcel source) {
+ private DataSpecificRegistrationInfo(/* @NonNull */ Parcel source) {
maxDataCalls = source.readInt();
isDcNrRestricted = source.readBoolean();
isNrAvailable = source.readBoolean();
isEnDcAvailable = source.readBoolean();
mLteVopsSupportInfo = LteVopsSupportInfo.CREATOR.createFromParcel(source);
- mIsUsingCarrierAggregation = source.readBoolean();
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(/* @NonNull */ Parcel dest, int flags) {
dest.writeInt(maxDataCalls);
dest.writeBoolean(isDcNrRestricted);
dest.writeBoolean(isNrAvailable);
dest.writeBoolean(isEnDcAvailable);
mLteVopsSupportInfo.writeToParcel(dest, flags);
- dest.writeBoolean(mIsUsingCarrierAggregation);
}
@Override
@@ -144,8 +133,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable {
.append(" isDcNrRestricted = " + isDcNrRestricted)
.append(" isNrAvailable = " + isNrAvailable)
.append(" isEnDcAvailable = " + isEnDcAvailable)
- .append(" " + mLteVopsSupportInfo.toString())
- .append(" mIsUsingCarrierAggregation = " + mIsUsingCarrierAggregation)
+ .append(" " + mLteVopsSupportInfo)
.append(" }")
.toString();
}
@@ -153,7 +141,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable,
- mLteVopsSupportInfo, mIsUsingCarrierAggregation);
+ mLteVopsSupportInfo);
}
@Override
@@ -167,8 +155,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable {
&& this.isDcNrRestricted == other.isDcNrRestricted
&& this.isNrAvailable == other.isNrAvailable
&& this.isEnDcAvailable == other.isEnDcAvailable
- && this.mLteVopsSupportInfo.equals(other.mLteVopsSupportInfo)
- && this.mIsUsingCarrierAggregation == other.mIsUsingCarrierAggregation;
+ && Objects.equals(mLteVopsSupportInfo, other.mLteVopsSupportInfo);
}
public static final @NonNull Parcelable.Creator<DataSpecificRegistrationInfo> CREATOR =
@@ -192,23 +179,4 @@ public final class DataSpecificRegistrationInfo implements Parcelable {
return mLteVopsSupportInfo;
}
- /**
- * Set the flag indicating if using carrier aggregation.
- *
- * @param isUsingCarrierAggregation {@code true} if using carrier aggregation.
- * @hide
- */
- public void setIsUsingCarrierAggregation(boolean isUsingCarrierAggregation) {
- mIsUsingCarrierAggregation = isUsingCarrierAggregation;
- }
-
- /**
- * Get whether network has configured carrier aggregation or not.
- *
- * @return {@code true} if using carrier aggregation.
- * @hide
- */
- public boolean isUsingCarrierAggregation() {
- return mIsUsingCarrierAggregation;
- }
}
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 31a83c9334d5..aee1e84ca356 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -218,6 +218,9 @@ public final class NetworkRegistrationInfo implements Parcelable {
@NonNull
private String mRplmn;
+ // Updated based on the accessNetworkTechnology
+ private boolean mIsUsingCarrierAggregation;
+
/**
* @param domain Network domain. Must be a {@link Domain}. For transport type
* {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, this must set to {@link #DOMAIN_PS}.
@@ -251,7 +254,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
mRegistrationState = registrationState;
mRoamingType = (registrationState == REGISTRATION_STATE_ROAMING)
? ServiceState.ROAMING_TYPE_UNKNOWN : ServiceState.ROAMING_TYPE_NOT_ROAMING;
- mAccessNetworkTechnology = accessNetworkTechnology;
+ setAccessNetworkTechnology(accessNetworkTechnology);
mRejectCause = rejectCause;
mAvailableServices = (availableServices != null)
? new ArrayList<>(availableServices) : new ArrayList<>();
@@ -290,13 +293,11 @@ public final class NetworkRegistrationInfo implements Parcelable {
@Nullable CellIdentity cellIdentity, @Nullable String rplmn,
int maxDataCalls, boolean isDcNrRestricted,
boolean isNrAvailable, boolean isEndcAvailable,
- LteVopsSupportInfo lteVopsSupportInfo,
- boolean isUsingCarrierAggregation) {
+ LteVopsSupportInfo lteVopsSupportInfo) {
this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
emergencyOnly, availableServices, cellIdentity, rplmn);
mDataSpecificInfo = new DataSpecificRegistrationInfo(
- maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo,
- isUsingCarrierAggregation);
+ maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo);
updateNrState();
}
@@ -317,6 +318,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
DataSpecificRegistrationInfo.class.getClassLoader());
mNrState = source.readInt();
mRplmn = source.readString();
+ mIsUsingCarrierAggregation = source.readBoolean();
}
/**
@@ -331,6 +333,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
mRegistrationState = nri.mRegistrationState;
mRoamingType = nri.mRoamingType;
mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
+ mIsUsingCarrierAggregation = nri.mIsUsingCarrierAggregation;
mRejectCause = nri.mRejectCause;
mEmergencyOnly = nri.mEmergencyOnly;
mAvailableServices = new ArrayList<>(nri.mAvailableServices);
@@ -484,9 +487,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
if (tech == TelephonyManager.NETWORK_TYPE_LTE_CA) {
// For old device backward compatibility support
tech = TelephonyManager.NETWORK_TYPE_LTE;
- if (mDataSpecificInfo != null) {
- mDataSpecificInfo.setIsUsingCarrierAggregation(true);
- }
+ mIsUsingCarrierAggregation = true;
}
mAccessNetworkTechnology = tech;
}
@@ -511,6 +512,27 @@ public final class NetworkRegistrationInfo implements Parcelable {
}
/**
+ * Set whether network has configured carrier aggregation or not.
+ *
+ * @param isUsingCarrierAggregation set whether or not carrier aggregation is used.
+ *
+ * @hide
+ */
+ public void setIsUsingCarrierAggregation(boolean isUsingCarrierAggregation) {
+ mIsUsingCarrierAggregation = isUsingCarrierAggregation;
+ }
+
+ /**
+ * Get whether network has configured carrier aggregation or not.
+ *
+ * @return {@code true} if using carrier aggregation.
+ * @hide
+ */
+ public boolean isUsingCarrierAggregation() {
+ return mIsUsingCarrierAggregation;
+ }
+
+ /**
* @hide
*/
@Nullable
@@ -572,7 +594,8 @@ public final class NetworkRegistrationInfo implements Parcelable {
return "Unknown reg state " + registrationState;
}
- private static String nrStateToString(@NRState int nrState) {
+ /** @hide */
+ public static String nrStateToString(@NRState int nrState) {
switch (nrState) {
case NR_STATE_RESTRICTED:
return "RESTRICTED";
@@ -616,6 +639,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
.append(" dataSpecificInfo=").append(mDataSpecificInfo)
.append(" nrState=").append(nrStateToString(mNrState))
.append(" rRplmn=").append(mRplmn)
+ .append(" isUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
.append("}").toString();
}
@@ -623,7 +647,8 @@ public final class NetworkRegistrationInfo implements Parcelable {
public int hashCode() {
return Objects.hash(mDomain, mTransportType, mRegistrationState, mRoamingType,
mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices,
- mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn);
+ mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn,
+ mIsUsingCarrierAggregation);
}
@Override
@@ -643,6 +668,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
&& mRejectCause == other.mRejectCause
&& mEmergencyOnly == other.mEmergencyOnly
&& mAvailableServices.equals(other.mAvailableServices)
+ && mIsUsingCarrierAggregation == other.mIsUsingCarrierAggregation
&& Objects.equals(mCellIdentity, other.mCellIdentity)
&& Objects.equals(mVoiceSpecificInfo, other.mVoiceSpecificInfo)
&& Objects.equals(mDataSpecificInfo, other.mDataSpecificInfo)
@@ -669,6 +695,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
dest.writeParcelable(mDataSpecificInfo, 0);
dest.writeInt(mNrState);
dest.writeString(mRplmn);
+ dest.writeBoolean(mIsUsingCarrierAggregation);
}
/**
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index b682bdd7aee3..3dbbf4138b02 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -53,14 +53,13 @@ import java.util.Objects;
*
*/
public final class PreciseDataConnectionState implements Parcelable {
-
- private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
- private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- private @DataFailureCause int mFailCause = DataFailCause.NONE;
- private @ApnType int mApnTypes = ApnSetting.TYPE_NONE;
- private String mApn = "";
- private LinkProperties mLinkProperties = null;
- private ApnSetting mApnSetting = null;
+ private final @DataState int mState;
+ private final @NetworkType int mNetworkType;
+ private final @DataFailureCause int mFailCause;
+ private final @ApnType int mApnTypes;
+ private final String mApn;
+ private final LinkProperties mLinkProperties;
+ private final ApnSetting mApnSetting;
/**
* Constructor
@@ -84,20 +83,21 @@ public final class PreciseDataConnectionState implements Parcelable {
/**
* Constructor of PreciseDataConnectionState
*
- * @param state the state of the data connection
- * @param networkType the access network that is/would carry this data connection
- * @param apnTypes the APN types that this data connection carries
- * @param apn the APN of this data connection
- * @param linkProperties if the data connection is connected, the properties of the connection
- * @param failCause in case a procedure related to this data connection fails, a non-zero error
+ * @param state The state of the data connection
+ * @param networkType The access network that is/would carry this data connection
+ * @param apnTypes The APN types that this data connection carries
+ * @param apn The APN of this data connection
+ * @param linkProperties If the data connection is connected, the properties of the connection
+ * @param failCause In case a procedure related to this data connection fails, a non-zero error
* code indicating the cause of the failure.
- * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings;
+ * @param apnSetting If there is a valid APN for this Data Connection, then the APN Settings;
* if there is no valid APN setting for the specific type, then this will be null
* @hide
*/
- public PreciseDataConnectionState(@DataState int state,
+ private PreciseDataConnectionState(@DataState int state,
@NetworkType int networkType,
- @ApnType int apnTypes, @NonNull String apn,
+ @ApnType int apnTypes,
+ @NonNull String apn,
@Nullable LinkProperties linkProperties,
@DataFailureCause int failCause,
@Nullable ApnSetting apnSetting) {
@@ -111,14 +111,6 @@ public final class PreciseDataConnectionState implements Parcelable {
}
/**
- * Empty Constructor
- *
- * @hide
- */
- public PreciseDataConnectionState() {
- }
-
- /**
* Construct a PreciseDataConnectionState object from the given parcel.
*
* @hide
@@ -167,22 +159,9 @@ public final class PreciseDataConnectionState implements Parcelable {
}
/**
- * Returns the network type associated with this data connection.
+ * Get the network type associated with this data connection.
*
- * @deprecated use {@link getNetworkType()}
- * @hide
- * @removed Removed from the R preview SDK but was never part of the stable API surface.
- */
- @Deprecated
- @SystemApi
- public @NetworkType int getDataConnectionNetworkType() {
- return mNetworkType;
- }
-
- /**
- * Returns the network type associated with this data connection.
- *
- * Return the current/latest (radio) bearer technology that carries this data connection.
+ * @return The current/latest (radio) bearer technology that carries this data connection.
* For a variety of reasons, the network type can change during the life of the data
* connection, and this information is not reliable unless the physical link is currently
* active; (there is currently no mechanism to know whether the physical link is active at
@@ -220,20 +199,6 @@ public final class PreciseDataConnectionState implements Parcelable {
/**
* Get the properties of the network link {@link LinkProperties}.
- *
- * @deprecated use {@link #getLinkProperties()}
- * @hide
- * @removed Removed from the R preview SDK but was never part of the stable API surface.
- */
- @Deprecated
- @SystemApi
- @Nullable
- public LinkProperties getDataConnectionLinkProperties() {
- return mLinkProperties;
- }
-
- /**
- * Get the properties of the network link {@link LinkProperties}.
*/
@Nullable
public LinkProperties getLinkProperties() {
@@ -301,24 +266,23 @@ public final class PreciseDataConnectionState implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mState, mNetworkType, mApnTypes, mApn, mLinkProperties,
- mFailCause, mApnSetting);
+ return Objects.hash(mState, mNetworkType, mFailCause, mApnTypes, mApn, mLinkProperties,
+ mApnSetting);
}
- @Override
- public boolean equals(@Nullable Object obj) {
- if (!(obj instanceof PreciseDataConnectionState)) {
- return false;
- }
-
- PreciseDataConnectionState other = (PreciseDataConnectionState) obj;
- return Objects.equals(mApn, other.mApn) && mApnTypes == other.mApnTypes
- && mFailCause == other.mFailCause
- && Objects.equals(mLinkProperties, other.mLinkProperties)
- && mNetworkType == other.mNetworkType
- && mState == other.mState
- && Objects.equals(mApnSetting, other.mApnSetting);
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PreciseDataConnectionState that = (PreciseDataConnectionState) o;
+ return mState == that.mState
+ && mNetworkType == that.mNetworkType
+ && mFailCause == that.mFailCause
+ && mApnTypes == that.mApnTypes
+ && Objects.equals(mApn, that.mApn)
+ && Objects.equals(mLinkProperties, that.mLinkProperties)
+ && Objects.equals(mApnSetting, that.mApnSetting);
}
@NonNull
@@ -336,4 +300,123 @@ public final class PreciseDataConnectionState implements Parcelable {
return sb.toString();
}
+
+ /**
+ * {@link PreciseDataConnectionState} builder
+ *
+ * @hide
+ */
+ public static final class Builder {
+ /** The state of the data connection */
+ private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
+
+ /** The network type associated with this data connection */
+ private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+ /** The APN types that this data connection carries */
+ private @ApnType int mApnTypes = ApnSetting.TYPE_NONE;
+
+ /** The APN of this data connection */
+ private @NonNull String mApn = "";
+
+ /** If the data connection is connected, the properties of the connection */
+ private @Nullable LinkProperties mLinkProperties = null;
+
+ /**
+ * In case a procedure related to this data connection fails, a non-zero error code
+ * indicating the cause of the failure.
+ */
+ private @DataFailureCause int mFailCause = DataFailCause.NONE;
+
+ /** The APN Setting for this data connection */
+ private @Nullable ApnSetting mApnSetting = null;
+
+ /**
+ * Set the state of the data connection.
+ *
+ * @param state The state of the data connection
+ * @return The builder
+ */
+ public Builder setState(@DataState int state) {
+ mState = state;
+ return this;
+ }
+
+ /**
+ * Set the network type associated with this data connection.
+ *
+ * @param networkType The network type
+ * @return The builder
+ */
+ public Builder setNetworkType(@NetworkType int networkType) {
+ mNetworkType = networkType;
+ return this;
+ }
+
+ /**
+ * Set the APN types that this data connection carries
+ *
+ * @param apnTypes The APN types
+ * @return The builder
+ */
+ public Builder setApnTypes(@ApnType int apnTypes) {
+ mApnTypes = apnTypes;
+ return this;
+ }
+
+ /**
+ * Set the APN of this data connection
+ *
+ * @param apn The APN of this data connection
+ * @return The builder
+ */
+ public Builder setApn(@NonNull String apn) {
+ mApn = apn;
+ return this;
+ }
+
+ /**
+ * Set the link properties of the connection.
+ *
+ * @param linkProperties Link properties
+ * @return The builder
+ */
+ public Builder setLinkProperties(@NonNull LinkProperties linkProperties) {
+ mLinkProperties = linkProperties;
+ return this;
+ }
+
+ /**
+ * Set the fail cause of the data connection.
+ *
+ * @param failCause In case a procedure related to this data connection fails, a non-zero
+ * error code indicating the cause of the failure.
+ * @return The builder
+ */
+ public Builder setFailCause(@DataFailureCause int failCause) {
+ mFailCause = failCause;
+ return this;
+ }
+
+ /**
+ * Set the APN Setting for this data connection.
+ *
+ * @param apnSetting APN setting
+ * @return This builder
+ */
+ public Builder setApnSetting(@NonNull ApnSetting apnSetting) {
+ mApnSetting = apnSetting;
+ return this;
+ }
+
+ /**
+ * Build the {@link PreciseDataConnectionState} instance.
+ *
+ * @return The {@link PreciseDataConnectionState} instance
+ */
+ public PreciseDataConnectionState build() {
+ return new PreciseDataConnectionState(mState, mNetworkType, mApnTypes, mApn,
+ mLinkProperties, mFailCause, mApnSetting);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 9e2ba6875577..3e7464739f9f 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1412,29 +1412,14 @@ public class ServiceState implements Parcelable {
/** @hide */
public boolean isUsingCarrierAggregation() {
- boolean isUsingCa = false;
- NetworkRegistrationInfo nri = getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (nri != null) {
- DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
- if (dsri != null) {
- isUsingCa = dsri.isUsingCarrierAggregation();
- }
- }
- return isUsingCa || getCellBandwidths().length > 1;
- }
+ if (getCellBandwidths().length > 1) return true;
- /** @hide */
- public void setIsUsingCarrierAggregation(boolean ca) {
- NetworkRegistrationInfo nri = getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (nri != null) {
- DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
- if (dsri != null) {
- dsri.setIsUsingCarrierAggregation(ca);
- addNetworkRegistrationInfo(nri);
+ synchronized (mNetworkRegistrationInfos) {
+ for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) {
+ if (nri.isUsingCarrierAggregation()) return true;
}
}
+ return false;
}
/**
diff --git a/telephony/java/android/telephony/SmsCbEtwsInfo.java b/telephony/java/android/telephony/SmsCbEtwsInfo.java
index 2a7f7ad81e3b..a98916d715e4 100644
--- a/telephony/java/android/telephony/SmsCbEtwsInfo.java
+++ b/telephony/java/android/telephony/SmsCbEtwsInfo.java
@@ -27,6 +27,7 @@ import com.android.internal.telephony.uicc.IccUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
@@ -173,7 +174,7 @@ public final class SmsCbEtwsInfo implements Parcelable {
/**
* Returns the Warning-Security-Information timestamp (GSM primary notifications only).
* As of Release 10, 3GPP TS 23.041 states that the UE shall ignore this value if received.
- * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present
+ * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present or invalid.
*/
public long getPrimaryNotificationTimestamp() {
if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 7) {
@@ -201,18 +202,23 @@ public final class SmsCbEtwsInfo implements Parcelable {
// timezoneOffset is in quarter hours.
int timeZoneOffsetSeconds = timezoneOffset * 15 * 60;
- LocalDateTime localDateTime = LocalDateTime.of(
- // We only need to support years above 2000.
- year + 2000,
- month /* 1-12 */,
- day,
- hour,
- minute,
- second);
-
- long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds;
- // Convert to milliseconds, ignore overflow.
- return epochSeconds * 1000;
+ try {
+ LocalDateTime localDateTime = LocalDateTime.of(
+ // We only need to support years above 2000.
+ year + 2000,
+ month /* 1-12 */,
+ day,
+ hour,
+ minute,
+ second);
+
+ long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds;
+ // Convert to milliseconds, ignore overflow.
+ return epochSeconds * 1000;
+ } catch (DateTimeException ex) {
+ // No-op
+ }
+ return 0;
}
/**
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index b376660f839e..183fdcce1ca4 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -670,10 +670,12 @@ public final class SmsManager {
}
if (priority < 0x00 || priority > 0x03) {
+ Log.e(TAG, "Invalid Priority " + priority);
priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
}
if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+ Log.e(TAG, "Invalid Validity Period " + validityPeriod);
validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
}
@@ -1231,10 +1233,12 @@ public final class SmsManager {
}
if (priority < 0x00 || priority > 0x03) {
+ Log.e(TAG, "Invalid Priority " + priority);
priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
}
if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+ Log.e(TAG, "Invalid Validity Period " + validityPeriod);
validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 347dcc81ce4e..717a9b155cbf 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -255,28 +255,6 @@ public class SmsMessage {
}
/**
- * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
- * +CMT unsolicited response (PDU mode, of course)
- * +CMT: [&lt;alpha>],<length><CR><LF><pdu>
- *
- * Only public for debugging and for RIL
- *
- * {@hide}
- */
- public static SmsMessage newFromCMT(byte[] pdu) {
- // received SMS in 3GPP format
- SmsMessageBase wrappedMessage =
- com.android.internal.telephony.gsm.SmsMessage.newFromCMT(pdu);
-
- if (wrappedMessage != null) {
- return new SmsMessage(wrappedMessage);
- } else {
- Rlog.e(LOG_TAG, "newFromCMT(): wrappedMessage is null");
- return null;
- }
- }
-
- /**
* Creates an SmsMessage from an SMS EF record.
*
* @param index Index of SMS EF record.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3104c20b0063..f9148d0c44c4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -113,8 +113,6 @@ import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.SmsApplication;
import com.android.telephony.Rlog;
-import java.io.FileInputStream;
-import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -127,8 +125,6 @@ import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Provides access to information about the telephony services on
@@ -2341,58 +2337,6 @@ public class TelephonyManager {
}
/**
- * Enables location update notifications. {@link PhoneStateListener#onCellLocationChanged
- * PhoneStateListener.onCellLocationChanged} will be called on location updates.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
- public void enableLocationUpdates() {
- enableLocationUpdates(getSubId());
- }
-
- /**
- * Enables location update notifications for a subscription.
- * {@link PhoneStateListener#onCellLocationChanged
- * PhoneStateListener.onCellLocationChanged} will be called on location updates.
- *
- * @param subId for which the location updates are enabled
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
- public void enableLocationUpdates(int subId) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.enableLocationUpdatesForSubscriber(subId);
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- }
- }
-
- /**
- * Disables location update notifications. {@link PhoneStateListener#onCellLocationChanged
- * PhoneStateListener.onCellLocationChanged} will be called on location updates.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
- public void disableLocationUpdates() {
- disableLocationUpdates(getSubId());
- }
-
- /** @hide */
- public void disableLocationUpdates(int subId) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.disableLocationUpdatesForSubscriber(subId);
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- }
- }
-
- /**
* Returns the neighboring cell information of the device.
*
* @return List of NeighboringCellInfo or null if info unavailable.
@@ -2592,7 +2536,8 @@ public class TelephonyManager {
return PhoneConstants.PHONE_TYPE_CDMA;
case RILConstants.NETWORK_MODE_LTE_ONLY:
- if (getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+ if (TelephonyProperties.lte_on_cdma_device().orElse(
+ PhoneConstants.LTE_ON_CDMA_FALSE) == PhoneConstants.LTE_ON_CDMA_TRUE) {
return PhoneConstants.PHONE_TYPE_CDMA;
} else {
return PhoneConstants.PHONE_TYPE_GSM;
@@ -2603,35 +2548,6 @@ public class TelephonyManager {
}
/**
- * The contents of the /proc/cmdline file
- */
- @UnsupportedAppUsage
- private static String getProcCmdLine()
- {
- String cmdline = "";
- FileInputStream is = null;
- try {
- is = new FileInputStream("/proc/cmdline");
- byte [] buffer = new byte[2048];
- int count = is.read(buffer);
- if (count > 0) {
- cmdline = new String(buffer, 0, count);
- }
- } catch (IOException e) {
- Rlog.d(TAG, "No /proc/cmdline exception=" + e);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- }
- }
- }
- Rlog.d(TAG, "/proc/cmdline=" + cmdline);
- return cmdline;
- }
-
- /**
* @return The max value for the timeout passed in {@link #requestNumberVerification}.
* @hide
*/
@@ -2640,56 +2556,6 @@ public class TelephonyManager {
return MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS;
}
- /** Kernel command line */
- private static final String sKernelCmdLine = getProcCmdLine();
-
- /** Pattern for selecting the product type from the kernel command line */
- private static final Pattern sProductTypePattern =
- Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)");
-
- /** The ProductType used for LTE on CDMA devices */
- private static final String sLteOnCdmaProductType =
- TelephonyProperties.lte_on_cdma_product_type().orElse("");
-
- /**
- * Return if the current radio is LTE on CDMA. This
- * is a tri-state return value as for a period of time
- * the mode may be unknown.
- *
- * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
- * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
- *
- * @hide
- */
- @UnsupportedAppUsage
- public static int getLteOnCdmaModeStatic() {
- int retVal;
- int curVal;
- String productType = "";
-
- curVal = TelephonyProperties.lte_on_cdma_device().orElse(
- PhoneConstants.LTE_ON_CDMA_UNKNOWN);
- retVal = curVal;
- if (retVal == PhoneConstants.LTE_ON_CDMA_UNKNOWN) {
- Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine);
- if (matcher.find()) {
- productType = matcher.group(1);
- if (sLteOnCdmaProductType.equals(productType)) {
- retVal = PhoneConstants.LTE_ON_CDMA_TRUE;
- } else {
- retVal = PhoneConstants.LTE_ON_CDMA_FALSE;
- }
- } else {
- retVal = PhoneConstants.LTE_ON_CDMA_FALSE;
- }
- }
-
- Rlog.d(TAG, "getLteOnCdmaMode=" + retVal + " curVal=" + curVal +
- " product_type='" + productType +
- "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'");
- return retVal;
- }
-
//
//
// Current Network
@@ -9290,17 +9156,14 @@ public class TelephonyManager {
return RADIO_POWER_UNAVAILABLE;
}
- /** @hide */
+ /**
+ * This method should not be used due to privacy and stability concerns.
+ *
+ * @hide
+ */
@SystemApi
- @SuppressLint("Doclava125")
public void updateServiceLocation() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.updateServiceLocation();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#updateServiceLocation", e);
- }
+ Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()");
}
/** @hide */
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index cdff651c9c3d..e890acb36b48 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -30,6 +30,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.telephony.ITelephony;
import com.android.telephony.Rlog;
@@ -55,6 +56,8 @@ public final class TelephonyScanManager {
public static final int CALLBACK_SCAN_COMPLETE = 3;
/** @hide */
public static final int CALLBACK_RESTRICTED_SCAN_RESULTS = 4;
+ /** @hide */
+ public static final int CALLBACK_TELEPHONY_DIED = 5;
/** @hide */
public static final int INVALID_SCAN_ID = -1;
@@ -103,17 +106,44 @@ public final class TelephonyScanManager {
}
private final Looper mLooper;
+ private final Handler mHandler;
private final Messenger mMessenger;
private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
+ private final Binder.DeathRecipient mDeathRecipient;
public TelephonyScanManager() {
HandlerThread thread = new HandlerThread(TAG);
thread.start();
mLooper = thread.getLooper();
- mMessenger = new Messenger(new Handler(mLooper) {
+ mHandler = new Handler(mLooper) {
@Override
public void handleMessage(Message message) {
checkNotNull(message, "message cannot be null");
+ if (message.what == CALLBACK_TELEPHONY_DIED) {
+ // If there are no objects in mScanInfo then binder death will simply return.
+ synchronized (mScanInfo) {
+ for (int i = 0; i < mScanInfo.size(); i++) {
+ NetworkScanInfo nsi = mScanInfo.valueAt(i);
+ // At this point we go into panic mode and ignore errors that would
+ // normally stop the show in order to try and clean up as gracefully
+ // as possible.
+ if (nsi == null) continue; // shouldn't be possible
+ Executor e = nsi.mExecutor;
+ NetworkScanCallback cb = nsi.mCallback;
+ if (e == null || cb == null) continue;
+ try {
+ e.execute(
+ () -> cb.onError(NetworkScan.ERROR_MODEM_UNAVAILABLE));
+ } catch (java.util.concurrent.RejectedExecutionException ignore) {
+ // ignore so that we can continue
+ }
+ }
+
+ mScanInfo.clear();
+ }
+ return;
+ }
+
NetworkScanInfo nsi;
synchronized (mScanInfo) {
nsi = mScanInfo.get(message.arg2);
@@ -158,6 +188,9 @@ public final class TelephonyScanManager {
Rlog.d(TAG, "onError: " + errorCode);
callback.onError(errorCode);
});
+ synchronized (mScanInfo) {
+ mScanInfo.remove(message.arg2);
+ }
} catch (Exception e) {
Rlog.e(TAG, "Exception in networkscan callback onError", e);
}
@@ -168,7 +201,9 @@ public final class TelephonyScanManager {
Rlog.d(TAG, "onComplete");
callback.onComplete();
});
- mScanInfo.remove(message.arg2);
+ synchronized (mScanInfo) {
+ mScanInfo.remove(message.arg2);
+ }
} catch (Exception e) {
Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
}
@@ -178,7 +213,14 @@ public final class TelephonyScanManager {
break;
}
}
- });
+ };
+ mMessenger = new Messenger(mHandler);
+ mDeathRecipient = new Binder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mHandler.obtainMessage(CALLBACK_TELEPHONY_DIED).sendToTarget();
+ }
+ };
}
/**
@@ -189,7 +231,7 @@ public final class TelephonyScanManager {
*
* <p>
* Requires Permission:
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
*
@@ -204,19 +246,26 @@ public final class TelephonyScanManager {
NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
String callingPackage, @Nullable String callingFeatureId) {
try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- synchronized (mScanInfo) {
- int scanId = telephony.requestNetworkScan(
- subId, request, mMessenger, new Binder(), callingPackage,
- callingFeatureId);
- if (scanId == INVALID_SCAN_ID) {
- Rlog.e(TAG, "Failed to initiate network scan");
- return null;
- }
- saveScanInfo(scanId, request, executor, callback);
- return new NetworkScan(scanId, subId);
- }
+ final ITelephony telephony = getITelephony();
+ if (telephony == null) return null;
+
+ int scanId = telephony.requestNetworkScan(
+ subId, request, mMessenger, new Binder(), callingPackage,
+ callingFeatureId);
+ if (scanId == INVALID_SCAN_ID) {
+ Rlog.e(TAG, "Failed to initiate network scan");
+ return null;
+ }
+ synchronized (mScanInfo) {
+ // We link to death whenever a scan is started to ensure that we are linked
+ // at the point that phone process death might matter.
+ // We never unlink because:
+ // - Duplicate links to death with the same callback do not result in
+ // extraneous callbacks (the tracking de-dupes).
+ // - Receiving binderDeath() when no scans are active is a no-op.
+ telephony.asBinder().linkToDeath(mDeathRecipient, 0);
+ saveScanInfo(scanId, request, executor, callback);
+ return new NetworkScan(scanId, subId);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "requestNetworkScan RemoteException", ex);
@@ -226,6 +275,7 @@ public final class TelephonyScanManager {
return null;
}
+ @GuardedBy("mScanInfo")
private void saveScanInfo(
int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 01d468cb53f6..d8a10ebb7bde 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -285,8 +285,8 @@ public class MmTelFeature extends ImsFeature {
public static final int CAPABILITY_TYPE_SMS = 1 << 3;
/**
- * @hide
- */
+ * @hide
+ */
@Override
@SystemApi @TestApi
public final void addCapabilities(@MmTelCapability int capabilities) {
@@ -294,8 +294,8 @@ public class MmTelFeature extends ImsFeature {
}
/**
- * @hide
- */
+ * @hide
+ */
@Override
@SystemApi @TestApi
public final void removeCapabilities(@MmTelCapability int capability) {
@@ -303,17 +303,18 @@ public class MmTelFeature extends ImsFeature {
}
/**
- * @hide
- */
+ * @param capabilities a bitmask of one or more capabilities.
+ *
+ * @return true if all queried capabilities are true, otherwise false.
+ */
@Override
- @SystemApi @TestApi
public final boolean isCapable(@MmTelCapability int capabilities) {
return super.isCapable(capabilities);
}
/**
- * @hide
- */
+ * @hide
+ */
@NonNull
@Override
public String toString() {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4276e713561b..e2de5c82940f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -222,42 +222,29 @@ interface ITelephony {
boolean setRadioPower(boolean turnOn);
/**
- * Request to update location information in service state
+ * This method has been removed due to security and stability issues.
*/
@UnsupportedAppUsage
void updateServiceLocation();
/**
- * Request to update location information for a subscrition in service state
- * @param subId user preferred subId.
+ * Version of updateServiceLocation that records the caller and validates permissions.
*/
- void updateServiceLocationForSubscriber(int subId);
+ void updateServiceLocationWithPackageName(String callingPkg);
/**
- * Enable location update notifications.
+ * This method has been removed due to security and stability issues.
*/
@UnsupportedAppUsage
void enableLocationUpdates();
/**
- * Enable location update notifications.
- * @param subId user preferred subId.
- */
- void enableLocationUpdatesForSubscriber(int subId);
-
- /**
- * Disable location update notifications.
+ * This method has been removed due to security and stability issues.
*/
@UnsupportedAppUsage
void disableLocationUpdates();
/**
- * Disable location update notifications.
- * @param subId user preferred subId.
- */
- void disableLocationUpdatesForSubscriber(int subId);
-
- /**
* Allow mobile data connections.
*/
@UnsupportedAppUsage
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index 3bd8cdd23df3..f8ab87d042eb 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -65,13 +65,7 @@ public class Sms7BitEncodingTranslator {
return "";
}
- if (!mIs7BitTranslationTableLoaded) {
- mTranslationTableCommon = new SparseIntArray();
- mTranslationTableGSM = new SparseIntArray();
- mTranslationTableCDMA = new SparseIntArray();
- load7BitTranslationTableFromXml();
- mIs7BitTranslationTableLoaded = true;
- }
+ ensure7BitTranslationTableLoaded();
if ((mTranslationTableCommon != null && mTranslationTableCommon.size() > 0) ||
(mTranslationTableGSM != null && mTranslationTableGSM.size() > 0) ||
@@ -115,6 +109,8 @@ public class Sms7BitEncodingTranslator {
*/
int translation = -1;
+ ensure7BitTranslationTableLoaded();
+
if (mTranslationTableCommon != null) {
translation = mTranslationTableCommon.get(c, -1);
}
@@ -155,6 +151,18 @@ public class Sms7BitEncodingTranslator {
}
}
+ private static void ensure7BitTranslationTableLoaded() {
+ synchronized (Sms7BitEncodingTranslator.class) {
+ if (!mIs7BitTranslationTableLoaded) {
+ mTranslationTableCommon = new SparseIntArray();
+ mTranslationTableGSM = new SparseIntArray();
+ mTranslationTableCDMA = new SparseIntArray();
+ load7BitTranslationTableFromXml();
+ mIs7BitTranslationTableLoaded = true;
+ }
+ }
+ }
+
/**
* Load the whole translation table file from the framework resource
* encoded in XML.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index d41a6c889afb..d216162cc257 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -171,8 +171,8 @@ public class TelephonyIntents {
* if not specified </dd>
* </dl>
*
- * <p class="note">
- * Requires the READ_PHONE_STATE permission.
+ * <p class="note">This is a sticky broadcast, and therefore requires no permissions to listen
+ * to. Do not add any additional information to this broadcast.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 4fbafb78fe3c..d186fcf63cfe 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -35,6 +35,7 @@ import com.android.internal.util.BitwiseOutputStream;
import com.android.telephony.Rlog;
import java.io.ByteArrayOutputStream;
+import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -315,10 +316,16 @@ public final class BearerData {
}
public long toMillis() {
- LocalDateTime localDateTime =
- LocalDateTime.of(year, monthOrdinal, monthDay, hour, minute, second);
- Instant instant = localDateTime.toInstant(mZoneId.getRules().getOffset(localDateTime));
- return instant.toEpochMilli();
+ try {
+ LocalDateTime localDateTime =
+ LocalDateTime.of(year, monthOrdinal, monthDay, hour, minute, second);
+ Instant instant =
+ localDateTime.toInstant(mZoneId.getRules().getOffset(localDateTime));
+ return instant.toEpochMilli();
+ } catch (DateTimeException ex) {
+ Rlog.e(LOG_TAG, "Invalid timestamp", ex);
+ }
+ return 0;
}
@@ -1093,7 +1100,7 @@ public final class BearerData {
bData.hasUserDataHeader = (inStream.read(1) == 1);
inStream.skip(3);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1462,7 +1469,7 @@ public final class BearerData {
bData.reportReq = (inStream.read(1) == 1);
inStream.skip(4);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "REPLY_OPTION decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1481,7 +1488,7 @@ public final class BearerData {
decodeSuccess = true;
bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1500,7 +1507,7 @@ public final class BearerData {
decodeSuccess = true;
bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1587,7 +1594,7 @@ public final class BearerData {
bData.errorClass = inStream.read(2);
bData.messageStatus = inStream.read(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1607,7 +1614,7 @@ public final class BearerData {
decodeSuccess = true;
bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1626,7 +1633,7 @@ public final class BearerData {
decodeSuccess = true;
bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1646,7 +1653,7 @@ public final class BearerData {
bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
inStream.readByteArray(6 * 8));
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1665,7 +1672,7 @@ public final class BearerData {
decodeSuccess = true;
bData.deferredDeliveryTimeRelative = inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1685,7 +1692,7 @@ public final class BearerData {
decodeSuccess = true;
bData.validityPeriodRelative = inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1706,7 +1713,7 @@ public final class BearerData {
bData.privacy = inStream.read(2);
inStream.skip(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1726,7 +1733,7 @@ public final class BearerData {
decodeSuccess = true;
bData.language = inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1747,7 +1754,7 @@ public final class BearerData {
bData.displayMode = inStream.read(2);
inStream.skip(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "DISPLAY_MODE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1768,7 +1775,7 @@ public final class BearerData {
bData.priority = inStream.read(2);
inStream.skip(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1789,7 +1796,7 @@ public final class BearerData {
bData.alert = inStream.read(2);
inStream.skip(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1809,7 +1816,7 @@ public final class BearerData {
decodeSuccess = true;
bData.userResponseCode = inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1871,7 +1878,7 @@ public final class BearerData {
decodeSuccess = true;
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ')');
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index ccb14749109d..7e31c4633050 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -43,6 +43,7 @@ import com.android.telephony.Rlog;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
+import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -91,14 +92,15 @@ public class SmsMessage extends SmsMessageBase {
private int mVoiceMailCount = 0;
+ /** TP-Validity-Period-Format (TP-VPF). See TS 23.040, 9.2.3.3 */
private static final int VALIDITY_PERIOD_FORMAT_NONE = 0x00;
private static final int VALIDITY_PERIOD_FORMAT_ENHANCED = 0x01;
private static final int VALIDITY_PERIOD_FORMAT_RELATIVE = 0x02;
private static final int VALIDITY_PERIOD_FORMAT_ABSOLUTE = 0x03;
- //Validity Period min - 5 mins
+ // Validity Period min - 5 mins
private static final int VALIDITY_PERIOD_MIN = 5;
- //Validity Period max - 63 weeks
+ // Validity Period max - 63 weeks
private static final int VALIDITY_PERIOD_MAX = 635040;
private static final int INVALID_VALIDITY_PERIOD = -1;
@@ -139,38 +141,6 @@ public class SmsMessage extends SmsMessageBase {
}
/**
- * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
- * +CMT unsolicited response (PDU mode, of course)
- * +CMT: [&lt;alpha>],<length><CR><LF><pdu>
- *
- * Only public for debugging
- *
- * {@hide}
- */
- public static SmsMessage newFromCMT(byte[] pdu) {
- try {
- SmsMessage msg = new SmsMessage();
- msg.parsePdu(pdu);
- return msg;
- } catch (RuntimeException ex) {
- Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
- return null;
- }
- }
-
- /** @hide */
- public static SmsMessage newFromCDS(byte[] pdu) {
- try {
- SmsMessage msg = new SmsMessage();
- msg.parsePdu(pdu);
- return msg;
- } catch (RuntimeException ex) {
- Rlog.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex);
- return null;
- }
- }
-
- /**
* Creates an SmsMessage from an SMS EF record.
*
* @param index Index of SMS EF record.
@@ -224,20 +194,20 @@ public class SmsMessage extends SmsMessageBase {
}
/**
- * Get Encoded Relative Validty Period Value from Validity period in mins.
+ * Gets Encoded Relative Validity Period Value from Validity period in mins.
*
* @param validityPeriod Validity period in mins.
*
* Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
- * ||relValidityPeriod (TP-VP) || || validityPeriod ||
- *
- * 0 to 143 ---> (TP-VP + 1) x 5 minutes
- *
- * 144 to 167 ---> 12 hours + ((TP-VP -143) x 30 minutes)
- *
- * 168 to 196 ---> (TP-VP - 166) x 1 day
- *
- * 197 to 255 ---> (TP-VP - 192) x 1 week
+ * ------------------------------------------------------------
+ * TP-VP | Validity period
+ * (Relative format) | value
+ * ------------------------------------------------------------
+ * 0 to 143 | (TP-VP + 1) x 5 minutes
+ * 144 to 167 | 12 hours + ((TP-VP -143) x 30 minutes)
+ * 168 to 196 | (TP-VP - 166) x 1 day
+ * 197 to 255 | (TP-VP - 192) x 1 week
+ * ------------------------------------------------------------
*
* @return relValidityPeriod Encoded Relative Validity Period Value.
* @hide
@@ -245,19 +215,16 @@ public class SmsMessage extends SmsMessageBase {
public static int getRelativeValidityPeriod(int validityPeriod) {
int relValidityPeriod = INVALID_VALIDITY_PERIOD;
- if (validityPeriod < VALIDITY_PERIOD_MIN || validityPeriod > VALIDITY_PERIOD_MAX) {
- Rlog.e(LOG_TAG,"Invalid Validity Period" + validityPeriod);
- return relValidityPeriod;
- }
-
- if (validityPeriod <= 720) {
- relValidityPeriod = (validityPeriod / 5) - 1;
- } else if (validityPeriod <= 1440) {
- relValidityPeriod = ((validityPeriod - 720) / 30) + 143;
- } else if (validityPeriod <= 43200) {
- relValidityPeriod = (validityPeriod / 1440) + 166;
- } else if (validityPeriod <= 635040) {
- relValidityPeriod = (validityPeriod / 10080) + 192;
+ if (validityPeriod >= VALIDITY_PERIOD_MIN) {
+ if (validityPeriod <= 720) {
+ relValidityPeriod = (validityPeriod / 5) - 1;
+ } else if (validityPeriod <= 1440) {
+ relValidityPeriod = ((validityPeriod - 720) / 30) + 143;
+ } else if (validityPeriod <= 43200) {
+ relValidityPeriod = (validityPeriod / 1440) + 166;
+ } else if (validityPeriod <= VALIDITY_PERIOD_MAX) {
+ relValidityPeriod = (validityPeriod / 10080) + 192;
+ }
}
return relValidityPeriod;
}
@@ -367,17 +334,19 @@ public class SmsMessage extends SmsMessageBase {
SubmitPdu ret = new SubmitPdu();
- int validityPeriodFormat = VALIDITY_PERIOD_FORMAT_NONE;
- int relativeValidityPeriod = INVALID_VALIDITY_PERIOD;
+ int relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod);
- // TP-Validity-Period-Format (TP-VPF) in 3GPP TS 23.040 V6.8.1 section 9.2.3.3
- //bit 4:3 = 10 - TP-VP field present - relative format
- if((relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod)) >= 0) {
- validityPeriodFormat = VALIDITY_PERIOD_FORMAT_RELATIVE;
+ byte mtiByte = 0x01; // SMS-SUBMIT
+
+ if (header != null) {
+ // Set TP-UDHI
+ mtiByte |= 0x40;
}
- byte mtiByte = (byte)(0x01 | (validityPeriodFormat << 0x03) |
- (header != null ? 0x40 : 0x00));
+ if (relativeValidityPeriod != INVALID_VALIDITY_PERIOD) {
+ // Set TP-Validity-Period-Format (TP-VPF)
+ mtiByte |= VALIDITY_PERIOD_FORMAT_RELATIVE << 3;
+ }
ByteArrayOutputStream bo = getSubmitPduHead(
scAddress, destinationAddress, mtiByte,
@@ -449,8 +418,8 @@ public class SmsMessage extends SmsMessageBase {
bo.write(0x08);
}
- if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) {
- // ( TP-Validity-Period - relative format)
+ // TP-Validity-Period (TP-VP)
+ if (relativeValidityPeriod != INVALID_VALIDITY_PERIOD) {
bo.write(relativeValidityPeriod);
}
@@ -887,10 +856,9 @@ public class SmsMessage extends SmsMessageBase {
}
/**
- * Parses an SC timestamp and returns a currentTimeMillis()-style
- * timestamp
+ * Parses an SC timestamp and returns a currentTimeMillis()-style timestamp, or 0 if
+ * invalid.
*/
-
long getSCTimestampMillis() {
// TP-Service-Centre-Time-Stamp
int year = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
@@ -916,16 +884,22 @@ public class SmsMessage extends SmsMessageBase {
// It's 2006. Should I really support years < 2000?
int fullYear = year >= 90 ? year + 1900 : year + 2000;
- LocalDateTime localDateTime = LocalDateTime.of(
- fullYear,
- month /* 1-12 */,
- day,
- hour,
- minute,
- second);
- long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds;
- // Convert to milliseconds.
- return epochSeconds * 1000;
+ try {
+ LocalDateTime localDateTime = LocalDateTime.of(
+ fullYear,
+ month /* 1-12 */,
+ day,
+ hour,
+ minute,
+ second);
+ long epochSeconds =
+ localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds;
+ // Convert to milliseconds.
+ return epochSeconds * 1000;
+ } catch (DateTimeException ex) {
+ Rlog.e(LOG_TAG, "Invalid timestamp", ex);
+ }
+ return 0;
}
/**
@@ -1276,6 +1250,7 @@ public class SmsMessage extends SmsMessageBase {
mRecipientAddress = p.getAddress();
// TP-Service-Centre-Time-Stamp
mScTimeMillis = p.getSCTimestampMillis();
+ // TP-Discharge-Time
p.getSCTimestampMillis();
// TP-Status
mStatus = p.getByte();
@@ -1334,6 +1309,7 @@ public class SmsMessage extends SmsMessageBase {
+ " data coding scheme: " + mDataCodingScheme);
}
+ // TP-Service-Centre-Time-Stamp
mScTimeMillis = p.getSCTimestampMillis();
if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis);
@@ -1376,23 +1352,17 @@ public class SmsMessage extends SmsMessageBase {
// TP-Validity-Period-Format
int validityPeriodLength = 0;
- int validityPeriodFormat = ((firstByte>>3) & 0x3);
- if (0x0 == validityPeriodFormat) /* 00, TP-VP field not present*/
- {
+ int validityPeriodFormat = ((firstByte >> 3) & 0x3);
+ if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_NONE) {
validityPeriodLength = 0;
- }
- else if (0x2 == validityPeriodFormat) /* 10, TP-VP: relative format*/
- {
+ } else if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) {
validityPeriodLength = 1;
- }
- else /* other case, 11 or 01, TP-VP: absolute or enhanced format*/
- {
+ } else { // VALIDITY_PERIOD_FORMAT_ENHANCED or VALIDITY_PERIOD_FORMAT_ABSOLUTE
validityPeriodLength = 7;
}
// TP-Validity-Period is not used on phone, so just ignore it for now.
- while (validityPeriodLength-- > 0)
- {
+ while (validityPeriodLength-- > 0) {
p.getByte();
}
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 69c296e7ee9c..c7c9fc739189 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -38,6 +38,7 @@ java_sdk_library {
],
compile_dex: true,
+ default_to_stubs: true,
}
// Build the android.test.base_static library
diff --git a/test-base/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java
index 6b79314a4385..9f7a2fa44dc2 100644
--- a/test-base/src/android/test/InstrumentationTestCase.java
+++ b/test-base/src/android/test/InstrumentationTestCase.java
@@ -34,9 +34,9 @@ import junit.framework.TestCase;
* A test case that has access to {@link Instrumentation}.
*
* @deprecated Use
- * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
+ * <a href="{@docRoot}reference/androidx/test/platform/app/InstrumentationRegistry.html">
* InstrumentationRegistry</a> instead. New tests should be written using the
- * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
+ * <a href="{@docRoot}training/testing/index.html">AndroidX Test Library</a>.
*/
@Deprecated
public class InstrumentationTestCase extends TestCase {
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 248c117d2e03..7d0f92fac4c7 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -37,6 +37,7 @@ java_sdk_library {
"android.test.mock",
],
compile_dex: true,
+ default_to_stubs: true,
}
// Make the current.txt available for use by the cts/tests/signature tests.
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index d1d64d39b688..a5c254f1aca2 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -33,6 +33,7 @@ import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ICancellationSignal;
@@ -336,7 +337,7 @@ public class MockContentProvider extends ContentProvider {
* @hide
*/
public IBinder getIContentProviderBinder() {
- throw new UnsupportedOperationException("unimplemented mock method");
+ return new Binder();
}
/**
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 75f5b5a96eb1..1f6db8403eee 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -41,6 +41,7 @@ java_sdk_library {
],
compile_dex: true,
+ default_to_stubs: true,
}
// Build the android.test.runner-minus-junit library
diff --git a/test-runner/api/current.txt b/test-runner/api/current.txt
index 2c19a2e85410..5407b685bb34 100644
--- a/test-runner/api/current.txt
+++ b/test-runner/api/current.txt
@@ -78,6 +78,7 @@ package android.test {
@Deprecated public class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider {
ctor @Deprecated public InstrumentationTestRunner();
+ method @Deprecated protected void addTestListener(junit.framework.TestListener);
method @Deprecated public junit.framework.TestSuite getAllTests();
method @Deprecated protected android.test.AndroidTestRunner getAndroidTestRunner();
method @Deprecated public android.os.Bundle getArguments();
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index b2582c19b548..07e3f8736cc8 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -410,7 +410,6 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
/**
* Add a {@link TestListener}
- * @hide
*/
protected void addTestListener(TestListener listener){
if(mTestRunner!=null && listener!=null){
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java
index ba2064005937..e1b508b424f5 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java
@@ -25,14 +25,30 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.perftests.am.util.Constants;
import com.android.frameworks.perftests.am.util.TargetPackageUtils;
+import com.android.frameworks.perftests.am.util.Utils;
+import org.junit.After;
import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class ServiceStartPerfTest extends BasePerfTest {
+ private static final String STUB_PACKAGE_NAME =
+ "com.android.frameworks.perftests.amteststestapp";
+
+ @Before
+ public void setUp() {
+ super.setUp();
+ Utils.runShellCommand("cmd deviceidle whitelist +" + STUB_PACKAGE_NAME);
+ }
+
+ @After
+ public void tearDown() {
+ Utils.runShellCommand("cmd deviceidle whitelist -" + STUB_PACKAGE_NAME);
+ }
/**
* Tries to start the service with the given intent, throwing a RuntimeException with the
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
index 62c1ba89653c..2d58ce8baddc 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.mk
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
@@ -36,8 +36,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := DynamicCodeLoggerNativeTestLibrary
LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp
-LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE)
+LOCAL_HEADER_LIBRARIES := jni_headers
LOCAL_SDK_VERSION := 28
LOCAL_NDK_STL_VARIANT := c++_static
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index baff952edc72..5161fba026db 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -16,7 +16,7 @@
android_test {
name: "FlickerTests",
- srcs: ["src/**/*.java"],
+ srcs: ["src/**/*.java", "src/**/*.kt"],
manifest: "AndroidManifest.xml",
test_config: "AndroidTest.xml",
platform_apis: true,
@@ -28,6 +28,7 @@ android_test {
"flickerlib",
"truth-prebuilt",
"app-helpers-core",
+ "launcher-helper-lib",
"launcher-aosp-tapl"
],
}
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
index e0bd66580ec4..98e02ced8d0b 100644
--- a/tests/FlickerTests/AndroidManifest.xml
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.wm.flicker">
- <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index d83ee3a29381..68c99a35e5d0 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -27,13 +27,13 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.wm.flicker"/>
- <option name="exclude-annotation" value="org.junit.Ignore" />
+ <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6000s" />
<option name="hidden-api-checks" value="false" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/sdcard/flicker" />
+ <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.server.wm.flicker/files" />
<option name="collect-on-run-ended-only" value="true" />
<option name="clean-up" value="true" />
</metrics_collector>
diff --git a/tests/FlickerTests/README.md b/tests/FlickerTests/README.md
index a7c9e20e0a07..6b28fdf8a8ef 100644
--- a/tests/FlickerTests/README.md
+++ b/tests/FlickerTests/README.md
@@ -1,146 +1,88 @@
# Flicker Test Library
## Motivation
-Detect *flicker* &mdash; any discontinuous, or unpredictable behavior seen during UI transitions that is not due to performance. This is often the result of a logic error in the code and difficult to identify because the issue is transient and at times difficult to reproduce. This library helps create integration tests between `SurfaceFlinger`, `WindowManager` and `SystemUI` to identify flicker.
+This set of tests use the flickerlib from `platform_testing/libraries/flicker` to execute a set of common UI transitions to detect discontinuous or unpredictable behavior.
-## Adding a Test
-The library builds and runs UI transitions, captures Winscope traces and exposes common assertions that can be tested against each trace.
-
-### Building Transitions
-Start by defining common or error prone transitions using `TransitionRunner`.
-```java
-// Example: Build a transition that cold launches an app from launcher
-TransitionRunner transition = TransitionRunner.newBuilder()
- // Specify a tag to identify the transition (optional)
- .withTag("OpenAppCold_" + testApp.getLauncherName())
-
- // Specify preconditions to setup the device
- // Wake up device and go to home screen
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
-
- // Setup transition under test
- // Press the home button and close the app to test a cold start
- .runBefore(device::pressHome)
- .runBefore(testApp::exit)
-
- // Run the transition under test
- // Open the app and wait for UI to be idle
- // This is the part of the transition that will be tested.
- .run(testApp::open)
- .run(device::waitForIdle)
-
- // Perform any tear downs
- // Close the app
- .runAfterAll(testApp::exit)
-
- // Number of times to repeat the transition to catch any flaky issues
- .repeat(5);
-```
-
-
-Run the transition to get a list of `TransitionResult` for each time the transition is repeated.
-```java
- List<TransitionResult> results = transition.run();
-```
-`TransitionResult` contains paths to test artifacts such as Winscope traces and screen recordings.
-
-
-### Checking Assertions
-Each `TransitionResult` can be tested using an extension of the Google Truth library, `LayersTraceSubject` and `WmTraceSubject`. They try to balance test principles set out by Google Truth (not supporting nested assertions, keeping assertions simple) with providing support for common assertion use cases.
-
-Each trace can be represented as a ordered collection of trace entries, with an associated timestamp. Each trace entry has common assertion checks. The trace subjects expose methods to filter the range of entries and test for changing assertions.
-
-```java
- TransitionResult result = results.get(0);
- Rect displayBounds = getDisplayBounds();
+The tests are organized in packages according to the transitions they test (e.g., `rotation`, `splitscreen`).
- // check all trace entries
- assertThat(result).coversRegion(displayBounds).forAllEntries();
-
- // check a range of entries
- assertThat(result).coversRegion(displayBounds).forRange(startTime, endTime);
-
- // check first entry
- assertThat(result).coversRegion(displayBounds).inTheBeginning();
+## Adding a Test
- // check last entry
- assertThat(result).coversRegion(displayBounds).atTheEnd();
+By default tests should inherit from `RotationTestBase` or `NonRotationTestBase` and must override the variable `transitionToRun` (Kotlin) or the function `getTransitionToRun()` (Java).
+Only tests that are not supported by these classes should inherit directly from the `FlickerTestBase` class.
- // check a change in assertions, e.g. wallpaper window is visible,
- // then wallpaper window becomes and stays invisible
- assertThat(result)
- .showsBelowAppWindow("wallpaper")
- .then()
- .hidesBelowAppWindow("wallpaper")
- .forAllEntries();
-```
+### Rotation animations and transitions
-All assertions return `Result` which contains a `success` flag, `assertionName` string identifier, and `reason` string to provide actionable details to the user. The `reason` string is build along the way with all the details as to why the assertions failed and any hints which might help the user determine the root cause. Failed assertion message will also contain a path to the trace that was tested. Example of a failed test:
+Tests that rotate the device should inherit from `RotationTestBase`.
+Tests that inherit from the class automatically receive start and end rotation values.
+Moreover, these tests inherit the following checks:
+* all regions on the screen are covered
+* status bar is always visible
+* status bar rotates
+* nav bar is always visible
+* nav bar is rotates
-```
- java.lang.AssertionError: Not true that <com.android.server.wm.flicker.LayersTrace@65da4cc>
- Layers Trace can be found in: /layers_trace_emptyregion.pb
- Timestamp: 2308008331271
- Assertion: coversRegion
- Reason: Region to test: Rect(0, 0 - 1440, 2880)
- first empty point: 0, 99
- visible regions:
- StatusBar#0Rect(0, 0 - 1440, 98)
- NavigationBar#0Rect(0, 2712 - 1440, 2880)
- ScreenDecorOverlay#0Rect(0, 0 - 1440, 91)
- ...
- at com.google.common.truth.FailureStrategy.fail(FailureStrategy.java:24)
- ...
-```
+The default tests can be disabled by overriding the respective methods and including an `@Ignore` annotation.
----
+### Non-Rotation animations and transitions
-## Running Tests
+`NonRotationTestBase` was created to make it easier to write tests that do not involve rotation (e.g., `Pip`, `split screen` or `IME`).
+Tests that inherit from the class are automatically executed twice: once in portrait and once in landscape mode and the assertions are checked independently.
+Moreover, these tests inherit the following checks:
+* all regions on the screen are covered
+* status bar is always visible
+* nav bar is always visible
-The tests can be run as any other Android JUnit tests. `platform_testing/tests/flicker` uses the library to test common UI transitions. Run `atest FlickerTest` to execute these tests.
+The default tests can be disabled by overriding the respective methods and including an `@Ignore` annotation.
----
+### Exceptional cases
-## Other Topics
-### Monitors
-Monitors capture test artifacts for each transition run. They are started before each iteration of the test transition (after the `runBefore` calls) and stopped after the transition is completed. Each iteration will produce a new test artifact. The following monitors are available:
+Tests that rotate the device should inherit from `RotationTestBase`.
+This class allows the test to be freely configured and does not provide any assertions.
-#### LayersTraceMonitor
-Captures Layers trace. This monitor is started by default. Build a transition with `skipLayersTrace()` to disable this monitor.
-#### WindowManagerTraceMonitor
-Captures Window Manager trace. This monitor is started by default. Build a transition with `skipWindowManagerTrace()` to disable this monitor.
-#### WindowAnimationFrameStatsMonitor
-Captures WindowAnimationFrameStats for the transition. This monitor is started by default and is used to eliminate *janky* runs. If an iteration has skipped frames, as determined by WindowAnimationFrameStats, the results for the iteration is skipped. If the list of results is empty after all iterations are completed, then the test should fail. Build a transition with `includeJankyRuns()` to disable this monitor.
-#### ScreenRecorder
-Captures screen to a video file. This monitor is disabled by default. Build a transition with `recordEachRun()` to capture each transition or build with `recordAllRuns()` to capture every transition including setup and teardown.
----
+### Example
-### Extending Assertions
-
-To add a new assertion, add a function to one of the trace entry classes, `LayersTrace.Entry` or `WindowManagerTrace.Entry`.
+Start by defining common or error prone transitions using `TransitionRunner`.
+```kotlin
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class MyTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : NonRotationTestBase(beginRotationName, beginRotation) {
+ init {
+ mTestApp = MyAppHelper(InstrumentationRegistry.getInstrumentation())
+ }
-```java
- // Example adds an assertion to the check if layer is hidden by parent.
- Result isHiddenByParent(String layerName) {
- // Result should contain a details if assertion fails for any reason
- // such as if layer is not found or layer is not hidden by parent
- // or layer has no parent.
- // ...
+ override val transitionToRun: TransitionRunner
+ get() = TransitionRunner.newBuilder()
+ .withTag("myTest")
+ .recordAllRuns()
+ .runBefore { device.pressHome() }
+ .runBefore { device.waitForIdle() }
+ .run { testApp.open() }
+ .runAfter{ testApp.exit() }
+ .repeat(2)
+ .includeJankyRuns()
+ .build()
+
+ @Test
+ fun myWMTest() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAppWindow(MyTestApp)
+ .forAllEntries()
+ }
}
-```
-Then add a function to the trace subject `LayersTraceSubject` or `WmTraceSubject` which will add the assertion for testing. When the assertion is evaluated, the trace will first be filtered then the assertion will be applied to the remaining entries.
-```java
- public LayersTraceSubject isHiddenByParent(String layerName) {
- mChecker.add(entry -> entry.isHiddenByParent(layerName),
- "isHiddenByParent(" + layerName + ")");
- return this;
+ @Test
+ fun mySFTest() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(MyTestApp)
+ .forAllEntries()
+ }
}
+}
```
-
-To use the new assertion:
-```java
- // Check if "Chrome" layer is hidden by parent in the first trace entry.
- assertThat(result).isHiddenByParent("Chrome").inTheBeginning();
-``` \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
deleted file mode 100644
index ed6f33e62031..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
+++ /dev/null
@@ -1,174 +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.wm.flicker;
-
-import static android.view.Surface.rotationToString;
-
-import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation;
-import static com.android.server.wm.flicker.WindowUtils.getAppPosition;
-import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition;
-import static com.android.server.wm.flicker.WindowUtils.getStatusBarPosition;
-import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
-
-import android.graphics.Rect;
-import android.util.Log;
-import android.view.Surface;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Cycle through supported app rotations.
- * To run this test: {@code atest FlickerTest:ChangeAppRotationTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class ChangeAppRotationTest extends FlickerTestBase {
- private int mBeginRotation;
- private int mEndRotation;
-
- public ChangeAppRotationTest(String beginRotationName, String endRotationName,
- int beginRotation, int endRotation) {
- this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "SimpleApp");
- this.mBeginRotation = beginRotation;
- this.mEndRotation = endRotation;
- }
-
- @Parameters(name = "{0}-{1}")
- public static Collection<Object[]> getParams() {
- int[] supportedRotations =
- {Surface.ROTATION_0, Surface.ROTATION_90};
- Collection<Object[]> params = new ArrayList<>();
- for (int begin : supportedRotations) {
- for (int end : supportedRotations) {
- if (begin != end) {
- params.add(new Object[]{rotationToString(begin), rotationToString(end), begin,
- end});
- }
- }
- }
- return params;
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return changeAppRotation(mTestApp, mUiDevice, mBeginRotation, mEndRotation)
- .includeJankyRuns().build();
- }
-
- @FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_navBarWindowIsAlwaysVisible() {
- checkResults(result -> assertThat(result)
- .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_statusBarWindowIsAlwaysVisible() {
- checkResults(result -> assertThat(result)
- .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkPosition_navBarLayerRotatesAndScales() {
- Rect startingPos = getNavigationBarPosition(mBeginRotation);
- Rect endingPos = getNavigationBarPosition(mEndRotation);
- checkResults(result -> {
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
- .inTheBeginning();
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos).atTheEnd();
- }
- );
- }
-
- @Test
- public void checkPosition_appLayerRotates() {
- Rect startingPos = getAppPosition(mBeginRotation);
- Rect endingPos = getAppPosition(mEndRotation);
- Log.e(TAG, "startingPos=" + startingPos + " endingPos=" + endingPos);
- checkResults(result -> {
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(mTestApp.getPackage(), startingPos).inTheBeginning();
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(mTestApp.getPackage(), endingPos).atTheEnd();
- }
- );
- }
-
- @Test
- public void checkPosition_statusBarLayerScales() {
- Rect startingPos = getStatusBarPosition(mBeginRotation);
- Rect endingPos = getStatusBarPosition(mEndRotation);
- checkResults(result -> {
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
- .inTheBeginning();
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos).atTheEnd();
- }
- );
- }
-
- @Ignore("Flaky. Pending debug")
- @Test
- public void checkVisibility_screenshotLayerBecomesInvisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(mTestApp.getPackage())
- .then()
- .replaceVisibleLayer(mTestApp.getPackage(), SCREENSHOT_LAYER)
- .then()
- .showsLayer(mTestApp.getPackage()).and().showsLayer(SCREENSHOT_LAYER)
- .then()
- .replaceVisibleLayer(SCREENSHOT_LAYER, mTestApp.getPackage())
- .forAllEntries());
- }
-
- @FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_navBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_statusBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java
deleted file mode 100644
index 58dcb9948c85..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java
+++ /dev/null
@@ -1,76 +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.server.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test IME window closing back to app window transitions.
- * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class CloseImeAutoOpenWindowToAppTest extends CloseImeWindowToAppTest {
-
- public CloseImeAutoOpenWindowToAppTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
-
- mTestApp = new ImeAppAutoFocusHelper(InstrumentationRegistry.getInstrumentation());
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return editTextLoseFocusToApp((ImeAppAutoFocusHelper) mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns().build();
- }
-
- @FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_imeLayerBecomesInvisible() {
- super.checkVisibility_imeLayerBecomesInvisible();
- }
-
- @FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_imeAppLayerIsAlwaysVisible() {
- super.checkVisibility_imeAppLayerIsAlwaysVisible();
- }
-
- @FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_imeAppWindowIsAlwaysVisible() {
- super.checkVisibility_imeAppWindowIsAlwaysVisible();
- }
-
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java
deleted file mode 100644
index 7f610a695e7e..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java
+++ /dev/null
@@ -1,75 +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.server.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test IME window closing back to app window transitions.
- * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class CloseImeAutoOpenWindowToHomeTest extends CloseImeWindowToHomeTest {
-
- public CloseImeAutoOpenWindowToHomeTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
-
- mTestApp = new ImeAppAutoFocusHelper(InstrumentationRegistry.getInstrumentation());
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return editTextLoseFocusToHome((ImeAppAutoFocusHelper) mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns().build();
- }
-
- @FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_imeWindowBecomesInvisible() {
- super.checkVisibility_imeWindowBecomesInvisible();
- }
-
- @FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_imeLayerBecomesInvisible() {
- super.checkVisibility_imeLayerBecomesInvisible();
- }
-
- @FlakyTest(bugId = 157449248)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_imeAppWindowBecomesInvisible() {
- super.checkVisibility_imeAppWindowBecomesInvisible();
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
deleted file mode 100644
index 57ad7e76e540..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
+++ /dev/null
@@ -1,79 +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.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-
-import com.android.server.wm.flicker.helpers.ImeAppHelper;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test IME window closing back to app window transitions.
- * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class CloseImeWindowToAppTest extends NonRotationTestBase {
-
- static final String IME_WINDOW_TITLE = "InputMethod";
-
- public CloseImeWindowToAppTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
-
- mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return editTextLoseFocusToApp((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns().build();
- }
-
- @Ignore("Flaky. Pending debug")
- @Test
- public void checkVisibility_imeLayerBecomesInvisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(IME_WINDOW_TITLE)
- .then()
- .hidesLayer(IME_WINDOW_TITLE)
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_imeAppLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(mTestApp.getPackage())
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_imeAppWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAppWindowOnTop(mTestApp.getPackage())
- .forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
deleted file mode 100644
index 12d11794c1bd..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
+++ /dev/null
@@ -1,98 +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.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import com.android.server.wm.flicker.helpers.ImeAppHelper;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test IME window closing to home transitions.
- * To run this test: {@code atest FlickerTests:CloseImeWindowToHomeTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class CloseImeWindowToHomeTest extends NonRotationTestBase {
-
- static final String IME_WINDOW_TITLE = "InputMethod";
-
- public CloseImeWindowToHomeTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
-
- mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return editTextLoseFocusToHome((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns().build();
- }
-
- @Test
- public void checkVisibility_imeWindowBecomesInvisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsImeWindow(IME_WINDOW_TITLE)
- .then()
- .hidesImeWindow(IME_WINDOW_TITLE)
- .forAllEntries());
- }
-
- @FlakyTest(bugId = 153739621)
- @Ignore
- @Test
- public void checkVisibility_imeLayerBecomesInvisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .skipUntilFirstAssertion()
- .showsLayer(IME_WINDOW_TITLE)
- .then()
- .hidesLayer(IME_WINDOW_TITLE)
- .forAllEntries());
- }
-
- @FlakyTest(bugId = 153739621)
- @Ignore
- @Test
- public void checkVisibility_imeAppLayerBecomesInvisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .skipUntilFirstAssertion()
- .showsLayer(mTestApp.getPackage())
- .then()
- .hidesLayer(mTestApp.getPackage())
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_imeAppWindowBecomesInvisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAppWindowOnTop(mTestApp.getPackage())
- .then()
- .hidesAppWindowOnTop(mTestApp.getPackage())
- .forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
deleted file mode 100644
index b1854c3294de..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
+++ /dev/null
@@ -1,399 +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.wm.flicker;
-
-import static android.os.SystemClock.sleep;
-import static android.view.Surface.rotationToString;
-
-import static com.android.server.wm.flicker.helpers.AutomationUtils.clearRecents;
-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.app.Instrumentation;
-import android.content.Context;
-import android.content.Intent;
-import android.os.RemoteException;
-import android.platform.helpers.IAppHelper;
-import android.util.Rational;
-import android.view.Surface;
-
-import androidx.annotation.Nullable;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
-import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
-import com.android.server.wm.flicker.helpers.AutomationUtils;
-import com.android.server.wm.flicker.helpers.ImeAppHelper;
-import com.android.server.wm.flicker.helpers.PipAppHelper;
-
-/**
- * Collection of common transitions which can be used to test different apps or scenarios.
- */
-class CommonTransitions {
-
- public static final int ITERATIONS = 1;
- private static final String TAG = "FLICKER";
- private static final long APP_LAUNCH_TIMEOUT = 10000;
-
- private static void setRotation(UiDevice device, int rotation) {
- try {
- switch (rotation) {
- case Surface.ROTATION_270:
- device.setOrientationLeft();
- break;
-
- case Surface.ROTATION_90:
- device.setOrientationRight();
- break;
-
- case Surface.ROTATION_0:
- default:
- device.setOrientationNatural();
- }
- // Wait for animation to complete
- sleep(1000);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param rotation Initial screen rotation
- *
- * @return test tag with pattern <NAME>__<APP>__<ROTATION>
- */
- private static String buildTestTag(String testName, IAppHelper app, int rotation) {
- return buildTestTag(
- testName, app, /* app2 */ null, rotation, rotation, /* description */ "");
- }
-
- /**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param beginRotation Initial screen rotation
- * @param endRotation End screen rotation (if any, otherwise use same as initial)
- *
- * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
- */
- private static String buildTestTag(String testName, IAppHelper app, int beginRotation,
- int endRotation) {
- return buildTestTag(
- testName, app, /* app2 */ null, beginRotation, endRotation, /* description */ "");
- }
-
- /**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param app2 Second app being launched (if any)
- * @param beginRotation Initial screen rotation
- * @param endRotation End screen rotation (if any, otherwise use same as initial)
- * @param extraInfo Additional information to append to the tag
- *
- * @return test tag with pattern <NAME>__<APP(S)>__<ROTATION(S)>[__<EXTRA>]
- */
- private static String buildTestTag(String testName, IAppHelper app, @Nullable IAppHelper app2,
- int beginRotation, int endRotation, String extraInfo) {
- StringBuilder testTag = new StringBuilder();
- testTag.append(testName)
- .append("__")
- .append(app.getLauncherName());
-
- if (app2 != null) {
- testTag.append("-")
- .append(app2.getLauncherName());
- }
-
- testTag.append("__")
- .append(rotationToString(beginRotation));
-
- if (endRotation != beginRotation) {
- testTag.append("-")
- .append(rotationToString(endRotation));
- }
-
- if (!extraInfo.isEmpty()) {
- testTag.append("__")
- .append(extraInfo);
- }
-
- return testTag.toString();
- }
-
- static TransitionBuilder openAppWarm(IAppHelper testApp, UiDevice
- device, int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("openAppWarm", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBeforeAll(() -> setRotation(device, beginRotation))
- .runBeforeAll(testApp::open)
- .runBefore(device::pressHome)
- .runBefore(device::waitForIdle)
- .runBefore(() -> setRotation(device, beginRotation))
- .run(testApp::open)
- .runAfterAll(testApp::exit)
- .runAfterAll(AutomationUtils::setDefaultWait)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder closeAppWithBackKey(IAppHelper testApp, UiDevice
- device, int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("closeAppWithBackKey", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBefore(testApp::open)
- .runBefore(device::waitForIdle)
- .run(device::pressBack)
- .run(device::waitForIdle)
- .runAfterAll(testApp::exit)
- .runAfterAll(AutomationUtils::setDefaultWait)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder closeAppWithHomeKey(IAppHelper testApp, UiDevice
- device, int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("closeAppWithHomeKey", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBefore(testApp::open)
- .runBefore(device::waitForIdle)
- .run(device::pressHome)
- .run(device::waitForIdle)
- .runAfterAll(testApp::exit)
- .runAfterAll(AutomationUtils::setDefaultWait)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder openAppCold(IAppHelper testApp,
- UiDevice device, int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("openAppCold", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBefore(device::pressHome)
- .runBeforeAll(() -> setRotation(device, beginRotation))
- .runBefore(testApp::exit)
- .runBefore(device::waitForIdle)
- .run(testApp::open)
- .runAfterAll(testApp::exit)
- .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder changeAppRotation(IAppHelper testApp, UiDevice
- device, int beginRotation, int endRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("changeAppRotation", testApp, beginRotation, endRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBeforeAll(testApp::open)
- .runBefore(() -> setRotation(device, beginRotation))
- .run(() -> setRotation(device, endRotation))
- .runAfterAll(testApp::exit)
- .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder changeAppRotation(Intent intent, String intentId, Context context,
- UiDevice device, int beginRotation, int endRotation) {
- final String testTag = "changeAppRotation_" + intentId + "_" +
- rotationToString(beginRotation) + "_" + rotationToString(endRotation);
- return TransitionRunner.newBuilder()
- .withTag(testTag)
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBeforeAll(() -> {
- context.startActivity(intent);
- device.wait(Until.hasObject(By.pkg(intent.getComponent()
- .getPackageName()).depth(0)), APP_LAUNCH_TIMEOUT);
- }
- )
- .runBefore(() -> setRotation(device, beginRotation))
- .run(() -> setRotation(device, endRotation))
- .runAfterAll(() -> stopPackage(context, intent.getComponent().getPackageName()))
- .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder appToSplitScreen(IAppHelper testApp, UiDevice device,
- int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("appToSplitScreen", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBeforeAll(() -> setRotation(device, beginRotation))
- .runBefore(testApp::open)
- .runBefore(device::waitForIdle)
- .runBefore(() -> sleep(500))
- .run(() -> launchSplitScreen(device))
- .runAfter(() -> exitSplitScreen(device))
- .runAfterAll(testApp::exit)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder splitScreenToLauncher(IAppHelper testApp, UiDevice device,
- int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("splitScreenToLauncher", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBeforeAll(() -> setRotation(device, beginRotation))
- .runBefore(testApp::open)
- .runBefore(device::waitForIdle)
- .runBefore(() -> launchSplitScreen(device))
- .run(() -> exitSplitScreen(device))
- .runAfterAll(testApp::exit)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder editTextSetFocus(ImeAppHelper testApp, UiDevice device,
- int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("editTextSetFocus", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBefore(device::pressHome)
- .runBefore(() -> setRotation(device, beginRotation))
- .runBefore(testApp::open)
- .run(() -> testApp.openIME(device))
- .runAfterAll(testApp::exit)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder resizeSplitScreen(Instrumentation instr, IAppHelper testAppTop,
- ImeAppHelper testAppBottom, UiDevice device, int beginRotation, Rational startRatio,
- Rational stopRatio) {
- String description = startRatio.toString().replace("/", "-") + "_to_"
- + stopRatio.toString().replace("/", "-");
- String testTag = buildTestTag("resizeSplitScreen", testAppTop, testAppBottom,
- beginRotation, beginRotation, description);
- return TransitionRunner.newBuilder()
- .withTag(testTag)
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBeforeAll(() -> setRotation(device, beginRotation))
- .runBeforeAll(() -> clearRecents(instr))
- .runBefore(testAppBottom::open)
- .runBefore(device::pressHome)
- .runBefore(testAppTop::open)
- .runBefore(device::waitForIdle)
- .runBefore(() -> launchSplitScreen(device))
- .runBefore(() -> {
- UiObject2 snapshot = device.findObject(
- By.res(device.getLauncherPackageName(), "snapshot"));
- snapshot.click();
- })
- .runBefore(() -> testAppBottom.openIME(device))
- .runBefore(device::pressBack)
- .runBefore(() -> AutomationUtils.resizeSplitScreen(device, startRatio))
- .run(() -> AutomationUtils.resizeSplitScreen(device, stopRatio))
- .runAfter(() -> exitSplitScreen(device))
- .runAfter(device::pressHome)
- .runAfterAll(testAppTop::exit)
- .runAfterAll(testAppBottom::exit)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder editTextLoseFocusToHome(ImeAppHelper testApp, UiDevice device,
- int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("editTextLoseFocusToHome", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBefore(device::pressHome)
- .runBefore(() -> setRotation(device, beginRotation))
- .runBefore(testApp::open)
- .runBefore(() -> testApp.openIME(device))
- .run(device::pressHome)
- .run(device::waitForIdle)
- .runAfterAll(testApp::exit)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder editTextLoseFocusToApp(ImeAppHelper testApp, UiDevice device,
- int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("editTextLoseFocusToApp", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBefore(device::pressHome)
- .runBefore(() -> setRotation(device, beginRotation))
- .runBefore(testApp::open)
- .runBefore(() -> testApp.openIME(device))
- .run(device::pressBack)
- .run(device::waitForIdle)
- .runAfterAll(testApp::exit)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder enterPipMode(PipAppHelper testApp, UiDevice device,
- int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("enterPipMode", testApp, beginRotation))
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBefore(device::pressHome)
- .runBefore(() -> setRotation(device, beginRotation))
- .runBefore(testApp::open)
- .run(() -> testApp.clickEnterPipButton(device))
- .runAfter(() -> testApp.closePipWindow(device))
- .runAfterAll(testApp::exit)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder exitPipModeToHome(PipAppHelper testApp, UiDevice device,
- int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("exitPipModeToHome", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBefore(device::pressHome)
- .runBefore(() -> setRotation(device, beginRotation))
- .runBefore(testApp::open)
- .run(() -> testApp.clickEnterPipButton(device))
- .run(() -> testApp.closePipWindow(device))
- .run(device::waitForIdle)
- .run(testApp::exit)
- .repeat(ITERATIONS);
- }
-
- static TransitionBuilder exitPipModeToApp(PipAppHelper testApp, UiDevice device,
- int beginRotation) {
- return TransitionRunner.newBuilder()
- .withTag(buildTestTag("exitPipModeToApp", testApp, beginRotation))
- .recordAllRuns()
- .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .run(device::pressHome)
- .run(() -> setRotation(device, beginRotation))
- .run(testApp::open)
- .run(() -> testApp.clickEnterPipButton(device))
- .run(() -> expandPipWindow(device))
- .run(device::waitForIdle)
- .run(testApp::exit)
- .repeat(ITERATIONS);
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt
new file mode 100644
index 000000000000..b69e6a9736a3
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker
+
+import android.app.Instrumentation
+import android.content.Context
+import android.content.Intent
+import android.os.RemoteException
+import android.os.SystemClock
+import android.platform.helpers.IAppHelper
+import android.util.Rational
+import android.view.Surface
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.AutomationUtils
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.PipAppHelper
+
+/**
+ * Collection of common transitions which can be used to test different apps or scenarios.
+ */
+internal object CommonTransitions {
+ private const val ITERATIONS = 1
+ private const val APP_LAUNCH_TIMEOUT: Long = 10000
+ private fun setRotation(device: UiDevice, rotation: Int) {
+ try {
+ when (rotation) {
+ Surface.ROTATION_270 -> device.setOrientationLeft()
+ Surface.ROTATION_90 -> device.setOrientationRight()
+ Surface.ROTATION_0 -> device.setOrientationNatural()
+ else -> device.setOrientationNatural()
+ }
+ // Wait for animation to complete
+ SystemClock.sleep(1000)
+ } catch (e: RemoteException) {
+ throw RuntimeException(e)
+ }
+ }
+
+ /**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param rotation Initial screen rotation
+ *
+ * @return test tag with pattern <NAME>__<APP>__<ROTATION>
+ </ROTATION></APP></NAME> */
+ private fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String {
+ return buildTestTag(
+ testName, app, rotation, rotation, app2 = null, extraInfo = "")
+ }
+
+ /**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param beginRotation Initial screen rotation
+ * @param endRotation End screen rotation (if any, otherwise use same as initial)
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+ </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+ private fun buildTestTag(
+ testName: String,
+ app: IAppHelper,
+ beginRotation: Int,
+ endRotation: Int
+ ): String {
+ return buildTestTag(
+ testName, app, beginRotation, endRotation, app2 = null, extraInfo = "")
+ }
+
+ /**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param app2 Second app being launched (if any)
+ * @param beginRotation Initial screen rotation
+ * @param endRotation End screen rotation (if any, otherwise use same as initial)
+ * @param extraInfo Additional information to append to the tag
+ *
+ * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
+ </EXTRA></NAME> */
+ private fun buildTestTag(
+ testName: String,
+ app: IAppHelper,
+ beginRotation: Int,
+ endRotation: Int,
+ app2: IAppHelper?,
+ extraInfo: String
+ ): String {
+ val testTag = StringBuilder()
+ testTag.append(testName)
+ .append("__")
+ .append(app.launcherName)
+ if (app2 != null) {
+ testTag.append("-")
+ .append(app2.launcherName)
+ }
+ testTag.append("__")
+ .append(Surface.rotationToString(beginRotation))
+ if (endRotation != beginRotation) {
+ testTag.append("-")
+ .append(Surface.rotationToString(endRotation))
+ }
+ if (extraInfo.isNotEmpty()) {
+ testTag.append("__")
+ .append(extraInfo)
+ }
+ return testTag.toString()
+ }
+
+ fun openAppWarm(
+ testApp: IAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("openAppWarm", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBeforeAll { setRotation(device, beginRotation) }
+ .runBeforeAll { testApp.open() }
+ .runBefore { device.pressHome() }
+ .runBefore { device.waitForIdle() }
+ .runBefore { setRotation(device, beginRotation) }
+ .run { testApp.open() }
+ .runAfterAll { testApp.exit() }
+ .runAfterAll { AutomationUtils.setDefaultWait() }
+ .repeat(ITERATIONS)
+ }
+
+ fun closeAppWithBackKey(
+ testApp: IAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("closeAppWithBackKey", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBefore { testApp.open() }
+ .runBefore { device.waitForIdle() }
+ .run { device.pressBack() }
+ .run { device.waitForIdle() }
+ .runAfterAll { testApp.exit() }
+ .runAfterAll { AutomationUtils.setDefaultWait() }
+ .repeat(ITERATIONS)
+ }
+
+ fun closeAppWithHomeKey(
+ testApp: IAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("closeAppWithHomeKey", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBefore { testApp.open() }
+ .runBefore { device.waitForIdle() }
+ .run { device.pressHome() }
+ .run { device.waitForIdle() }
+ .runAfterAll { testApp.exit() }
+ .runAfterAll { AutomationUtils.setDefaultWait() }
+ .repeat(ITERATIONS)
+ }
+
+ fun openAppCold(
+ testApp: IAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("openAppCold", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBefore { device.pressHome() }
+ .runBeforeAll { setRotation(device, beginRotation) }
+ .runBefore { testApp.exit() }
+ .runBefore { device.waitForIdle() }
+ .run { testApp.open() }
+ .runAfterAll { testApp.exit() }
+ .runAfterAll { setRotation(device, Surface.ROTATION_0) }
+ .repeat(ITERATIONS)
+ }
+
+ fun changeAppRotation(
+ testApp: IAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int,
+ endRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("changeAppRotation", testApp, beginRotation, endRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBeforeAll { testApp.open() }
+ .runBefore { setRotation(device, beginRotation) }
+ .run { setRotation(device, endRotation) }
+ .runAfterAll { testApp.exit() }
+ .runAfterAll { setRotation(device, Surface.ROTATION_0) }
+ .repeat(ITERATIONS)
+ }
+
+ fun changeAppRotation(
+ intent: Intent,
+ intentId: String,
+ context: Context,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int,
+ endRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ val testTag = "changeAppRotation_" + intentId + "_" +
+ Surface.rotationToString(beginRotation) + "_" +
+ Surface.rotationToString(endRotation)
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(testTag)
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBeforeAll {
+ context.startActivity(intent)
+ device.wait(Until.hasObject(By.pkg(intent.component?.packageName)
+ .depth(0)), APP_LAUNCH_TIMEOUT)
+ }
+ .runBefore { setRotation(device, beginRotation) }
+ .run { setRotation(device, endRotation) }
+ .runAfterAll { AutomationUtils.stopPackage(context, intent.component?.packageName) }
+ .runAfterAll { setRotation(device, Surface.ROTATION_0) }
+ .repeat(ITERATIONS)
+ }
+
+ fun appToSplitScreen(
+ testApp: IAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("appToSplitScreen", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBeforeAll { setRotation(device, beginRotation) }
+ .runBefore { testApp.open() }
+ .runBefore { device.waitForIdle() }
+ .runBefore { SystemClock.sleep(500) }
+ .run { AutomationUtils.launchSplitScreen(device) }
+ .runAfter { AutomationUtils.exitSplitScreen(device) }
+ .runAfterAll { testApp.exit() }
+ .repeat(ITERATIONS)
+ }
+
+ fun splitScreenToLauncher(
+ testApp: IAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("splitScreenToLauncher", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBefore { testApp.open() }
+ .runBefore { device.waitForIdle() }
+ .runBefore { AutomationUtils.launchSplitScreen(device) }
+ .run { AutomationUtils.exitSplitScreen(device) }
+ .runAfterAll { testApp.exit() }
+ .repeat(ITERATIONS)
+ }
+
+ fun editTextSetFocus(
+ testApp: ImeAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("editTextSetFocus", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBefore { device.pressHome() }
+ .runBefore { setRotation(device, beginRotation) }
+ .runBefore { testApp.open() }
+ .run { testApp.openIME(device) }
+ .runAfterAll { testApp.exit() }
+ .repeat(ITERATIONS)
+ }
+
+ fun resizeSplitScreen(
+ testAppTop: IAppHelper,
+ testAppBottom: ImeAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int,
+ startRatio: Rational,
+ stopRatio: Rational
+ ): TransitionRunner.TransitionBuilder {
+ val description = (startRatio.toString().replace("/", "-") + "_to_" +
+ stopRatio.toString().replace("/", "-"))
+ val testTag = buildTestTag("resizeSplitScreen", testAppTop, beginRotation,
+ beginRotation, testAppBottom, description)
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(testTag)
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBeforeAll { setRotation(device, beginRotation) }
+ .runBeforeAll { AutomationUtils.clearRecents(instrumentation) }
+ .runBefore { testAppBottom.open() }
+ .runBefore { device.pressHome() }
+ .runBefore { testAppTop.open() }
+ .runBefore { device.waitForIdle() }
+ .runBefore { AutomationUtils.launchSplitScreen(device) }
+ .runBefore {
+ val snapshot = device.findObject(
+ By.res(device.launcherPackageName, "snapshot"))
+ snapshot.click()
+ }
+ .runBefore { testAppBottom.openIME(device) }
+ .runBefore { device.pressBack() }
+ .runBefore { AutomationUtils.resizeSplitScreen(device, startRatio) }
+ .run { AutomationUtils.resizeSplitScreen(device, stopRatio) }
+ .runAfter { AutomationUtils.exitSplitScreen(device) }
+ .runAfter { device.pressHome() }
+ .runAfterAll { testAppTop.exit() }
+ .runAfterAll { testAppBottom.exit() }
+ .repeat(ITERATIONS)
+ }
+
+ fun editTextLoseFocusToHome(
+ testApp: ImeAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("editTextLoseFocusToHome", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBefore { device.pressHome() }
+ .runBefore { setRotation(device, beginRotation) }
+ .runBefore { testApp.open() }
+ .runBefore { testApp.openIME(device) }
+ .run { device.pressHome() }
+ .run { device.waitForIdle() }
+ .runAfterAll { testApp.exit() }
+ .repeat(ITERATIONS)
+ }
+
+ fun editTextLoseFocusToApp(
+ testApp: ImeAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("editTextLoseFocusToApp", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBefore { device.pressHome() }
+ .runBefore { setRotation(device, beginRotation) }
+ .runBefore { testApp.open() }
+ .runBefore { testApp.openIME(device) }
+ .run { device.pressBack() }
+ .run { device.waitForIdle() }
+ .runAfterAll { testApp.exit() }
+ .repeat(ITERATIONS)
+ }
+
+ fun enterPipMode(
+ testApp: PipAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("enterPipMode", testApp, beginRotation))
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBefore { device.pressHome() }
+ .runBefore { setRotation(device, beginRotation) }
+ .runBefore { testApp.open() }
+ .run { testApp.clickEnterPipButton(device) }
+ .runAfter { testApp.closePipWindow(device) }
+ .runAfterAll { testApp.exit() }
+ .repeat(ITERATIONS)
+ }
+
+ fun exitPipModeToHome(
+ testApp: PipAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("exitPipModeToHome", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .runBefore { device.pressHome() }
+ .runBefore { setRotation(device, beginRotation) }
+ .runBefore { testApp.open() }
+ .run { testApp.clickEnterPipButton(device) }
+ .run { testApp.closePipWindow(device) }
+ .run { device.waitForIdle() }
+ .run { testApp.exit() }
+ .repeat(ITERATIONS)
+ }
+
+ fun exitPipModeToApp(
+ testApp: PipAppHelper,
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ beginRotation: Int
+ ): TransitionRunner.TransitionBuilder {
+ return TransitionRunner.TransitionBuilder(instrumentation)
+ .withTag(buildTestTag("exitPipModeToApp", testApp, beginRotation))
+ .recordAllRuns()
+ .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
+ .run { device.pressHome() }
+ .run { setRotation(device, beginRotation) }
+ .run { testApp.open() }
+ .run { testApp.clickEnterPipButton(device) }
+ .run { AutomationUtils.expandPipWindow(device) }
+ .run { device.waitForIdle() }
+ .run { testApp.exit() }
+ .repeat(ITERATIONS)
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
deleted file mode 100644
index dec5680e17f5..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
+++ /dev/null
@@ -1,193 +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.wm.flicker;
-
-import android.app.Instrumentation;
-import android.platform.helpers.IAppHelper;
-import android.util.Rational;
-import android.view.Surface;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.UiDevice;
-
-import com.android.server.wm.flicker.helpers.ImeAppHelper;
-import com.android.server.wm.flicker.helpers.PipAppHelper;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-
-/**
- * Tests to help debug individual transitions, capture video recordings and create test cases.
- */
-@LargeTest
-@Ignore("Used for debugging transitions used in FlickerTests.")
-@RunWith(AndroidJUnit4.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class DebugTest {
- private IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "SimpleApp");
- private UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-
- /**
- * atest FlickerTests:DebugTest#openAppCold
- */
- @Test
- public void openAppCold() {
- CommonTransitions.openAppCold(testApp, uiDevice, Surface.ROTATION_0)
- .recordAllRuns().build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#openAppWarm
- */
- @Test
- public void openAppWarm() {
- CommonTransitions.openAppWarm(testApp, uiDevice, Surface.ROTATION_0)
- .recordAllRuns().build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#changeOrientationFromNaturalToLeft
- */
- @Test
- public void changeOrientationFromNaturalToLeft() {
- CommonTransitions.changeAppRotation(testApp, uiDevice, Surface.ROTATION_0,
- Surface.ROTATION_270).recordAllRuns().build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#closeAppWithBackKey
- */
- @Test
- public void closeAppWithBackKey() {
- CommonTransitions.closeAppWithBackKey(testApp, uiDevice, Surface.ROTATION_0)
- .recordAllRuns().build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#closeAppWithHomeKey
- */
- @Test
- public void closeAppWithHomeKey() {
- CommonTransitions.closeAppWithHomeKey(testApp, uiDevice, Surface.ROTATION_0)
- .recordAllRuns().build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#openAppToSplitScreen
- */
- @Test
- public void openAppToSplitScreen() {
- CommonTransitions.appToSplitScreen(testApp, uiDevice,
- Surface.ROTATION_0).includeJankyRuns().recordAllRuns()
- .build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#splitScreenToLauncher
- */
- @Test
- public void splitScreenToLauncher() {
- CommonTransitions.splitScreenToLauncher(testApp, uiDevice, Surface.ROTATION_0)
- .includeJankyRuns().recordAllRuns().build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#resizeSplitScreen
- */
- @Test
- public void resizeSplitScreen() {
- Instrumentation instr = InstrumentationRegistry.getInstrumentation();
- ImeAppHelper bottomApp = new ImeAppHelper(instr);
- CommonTransitions.resizeSplitScreen(instr, testApp, bottomApp, uiDevice, Surface.ROTATION_0,
- new Rational(1, 3), new Rational(2, 3))
- .includeJankyRuns().build().run();
- }
-
- // IME tests
-
- /**
- * atest FlickerTests:DebugTest#editTextSetFocus
- */
- @Test
- public void editTextSetFocus() {
- ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
- CommonTransitions.editTextSetFocus(testApp, uiDevice, Surface.ROTATION_0)
- .includeJankyRuns()
- .build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#editTextLoseFocusToHome
- */
- @Test
- public void editTextLoseFocusToHome() {
- ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
- CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
- .includeJankyRuns()
- .build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#editTextLoseFocusToApp
- */
- @Test
- public void editTextLoseFocusToApp() {
- ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
- CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
- .includeJankyRuns()
- .build().run();
- }
-
- // PIP tests
-
- /**
- * atest FlickerTests:DebugTest#enterPipMode
- */
- @Test
- public void enterPipMode() {
- PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
- CommonTransitions.enterPipMode(testApp, uiDevice, Surface.ROTATION_0).includeJankyRuns()
- .build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#exitPipModeToHome
- */
- @Test
- public void exitPipModeToHome() {
- PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
- CommonTransitions.exitPipModeToHome(testApp, uiDevice, Surface.ROTATION_0)
- .includeJankyRuns()
- .build().run();
- }
-
- /**
- * atest FlickerTests:DebugTest#exitPipModeToApp
- */
- @Test
- public void exitPipModeToApp() {
- PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
- CommonTransitions.exitPipModeToApp(testApp, uiDevice, Surface.ROTATION_0).includeJankyRuns()
- .build().run();
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt
new file mode 100644
index 000000000000..43cfdffb6efb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker
+
+import android.platform.helpers.IAppHelper
+import android.util.Rational
+import android.view.Surface
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import androidx.test.runner.AndroidJUnit4
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+
+/**
+ * Tests to help debug individual transitions, capture video recordings and create test cases.
+ *
+ * Not actual tests
+ */
+@LargeTest
+@FlakyTest
+@RunWith(AndroidJUnit4::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class DebugTest {
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp: IAppHelper = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ private val uiDevice = UiDevice.getInstance(instrumentation)
+
+ /**
+ * atest FlickerTests:DebugTest#openAppCold
+ */
+ @Test
+ fun openAppCold() {
+ CommonTransitions.openAppCold(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
+ .recordAllRuns().build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#openAppWarm
+ */
+ @Test
+ fun openAppWarm() {
+ CommonTransitions.openAppWarm(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
+ .recordAllRuns().build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#changeOrientationFromNaturalToLeft
+ */
+ @Test
+ fun changeOrientationFromNaturalToLeft() {
+ CommonTransitions.changeAppRotation(testApp, instrumentation, uiDevice, Surface.ROTATION_0,
+ Surface.ROTATION_270).recordAllRuns().build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#closeAppWithBackKey
+ */
+ @Test
+ fun closeAppWithBackKey() {
+ CommonTransitions.closeAppWithBackKey(testApp, instrumentation, uiDevice,
+ Surface.ROTATION_0).recordAllRuns().build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#closeAppWithHomeKey
+ */
+ @Test
+ fun closeAppWithHomeKey() {
+ CommonTransitions.closeAppWithHomeKey(testApp, instrumentation, uiDevice,
+ Surface.ROTATION_0).recordAllRuns().build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#openAppToSplitScreen
+ */
+ @Test
+ fun openAppToSplitScreen() {
+ CommonTransitions.appToSplitScreen(testApp, instrumentation, uiDevice,
+ Surface.ROTATION_0).includeJankyRuns().recordAllRuns()
+ .build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#splitScreenToLauncher
+ */
+ @Test
+ fun splitScreenToLauncher() {
+ CommonTransitions.splitScreenToLauncher(testApp, instrumentation, uiDevice,
+ Surface.ROTATION_0).includeJankyRuns().recordAllRuns().build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#resizeSplitScreen
+ */
+ @Test
+ fun resizeSplitScreen() {
+ val bottomApp = ImeAppHelper(instrumentation)
+ CommonTransitions.resizeSplitScreen(
+ testApp,
+ bottomApp,
+ instrumentation,
+ uiDevice,
+ Surface.ROTATION_0,
+ Rational(1, 3), Rational(2, 3)
+ ).includeJankyRuns().build().run()
+ }
+ // IME tests
+ /**
+ * atest FlickerTests:DebugTest#editTextSetFocus
+ */
+ @Test
+ fun editTextSetFocus() {
+ val testApp = ImeAppHelper(instrumentation)
+ CommonTransitions.editTextSetFocus(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
+ .includeJankyRuns()
+ .build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#editTextLoseFocusToHome
+ */
+ @Test
+ fun editTextLoseFocusToHome() {
+ val testApp = ImeAppHelper(instrumentation)
+ CommonTransitions.editTextLoseFocusToHome(testApp, instrumentation, uiDevice,
+ Surface.ROTATION_0).includeJankyRuns().build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#editTextLoseFocusToApp
+ */
+ @Test
+ fun editTextLoseFocusToApp() {
+ val testApp = ImeAppHelper(instrumentation)
+ CommonTransitions.editTextLoseFocusToHome(testApp, instrumentation, uiDevice,
+ Surface.ROTATION_0).includeJankyRuns().build().run()
+ }
+ // PIP tests
+ /**
+ * atest FlickerTests:DebugTest#enterPipMode
+ */
+ @Test
+ fun enterPipMode() {
+ val testApp = PipAppHelper(instrumentation)
+ CommonTransitions.enterPipMode(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
+ .includeJankyRuns().build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#exitPipModeToHome
+ */
+ @Test
+ fun exitPipModeToHome() {
+ val testApp = PipAppHelper(instrumentation)
+ CommonTransitions.exitPipModeToHome(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
+ .includeJankyRuns()
+ .build().run()
+ }
+
+ /**
+ * atest FlickerTests:DebugTest#exitPipModeToApp
+ */
+ @Test
+ fun exitPipModeToApp() {
+ val testApp = PipAppHelper(instrumentation)
+ CommonTransitions.exitPipModeToApp(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
+ .includeJankyRuns().build().run()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
deleted file mode 100644
index 6bbf684d567f..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
+++ /dev/null
@@ -1,146 +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.wm.flicker;
-
-import static com.android.server.wm.flicker.helpers.AutomationUtils.setDefaultWait;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.platform.helpers.IAppHelper;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.uiautomator.UiDevice;
-
-import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
-
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Base class of all Flicker test that performs common functions for all flicker tests:
- * <p>
- * - Caches transitions so that a transition is run once and the transition results are used by
- * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
- * multiple times.
- * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
- * - Fails tests if results are not available for any test due to jank.
- */
-public abstract class FlickerTestBase {
- public static final String TAG = "FLICKER";
- static final String SCREENSHOT_LAYER = "RotationLayer";
- static final String NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar";
- static final String STATUS_BAR_WINDOW_TITLE = "StatusBar";
- static final String DOCKED_STACK_DIVIDER = "DockedStackDivider";
- private static HashMap<String, List<TransitionResult>> transitionResults =
- new HashMap<>();
- IAppHelper mTestApp;
- UiDevice mUiDevice;
- private List<TransitionResult> mResults;
- private TransitionResult mLastResult = null;
-
- @Before
- public void setUp() {
- mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- }
-
- /**
- * Teardown any system settings and clean up test artifacts from the file system.
- *
- * Note: test artifacts for failed tests will remain on the device.
- */
- @AfterClass
- public static void teardown() {
- setDefaultWait();
- transitionResults.values().stream()
- .flatMap(List::stream)
- .forEach(result -> {
- if (result.canDelete()) {
- result.delete();
- } else {
- if (result.layersTraceExists()) {
- Log.e(TAG, "Layers trace saved to " + result.getLayersTracePath());
- }
- if (result.windowManagerTraceExists()) {
- Log.e(TAG, "WindowManager trace saved to " + result
- .getWindowManagerTracePath
- ());
- }
- if (result.screenCaptureVideoExists()) {
- Log.e(TAG, "Screen capture video saved to " + result
- .screenCaptureVideoPath().toString());
- }
- }
- });
- }
-
- /**
- * Runs a transition, returns a cached result if the transition has run before.
- */
- void run(TransitionRunner transition) {
- if (transitionResults.containsKey(transition.getTestTag())) {
- mResults = transitionResults.get(transition.getTestTag());
- return;
- }
- mResults = transition.run().getResults();
- /* Fail if we don't have any results due to jank */
- assertWithMessage("No results to test because all transition runs were invalid because "
- + "of Jank").that(mResults).isNotEmpty();
- transitionResults.put(transition.getTestTag(), mResults);
- }
-
- /**
- * Runs a transition, returns a cached result if the transition has run before.
- */
- @Before
- public void runTransition() {
- run(getTransitionToRun());
- }
-
- /**
- * Gets the transition that will be executed
- */
- abstract TransitionRunner getTransitionToRun();
-
- /**
- * Goes through a list of transition results and checks assertions on each result.
- */
- void checkResults(Consumer<TransitionResult> assertion) {
-
- for (TransitionResult result : mResults) {
- mLastResult = result;
- assertion.accept(result);
- }
- mLastResult = null;
- }
-
- /**
- * Kludge to mark a file for saving. If {@code checkResults} fails, the last result is not
- * cleared. This indicates the assertion failed for the result, so mark it for saving.
- */
- @After
- public void markArtifactsForSaving() {
- if (mLastResult != null) {
- mLastResult.flagForSaving();
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
new file mode 100644
index 000000000000..d7586d0db915
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker
+
+import android.platform.helpers.IAppHelper
+import android.util.Log
+import androidx.test.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.helpers.AutomationUtils
+import com.google.common.truth.Truth
+import org.junit.After
+import org.junit.AfterClass
+import org.junit.Before
+
+/**
+ * Base class of all Flicker test that performs common functions for all flicker tests:
+ *
+ *
+ * - Caches transitions so that a transition is run once and the transition results are used by
+ * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
+ * multiple times.
+ * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
+ * - Fails tests if results are not available for any test due to jank.
+ */
+abstract class FlickerTestBase {
+ lateinit var testApp: IAppHelper
+ open val instrumentation by lazy {
+ InstrumentationRegistry.getInstrumentation()
+ }
+ val uiDevice by lazy {
+ UiDevice.getInstance(instrumentation)
+ }
+ lateinit var tesults: List<TransitionResult>
+ private var lastResult: TransitionResult? = null
+
+ /**
+ * Runs a transition, returns a cached result if the transition has run before.
+ */
+ fun run(transition: TransitionRunner) {
+ if (transitionResults.containsKey(transition.testTag)) {
+ tesults = transitionResults[transition.testTag]
+ ?: throw IllegalStateException("Results do not contain test tag " +
+ transition.testTag)
+ return
+ }
+ tesults = transition.run().results
+ /* Fail if we don't have any results due to jank */
+ Truth.assertWithMessage("No results to test because all transition runs were invalid " +
+ "because of Jank").that(tesults).isNotEmpty()
+ transitionResults[transition.testTag] = tesults
+ }
+
+ /**
+ * Runs a transition, returns a cached result if the transition has run before.
+ */
+ @Before
+ fun runTransition() {
+ run(transitionToRun)
+ }
+
+ /**
+ * Gets the transition that will be executed
+ */
+ abstract val transitionToRun: TransitionRunner
+
+ /**
+ * Goes through a list of transition results and checks assertions on each result.
+ */
+ fun checkResults(assertion: (TransitionResult) -> Unit) {
+ for (result in tesults) {
+ lastResult = result
+ assertion(result)
+ }
+ lastResult = null
+ }
+
+ /**
+ * Kludge to mark a file for saving. If `checkResults` fails, the last result is not
+ * cleared. This indicates the assertion failed for the result, so mark it for saving.
+ */
+ @After
+ fun markArtifactsForSaving() {
+ lastResult?.flagForSaving()
+ }
+
+ companion object {
+ const val TAG = "FLICKER"
+ const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
+ const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
+ const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
+ private val transitionResults = mutableMapOf<String, List<TransitionResult>>()
+
+ /**
+ * Teardown any system settings and clean up test artifacts from the file system.
+ *
+ * Note: test artifacts for failed tests will remain on the device.
+ */
+ @AfterClass
+ @JvmStatic
+ fun teardown() {
+ AutomationUtils.setDefaultWait()
+ transitionResults.values
+ .flatten()
+ .forEach {
+ if (it.canDelete()) {
+ it.delete()
+ } else {
+ if (it.layersTraceExists()) {
+ Log.e(TAG, "Layers trace saved to ${it.layersTracePath}")
+ }
+ if (it.windowManagerTraceExists()) {
+ Log.e(TAG,
+ "WindowManager trace saved to ${it.windowManagerTracePath}")
+ }
+ if (it.screenCaptureVideoExists()) {
+ Log.e(TAG,
+ "Screen capture video saved to ${it.screenCaptureVideoPath()}")
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java
deleted file mode 100644
index 54941dc0f585..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java
+++ /dev/null
@@ -1,80 +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.server.wm.flicker;
-
-import static android.view.Surface.rotationToString;
-
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
-
-import android.graphics.Rect;
-import android.view.Surface;
-
-import androidx.test.filters.FlakyTest;
-
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-public abstract class NonRotationTestBase extends FlickerTestBase {
-
- int mBeginRotation;
-
- public NonRotationTestBase(String beginRotationName, int beginRotation) {
- this.mBeginRotation = beginRotation;
- }
-
- @Parameters(name = "{0}")
- public static Collection<Object[]> getParams() {
- int[] supportedRotations =
- {Surface.ROTATION_0, Surface.ROTATION_90};
- Collection<Object[]> params = new ArrayList<>();
-
- for (int begin : supportedRotations) {
- params.add(new Object[]{rotationToString(begin), begin});
- }
-
- return params;
- }
-
- @FlakyTest(bugId = 141361128)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkCoveredRegion_noUncoveredRegions() {
- Rect displayBounds = getDisplayBounds(mBeginRotation);
- checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
- displayBounds).forAllEntries());
- }
-
- @FlakyTest(bugId = 141361128)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_navBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @FlakyTest(bugId = 141361128)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkVisibility_statusBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
new file mode 100644
index 000000000000..1f8150c0977b
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker
+
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import org.junit.Test
+import org.junit.runners.Parameterized
+
+abstract class NonRotationTestBase(
+ beginRotationName: String,
+ protected val beginRotation: Int
+) : FlickerTestBase() {
+ @FlakyTest(bugId = 141361128)
+ @Test
+ fun checkCoveredRegion_noUncoveredRegions() {
+ val displayBounds = WindowUtils.getDisplayBounds(beginRotation)
+ checkResults {
+ LayersTraceSubject.assertThat(it).coversRegion(
+ displayBounds).forAllEntries()
+ }
+ }
+
+ @FlakyTest(bugId = 141361128)
+ @Test
+ fun checkVisibility_navBarLayerIsAlwaysVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ @FlakyTest(bugId = 141361128)
+ @Test
+ fun checkVisibility_statusBarLayerIsAlwaysVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ val params: MutableCollection<Array<Any>> = ArrayList()
+ for (begin in supportedRotations) {
+ params.add(arrayOf(Surface.rotationToString(begin), begin))
+ }
+ return params
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
deleted file mode 100644
index c150e0994316..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
+++ /dev/null
@@ -1,84 +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.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.openAppCold;
-import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test cold launch app from launcher.
- * To run this test: {@code atest FlickerTests:OpenAppColdTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class OpenAppColdTest extends NonRotationTestBase {
-
- public OpenAppColdTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
-
- this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "SimpleApp");
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return openAppCold(mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns().build();
- }
-
- @Test
- public void checkVisibility_wallpaperWindowBecomesInvisible() {
- checkResults(result -> assertThat(result)
- .showsBelowAppWindow("Wallpaper")
- .then()
- .hidesBelowAppWindow("Wallpaper")
- .forAllEntries());
- }
-
- @FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
- checkResults(result -> assertThat(result)
- .showsAppWindowOnTop(
- "com.android.launcher3/.Launcher")
- .then()
- .showsAppWindowOnTop(mTestApp.getPackage())
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_wallpaperLayerBecomesInvisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer("Wallpaper")
- .then()
- .replaceVisibleLayer("Wallpaper", mTestApp.getPackage())
- .forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
deleted file mode 100644
index 372765ded577..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
+++ /dev/null
@@ -1,86 +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.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test open app to split screen.
- * To run this test: {@code atest FlickerTests:OpenAppToSplitScreenTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 151632128)
-@Ignore("Waiting bug feedback")
-public class OpenAppToSplitScreenTest extends NonRotationTestBase {
-
- public OpenAppToSplitScreenTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
-
- this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "SimpleApp");
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return appToSplitScreen(mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns()
- .build();
- }
-
- @Test
- public void checkVisibility_navBarWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkVisibility_statusBarWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkVisibility_dividerWindowBecomesVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
- .then()
- .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_dividerLayerBecomesVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .hidesLayer(DOCKED_STACK_DIVIDER)
- .then()
- .showsLayer(DOCKED_STACK_DIVIDER)
- .forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
deleted file mode 100644
index 62ae254709b1..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
+++ /dev/null
@@ -1,84 +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.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.openAppWarm;
-import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test warm launch app.
- * To run this test: {@code atest FlickerTests:OpenAppWarmTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class OpenAppWarmTest extends NonRotationTestBase {
-
- public OpenAppWarmTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
-
- this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "SimpleApp");
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return openAppWarm(mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns().build();
- }
-
- @Test
- public void checkVisibility_wallpaperBecomesInvisible() {
- checkResults(result -> assertThat(result)
- .showsBelowAppWindow("Wallpaper")
- .then()
- .hidesBelowAppWindow("Wallpaper")
- .forAllEntries());
- }
-
- @FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
- @Test
- public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
- checkResults(result -> assertThat(result)
- .showsAppWindowOnTop(
- "com.android.launcher3/.Launcher")
- .then()
- .showsAppWindowOnTop(mTestApp.getPackage())
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_wallpaperLayerBecomesInvisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer("Wallpaper")
- .then()
- .replaceVisibleLayer("Wallpaper", mTestApp.getPackage())
- .forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
deleted file mode 100644
index fcb022c280da..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
+++ /dev/null
@@ -1,73 +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.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.editTextSetFocus;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-
-import com.android.server.wm.flicker.helpers.ImeAppHelper;
-
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test IME window opening transitions.
- * To run this test: {@code atest FlickerTests:OpenImeWindowTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class OpenImeWindowTest extends NonRotationTestBase {
-
- private static final String IME_WINDOW_TITLE = "InputMethod";
-
- public OpenImeWindowTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
-
- mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return editTextSetFocus((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns().build();
- }
-
- @Test
- public void checkVisibility_imeWindowBecomesVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .skipUntilFirstAssertion()
- .hidesImeWindow(IME_WINDOW_TITLE)
- .then()
- .showsImeWindow(IME_WINDOW_TITLE)
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_imeLayerBecomesVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .hidesLayer(IME_WINDOW_TITLE)
- .then()
- .showsLayer(IME_WINDOW_TITLE)
- .forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipTestBase.java
deleted file mode 100644
index cf51d780d7d2..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/PipTestBase.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker;
-
-import static com.android.server.wm.flicker.helpers.AutomationUtils.closePipWindow;
-import static com.android.server.wm.flicker.helpers.AutomationUtils.hasPipWindow;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.uiautomator.UiDevice;
-
-import com.android.server.wm.flicker.helpers.PipAppHelper;
-
-import org.junit.AfterClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public abstract class PipTestBase extends NonRotationTestBase {
- static final String sPipWindowTitle = "PipMenuActivity";
-
- public PipTestBase(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
-
- this.mTestApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
- }
-
- @AfterClass
- public static void teardown() {
- UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-
- if (hasPipWindow(device)) {
- closePipWindow(device);
- }
- }
-
- @Test
- public void checkVisibility_pipWindowBecomesVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .skipUntilFirstAssertion()
- .showsAppWindowOnTop(sPipWindowTitle)
- .then()
- .hidesAppWindow(sPipWindowTitle)
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_pipLayerBecomesVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .skipUntilFirstAssertion()
- .showsLayer(sPipWindowTitle)
- .then()
- .hidesLayer(sPipWindowTitle)
- .forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java
deleted file mode 100644
index 5e67ada5ff55..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java
+++ /dev/null
@@ -1,66 +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.server.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.exitPipModeToApp;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import com.android.server.wm.flicker.helpers.PipAppHelper;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test Pip launch.
- * To run this test: {@code atest FlickerTests:PipToAppTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
-@Ignore("Waiting bug feedback")
-public class PipToAppTest extends PipTestBase {
- public PipToAppTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return exitPipModeToApp((PipAppHelper) mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns().build();
- }
-
- @Test
- public void checkVisibility_backgroundWindowVisibleBehindPipLayer() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .skipUntilFirstAssertion()
- .showsAppWindowOnTop(sPipWindowTitle)
- .then()
- .showsBelowAppWindow("Wallpaper")
- .then()
- .showsAppWindowOnTop(mTestApp.getPackage())
- .then()
- .hidesAppWindowOnTop(mTestApp.getPackage())
- .forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java
deleted file mode 100644
index af713c7b39b7..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java
+++ /dev/null
@@ -1,64 +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.server.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.exitPipModeToHome;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import com.android.server.wm.flicker.helpers.PipAppHelper;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-
-/**
- * Test Pip launch.
- * To run this test: {@code atest FlickerTests:PipToHomeTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
-@Ignore("Waiting bug feedback")
-public class PipToHomeTest extends PipTestBase {
- public PipToHomeTest(String beginRotationName, int beginRotation) {
- super(beginRotationName, beginRotation);
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return exitPipModeToHome((PipAppHelper) mTestApp, mUiDevice, mBeginRotation)
- .includeJankyRuns().build();
- }
-
- @Ignore
- @Test
- public void checkVisibility_backgroundWindowVisibleBehindPipLayer() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAppWindowOnTop(sPipWindowTitle)
- .then()
- .showsBelowAppWindow("Wallpaper")
- .then()
- .showsAppWindowOnTop("Wallpaper")
- .forAllEntries());
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
deleted file mode 100644
index 9516af624f6f..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
+++ /dev/null
@@ -1,200 +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.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.resizeSplitScreen;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
-import static com.android.server.wm.flicker.WindowUtils.getDockedStackDividerInset;
-import static com.android.server.wm.flicker.WindowUtils.getNavigationBarHeight;
-import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen;
-import static com.android.server.wm.flicker.helpers.AutomationUtils.isInSplitScreen;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Instrumentation;
-import android.graphics.Rect;
-import android.util.Rational;
-import android.view.Surface;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.UiDevice;
-
-import com.android.server.wm.flicker.helpers.ImeAppHelper;
-
-import org.junit.AfterClass;
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-
-/**
- * Test split screen resizing window transitions.
- * To run this test: {@code atest FlickerTests:ResizeSplitScreenTest}
- *
- * Currently it runs only in 0 degrees because of b/156100803
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 159096424)
-@Ignore("Waiting bug feedback")
-public class ResizeSplitScreenTest extends FlickerTestBase {
-
- private static String sSimpleActivity = "SimpleActivity";
- private static String sImeActivity = "ImeActivity";
-
- public ResizeSplitScreenTest() {
- this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "SimpleApp");
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- Instrumentation instr = InstrumentationRegistry.getInstrumentation();
- ImeAppHelper bottomApp = new ImeAppHelper(instr);
- return resizeSplitScreen(instr, mTestApp, bottomApp, mUiDevice, Surface.ROTATION_0,
- new Rational(1, 3), new Rational(2, 3))
- .includeJankyRuns().build();
- }
-
- @Test
- public void checkVisibility_topAppLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(sSimpleActivity)
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_bottomAppLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(sImeActivity)
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_dividerLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(DOCKED_STACK_DIVIDER)
- .forAllEntries());
- }
-
- @Test
- @Ignore("Waiting feedback")
- public void checkPosition_appsStartingBounds() {
- Rect displayBounds = getDisplayBounds();
- checkResults(result -> {
- LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
- result.getLayersTracePath(), result.getLayersTraceChecksum());
-
- assertThat(entries.getEntries()).isNotEmpty();
- Rect startingDividerBounds = entries.getEntries().get(0).getVisibleBounds
- (DOCKED_STACK_DIVIDER);
-
- Rect startingTopAppBounds = new Rect(0, 0, startingDividerBounds.right,
- startingDividerBounds.top + getDockedStackDividerInset());
-
- Rect startingBottomAppBounds = new Rect(0,
- startingDividerBounds.bottom - getDockedStackDividerInset(),
- displayBounds.right,
- displayBounds.bottom - getNavigationBarHeight());
-
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
- .inTheBeginning();
-
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
- .inTheBeginning();
- });
- }
-
- @Test
- @Ignore("Waiting feedback")
- public void checkPosition_appsEndingBounds() {
- Rect displayBounds = getDisplayBounds();
- checkResults(result -> {
- LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
- result.getLayersTracePath(), result.getLayersTraceChecksum());
-
- assertThat(entries.getEntries()).isNotEmpty();
- Rect endingDividerBounds = entries.getEntries().get(
- entries.getEntries().size() - 1).getVisibleBounds(
- DOCKED_STACK_DIVIDER);
-
- Rect startingTopAppBounds = new Rect(0, 0, endingDividerBounds.right,
- endingDividerBounds.top + getDockedStackDividerInset());
-
- Rect startingBottomAppBounds = new Rect(0,
- endingDividerBounds.bottom - getDockedStackDividerInset(),
- displayBounds.right,
- displayBounds.bottom - getNavigationBarHeight());
-
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(sSimpleActivity, startingTopAppBounds)
- .atTheEnd();
-
- LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(sImeActivity, startingBottomAppBounds)
- .atTheEnd();
- });
- }
-
- @Test
- public void checkVisibility_navBarWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_statusBarWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
- .forAllEntries());
- }
-
- @Test
- @FlakyTest(bugId = 156223549)
- @Ignore("Waiting bug feedback")
- public void checkVisibility_topAppWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAppWindow(sSimpleActivity)
- .forAllEntries());
- }
-
- @Test
- @FlakyTest(bugId = 156223549)
- @Ignore("Waiting bug feedback")
- public void checkVisibility_bottomAppWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAppWindow(sImeActivity)
- .forAllEntries());
- }
-
- @AfterClass
- public static void teardown() {
- UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-
- if (isInSplitScreen(device)) {
- exitSplitScreen(device);
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
new file mode 100644
index 000000000000..dfc3c07d5417
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker
+
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import org.junit.Test
+import org.junit.runners.Parameterized
+
+abstract class RotationTestBase(
+ beginRotationName: String,
+ endRotationName: String,
+ protected val beginRotation: Int,
+ protected val endRotation: Int
+) : FlickerTestBase() {
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun checkVisibility_statusBarWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkPosition_navBarLayerRotatesAndScales() {
+ val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
+ val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
+ if (startingPos == endingPos) {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ .forAllEntries()
+ }
+ } else {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ .inTheBeginning()
+ }
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+ .atTheEnd()
+ }
+ }
+ }
+
+ @Test
+ fun checkPosition_statusBarLayerRotatesScales() {
+ val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
+ val endingPos = WindowUtils.getStatusBarPosition(endRotation)
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
+ .inTheBeginning()
+ LayersTraceSubject.assertThat(it)
+ .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos).atTheEnd()
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun checkVisibility_navBarLayerIsAlwaysVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun checkVisibility_statusBarLayerIsAlwaysVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ companion object {
+ const val SCREENSHOT_LAYER = "RotationLayer"
+
+ @Parameterized.Parameters(name = "{0}-{1}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ val params: MutableCollection<Array<Any>> = mutableListOf()
+ for (begin in supportedRotations) {
+ for (end in supportedRotations) {
+ if (begin != end) {
+ params.add(arrayOf(
+ Surface.rotationToString(begin),
+ Surface.rotationToString(end),
+ begin,
+ end
+ ))
+ }
+ }
+ }
+ return params
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
deleted file mode 100644
index 3cff8683eca5..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker;
-
-import static android.view.Surface.rotationToString;
-
-import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation;
-import static com.android.server.wm.flicker.WindowUtils.getAppPosition;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
-import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition;
-import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
-import static com.android.server.wm.flicker.testapp.ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME;
-
-import android.content.Intent;
-import android.graphics.Rect;
-import android.view.Surface;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Cycle through supported app rotations using seamless rotations.
- * To run this test: {@code atest FlickerTests:SeamlessAppRotationTest}
- */
-@LargeTest
-@RunWith(Parameterized.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 147659548)
-@Ignore("Waiting bug feedback")
-public class SeamlessAppRotationTest extends FlickerTestBase {
- private int mBeginRotation;
- private int mEndRotation;
- private Intent mIntent;
-
- public SeamlessAppRotationTest(String testId, Intent intent, int beginRotation,
- int endRotation) {
- this.mIntent = intent;
- this.mBeginRotation = beginRotation;
- this.mEndRotation = endRotation;
- }
-
- @Parameters(name = "{0}")
- public static Collection<Object[]> getParams() {
- int[] supportedRotations =
- {Surface.ROTATION_0, Surface.ROTATION_90};
- Collection<Object[]> params = new ArrayList<>();
-
- ArrayList<Intent> testIntents = new ArrayList<>();
-
- // launch test activity that supports seamless rotation
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setComponent(SEAMLESS_ACTIVITY_COMPONENT_NAME);
- testIntents.add(intent);
-
- // launch test activity that supports seamless rotation with a busy UI thread to miss frames
- // when the app is asked to redraw
- intent = new Intent(intent);
- intent.putExtra(EXTRA_STARVE_UI_THREAD, true);
- testIntents.add(intent);
-
- for (Intent testIntent : testIntents) {
- for (int begin : supportedRotations) {
- for (int end : supportedRotations) {
- if (begin != end) {
- String testId = rotationToString(begin) + "_" + rotationToString(end);
- if (testIntent.getExtras() != null &&
- testIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) {
- testId += "_" + "BUSY_UI_THREAD";
- }
- params.add(new Object[]{testId, testIntent, begin, end});
- }
- }
- }
- }
- return params;
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- String intentId = "";
- if (mIntent.getExtras() != null &&
- mIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) {
- intentId = "BUSY_UI_THREAD";
- }
-
- return changeAppRotation(mIntent, intentId, InstrumentationRegistry.getContext(),
- mUiDevice, mBeginRotation, mEndRotation).build();
- }
-
- @Test
- public void checkVisibility_navBarWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkPosition_navBarLayerRotatesAndScales() {
- Rect startingPos = getNavigationBarPosition(mBeginRotation);
- Rect endingPos = getNavigationBarPosition(mEndRotation);
- if (startingPos.equals(endingPos)) {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
- .forAllEntries());
- } else {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
- .inTheBeginning());
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
- .atTheEnd());
- }
- }
-
- @Test
- public void checkPosition_appLayerRotates() {
- Rect startingPos = getAppPosition(mBeginRotation);
- Rect endingPos = getAppPosition(mEndRotation);
- if (startingPos.equals(endingPos)) {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos)
- .forAllEntries());
- } else {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos)
- .then()
- .hasVisibleRegion(mIntent.getComponent().getPackageName(), endingPos)
- .forAllEntries());
- }
- }
-
- @Test
- public void checkCoveredRegion_noUncoveredRegions() {
- Rect startingBounds = getDisplayBounds(mBeginRotation);
- Rect endingBounds = getDisplayBounds(mEndRotation);
- if (startingBounds.equals(endingBounds)) {
- checkResults(result ->
- LayersTraceSubject.assertThat(result)
- .coversRegion(startingBounds)
- .forAllEntries());
- } else {
- checkResults(result ->
- LayersTraceSubject.assertThat(result)
- .coversRegion(startingBounds)
- .then()
- .coversRegion(endingBounds)
- .forAllEntries());
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
deleted file mode 100644
index bd9529de5630..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
+++ /dev/null
@@ -1,115 +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.wm.flicker;
-
-import static com.android.server.wm.flicker.CommonTransitions.splitScreenToLauncher;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
-import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen;
-import static com.android.server.wm.flicker.helpers.AutomationUtils.isInSplitScreen;
-
-import android.view.Surface;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.UiDevice;
-
-import org.junit.AfterClass;
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-
-/**
- * Test open app to split screen.
- * To run this test: {@code atest FlickerTests:SplitScreenToLauncherTest}
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 140856143)
-@Ignore("Waiting bug feedback")
-public class SplitScreenToLauncherTest extends FlickerTestBase {
-
- public SplitScreenToLauncherTest() {
- this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "SimpleApp");
- }
-
- @Override
- TransitionRunner getTransitionToRun() {
- return splitScreenToLauncher(mTestApp, mUiDevice, Surface.ROTATION_0)
- .includeJankyRuns().build();
- }
-
- @Test
- public void checkCoveredRegion_noUncoveredRegions() {
- checkResults(result ->
- LayersTraceSubject.assertThat(result)
- .coversRegion(getDisplayBounds()).forAllEntries());
- }
-
- @Test
- public void checkVisibility_dividerLayerBecomesInVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(DOCKED_STACK_DIVIDER)
- .then()
- .hidesLayer(DOCKED_STACK_DIVIDER)
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_appLayerBecomesInVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(mTestApp.getPackage())
- .then()
- .hidesLayer(mTestApp.getPackage())
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_navBarWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkVisibility_statusBarWindowIsAlwaysVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkVisibility_dividerWindowBecomesInVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
- .then()
- .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
- .forAllEntries());
- }
-
- @AfterClass
- public static void teardown() {
- UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-
- if (isInSplitScreen(device)) {
- exitSplitScreen(device);
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt
new file mode 100644
index 000000000000..e579533d2bb7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import com.android.server.wm.flicker.StandardAppHelper
+
+abstract class FlickerAppHelper(
+ instr: Instrumentation,
+ launcherName: String
+) : StandardAppHelper(instr, sFlickerPackage, launcherName) {
+ companion object {
+ var sFindTimeout = 10000
+ var sFlickerPackage = "com.android.server.wm.flicker.testapp"
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 1b2c48405f6e..d587f1e383c5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,13 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.helpers;
+package com.android.server.wm.flicker.helpers
-import android.app.Instrumentation;
+import android.app.Instrumentation
+import androidx.test.uiautomator.UiDevice
-import androidx.test.uiautomator.UiDevice;
-
-public class ImeAppAutoFocusHelper extends ImeAppHelper {
-
- public ImeAppAutoFocusHelper(Instrumentation instr) {
- super(instr, "ImeAppAutoFocus");
- }
-
- public void openIME(UiDevice device) {
+class ImeAppAutoFocusHelper(instr: Instrumentation) : ImeAppHelper(instr, "ImeAppAutoFocus") {
+ override fun openIME(device: UiDevice) {
// do nothing (the app is focused automatically)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java
deleted file mode 100644
index 095722d59fc7..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java
+++ /dev/null
@@ -1,51 +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.server.wm.flicker.helpers;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-
-import android.app.Instrumentation;
-
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
-public class ImeAppHelper extends FlickerAppHelper {
-
- ImeAppHelper(Instrumentation instr, String launcherName) {
- super(instr, launcherName);
- }
-
- public ImeAppHelper(Instrumentation instr) {
- this(instr, "ImeApp");
- }
-
- public void openIME(UiDevice device) {
- UiObject2 editText = device.wait(
- Until.findObject(By.res(getPackage(), "plain_text_input")),
- AutomationUtils.FIND_TIMEOUT);
- assertNotNull("Text field not found, this usually happens when the device was left "
- + "in an unknown state (e.g. in split screen)", editText);
- editText.click();
-
- if (!AutomationUtils.waitForIME(device)) {
- fail("IME did not appear");
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
new file mode 100644
index 000000000000..979cbeaca539
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import org.junit.Assert
+
+open class ImeAppHelper(
+ instr: Instrumentation,
+ launcherName: String = "ImeApp"
+) : FlickerAppHelper(instr, launcherName) {
+ open fun openIME(device: UiDevice) {
+ val editText = device.wait(
+ Until.findObject(By.res(getPackage(), "plain_text_input")),
+ AutomationUtils.FIND_TIMEOUT)
+ Assert.assertNotNull("Text field not found, this usually happens when the device " +
+ "was left in an unknown state (e.g. in split screen)", editText)
+ editText.click()
+ if (!AutomationUtils.waitForIME(device)) {
+ Assert.fail("IME did not appear")
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
deleted file mode 100644
index f2b7a7e351fc..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
+++ /dev/null
@@ -1,47 +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.server.wm.flicker.helpers;
-
-import static com.android.server.wm.flicker.helpers.AutomationUtils.hasPipWindow;
-
-import static org.junit.Assert.assertNotNull;
-
-import android.app.Instrumentation;
-
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-
-public class PipAppHelper extends FlickerAppHelper {
-
- public PipAppHelper(Instrumentation instr) {
- super(instr, "PipApp");
- }
-
- public void clickEnterPipButton(UiDevice device) {
- UiObject2 enterPipButton = device.findObject(By.res(getPackage(), "enter_pip"));
- assertNotNull("Pip button not found, this usually happens when the device was left "
- + "in an unknown state (e.g. in split screen)", enterPipButton);
- enterPipButton.click();
- hasPipWindow(device);
- }
-
- public void closePipWindow(UiDevice device) {
- AutomationUtils.closePipWindow(device);
- }
-
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
new file mode 100644
index 000000000000..daee810b6899
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import org.junit.Assert
+
+class PipAppHelper(instr: Instrumentation) : FlickerAppHelper(instr, "PipApp") {
+ fun clickEnterPipButton(device: UiDevice) {
+ val enterPipButton = device.findObject(By.res(getPackage(), "enter_pip"))
+ Assert.assertNotNull("Pip button not found, this usually happens when the device " +
+ "was left in an unknown state (e.g. in split screen)", enterPipButton)
+ enterPipButton.click()
+ AutomationUtils.hasPipWindow(device)
+ }
+
+ fun closePipWindow(device: UiDevice) {
+ AutomationUtils.closePipWindow(device)
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
new file mode 100644
index 000000000000..814cdcf5e114
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing back to app window transitions.
+ * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class CloseImeAutoOpenWindowToAppTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : CloseImeWindowToAppTest(beginRotationName, beginRotation) {
+ init {
+ testApp = ImeAppAutoFocusHelper(instrumentation)
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.editTextLoseFocusToApp(testApp as ImeAppAutoFocusHelper,
+ instrumentation, uiDevice, beginRotation)
+ .includeJankyRuns().build()
+
+ @FlakyTest(bugId = 141458352)
+ @Test
+ override fun checkVisibility_imeLayerBecomesInvisible() {
+ super.checkVisibility_imeLayerBecomesInvisible()
+ }
+
+ @FlakyTest(bugId = 141458352)
+ @Test
+ override fun checkVisibility_imeAppLayerIsAlwaysVisible() {
+ super.checkVisibility_imeAppLayerIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 141458352)
+ @Test
+ override fun checkVisibility_imeAppWindowIsAlwaysVisible() {
+ super.checkVisibility_imeAppWindowIsAlwaysVisible()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
new file mode 100644
index 000000000000..c2025b6da204
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing back to app window transitions.
+ * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class CloseImeAutoOpenWindowToHomeTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : CloseImeWindowToHomeTest(beginRotationName, beginRotation) {
+ init {
+ testApp = ImeAppAutoFocusHelper(instrumentation)
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.editTextLoseFocusToHome(testApp as ImeAppAutoFocusHelper,
+ instrumentation, uiDevice, beginRotation)
+ .includeJankyRuns().build()
+
+ @FlakyTest(bugId = 141458352)
+ @Test
+ override fun checkVisibility_imeWindowBecomesInvisible() {
+ super.checkVisibility_imeWindowBecomesInvisible()
+ }
+
+ @FlakyTest(bugId = 141458352)
+ @Test
+ override fun checkVisibility_imeLayerBecomesInvisible() {
+ super.checkVisibility_imeLayerBecomesInvisible()
+ }
+
+ @FlakyTest(bugId = 157449248)
+ @Test
+ override fun checkVisibility_imeAppWindowBecomesInvisible() {
+ super.checkVisibility_imeAppWindowBecomesInvisible()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
new file mode 100644
index 000000000000..b38262e9f298
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.NonRotationTestBase
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing back to app window transitions.
+ * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class CloseImeWindowToAppTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : NonRotationTestBase(beginRotationName, beginRotation) {
+ init {
+ testApp = ImeAppHelper(instrumentation)
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.editTextLoseFocusToApp(testApp as ImeAppHelper,
+ instrumentation, uiDevice, beginRotation)
+ .includeJankyRuns().build()
+
+ @FlakyTest
+ @Test
+ open fun checkVisibility_imeLayerBecomesInvisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(IME_WINDOW_TITLE)
+ .then()
+ .hidesLayer(IME_WINDOW_TITLE)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ open fun checkVisibility_imeAppLayerIsAlwaysVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ open fun checkVisibility_imeAppWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAppWindowOnTop(testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+
+ companion object {
+ const val IME_WINDOW_TITLE = "InputMethod"
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
new file mode 100644
index 000000000000..ca04babfa0f1
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.NonRotationTestBase
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing to home transitions.
+ * To run this test: `atest FlickerTests:CloseImeWindowToHomeTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class CloseImeWindowToHomeTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : NonRotationTestBase(beginRotationName, beginRotation) {
+ init {
+ testApp = ImeAppHelper(instrumentation)
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.editTextLoseFocusToHome(testApp as ImeAppHelper,
+ instrumentation, uiDevice, beginRotation)
+ .includeJankyRuns().build()
+
+ @Test
+ open fun checkVisibility_imeWindowBecomesInvisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsNonAppWindow(IME_WINDOW_TITLE)
+ .then()
+ .hidesNonAppWindow(IME_WINDOW_TITLE)
+ .forAllEntries()
+ }
+ }
+
+ @FlakyTest(bugId = 153739621)
+ @Test
+ open fun checkVisibility_imeLayerBecomesInvisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .skipUntilFirstAssertion()
+ .showsLayer(IME_WINDOW_TITLE)
+ .then()
+ .hidesLayer(IME_WINDOW_TITLE)
+ .forAllEntries()
+ }
+ }
+
+ @FlakyTest(bugId = 153739621)
+ @Test
+ fun checkVisibility_imeAppLayerBecomesInvisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .skipUntilFirstAssertion()
+ .showsLayer(testApp.getPackage())
+ .then()
+ .hidesLayer(testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ open fun checkVisibility_imeAppWindowBecomesInvisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAppWindowOnTop(testApp.getPackage())
+ .then()
+ .appWindowNotOnTop(testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+
+ companion object {
+ const val IME_WINDOW_TITLE: String = "InputMethod"
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
new file mode 100644
index 000000000000..c7731f330aeb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.NonRotationTestBase
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window opening transitions.
+ * To run this test: `atest FlickerTests:OpenImeWindowTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenImeWindowTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : NonRotationTestBase(beginRotationName, beginRotation) {
+ init {
+ testApp = ImeAppHelper(instrumentation)
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.editTextSetFocus(testApp as ImeAppHelper,
+ instrumentation, uiDevice, beginRotation)
+ .includeJankyRuns().build()
+
+ @Test
+ fun checkVisibility_imeWindowBecomesVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .skipUntilFirstAssertion()
+ .hidesNonAppWindow(IME_WINDOW_TITLE)
+ .then()
+ .showsNonAppWindow(IME_WINDOW_TITLE)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_imeLayerBecomesVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .hidesLayer(IME_WINDOW_TITLE)
+ .then()
+ .showsLayer(IME_WINDOW_TITLE)
+ .forAllEntries()
+ }
+ }
+
+ companion object {
+ private const val IME_WINDOW_TITLE = "InputMethod"
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
new file mode 100644
index 000000000000..88b885407935
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.NonRotationTestBase
+import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WmTraceSubject
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launch app from launcher.
+ * To run this test: `atest FlickerTests:OpenAppColdTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppColdTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : NonRotationTestBase(beginRotationName, beginRotation) {
+ init {
+ testApp = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.openAppCold(testApp, instrumentation, uiDevice, beginRotation)
+ .includeJankyRuns().build()
+
+ @Test
+ fun checkVisibility_wallpaperWindowBecomesInvisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsBelowAppWindow("Wallpaper")
+ .then()
+ .hidesBelowAppWindow("Wallpaper")
+ .forAllEntries()
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun checkZOrder_appWindowReplacesLauncherAsTopWindow() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAppWindowOnTop(
+ "com.android.launcher3/.Launcher")
+ .then()
+ .showsAppWindowOnTop(testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_wallpaperLayerBecomesInvisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer("Wallpaper")
+ .then()
+ .replaceVisibleLayer("Wallpaper", testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
new file mode 100644
index 000000000000..f0bc3f00338a
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.NonRotationTestBase
+import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WmTraceSubject
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test warm launch app.
+ * To run this test: `atest FlickerTests:OpenAppWarmTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppWarmTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : NonRotationTestBase(beginRotationName, beginRotation) {
+ init {
+ testApp = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.openAppWarm(testApp, instrumentation, uiDevice, beginRotation)
+ .includeJankyRuns().build()
+
+ @Test
+ fun checkVisibility_wallpaperBecomesInvisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsBelowAppWindow("Wallpaper")
+ .then()
+ .hidesBelowAppWindow("Wallpaper")
+ .forAllEntries()
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun checkZOrder_appWindowReplacesLauncherAsTopWindow() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAppWindowOnTop(
+ "com.android.launcher3/.Launcher")
+ .then()
+ .showsAppWindowOnTop(testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_wallpaperLayerBecomesInvisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer("Wallpaper")
+ .then()
+ .replaceVisibleLayer("Wallpaper", testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
new file mode 100644
index 000000000000..79321f9488ad
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.pip
+
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.LargeTest
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.NonRotationTestBase
+import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.helpers.AutomationUtils
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import org.junit.AfterClass
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+abstract class PipTestBase(
+ beginRotationName: String,
+ beginRotation: Int
+) : NonRotationTestBase(beginRotationName, beginRotation) {
+ init {
+ testApp = PipAppHelper(instrumentation)
+ }
+
+ @Test
+ fun checkVisibility_pipWindowBecomesVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .skipUntilFirstAssertion()
+ .showsAppWindowOnTop(sPipWindowTitle)
+ .then()
+ .hidesAppWindow(sPipWindowTitle)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_pipLayerBecomesVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .skipUntilFirstAssertion()
+ .showsLayer(sPipWindowTitle)
+ .then()
+ .hidesLayer(sPipWindowTitle)
+ .forAllEntries()
+ }
+ }
+
+ companion object {
+ const val sPipWindowTitle = "PipMenuActivity"
+
+ @AfterClass
+ @JvmStatic
+ fun teardown() {
+ val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ if (AutomationUtils.hasPipWindow(device)) {
+ AutomationUtils.closePipWindow(device)
+ }
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
new file mode 100644
index 000000000000..89ffb7a57b73
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.pip
+
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest FlickerTests:PipToAppTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 152738416)
+class PipToAppTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : PipTestBase(beginRotationName, beginRotation) {
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.exitPipModeToApp(testApp as PipAppHelper, instrumentation,
+ uiDevice, beginRotation)
+ .includeJankyRuns().build()
+
+ @Test
+ fun checkVisibility_backgroundWindowVisibleBehindPipLayer() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .skipUntilFirstAssertion()
+ .showsAppWindowOnTop(sPipWindowTitle)
+ .then()
+ .showsBelowAppWindow("Wallpaper")
+ .then()
+ .showsAppWindowOnTop(testApp.getPackage())
+ .then()
+ .appWindowNotOnTop(testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
new file mode 100644
index 000000000000..8591360cff60
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.pip
+
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest FlickerTests:PipToHomeTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 152738416)
+class PipToHomeTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : PipTestBase(beginRotationName, beginRotation) {
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.exitPipModeToHome(testApp as PipAppHelper, instrumentation,
+ uiDevice, beginRotation)
+ .includeJankyRuns().build()
+
+ @Test
+ fun checkVisibility_backgroundWindowVisibleBehindPipLayer() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAppWindowOnTop(sPipWindowTitle)
+ .then()
+ .showsBelowAppWindow("Wallpaper")
+ .then()
+ .showsAppWindowOnTop("Wallpaper")
+ .forAllEntries()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
new file mode 100644
index 000000000000..fb1cb399e8bc
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.rotation
+
+import android.util.Log
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.RotationTestBase
+import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WindowUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Cycle through supported app rotations.
+ * To run this test: `atest FlickerTest:ChangeAppRotationTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class ChangeAppRotationTest(
+ beginRotationName: String,
+ endRotationName: String,
+ beginRotation: Int,
+ endRotation: Int
+) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
+ init {
+ testApp = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.changeAppRotation(testApp, instrumentation, uiDevice,
+ beginRotation, endRotation)
+ .includeJankyRuns().build()
+
+ @Test
+ fun checkPosition_appLayerRotates() {
+ val startingPos = WindowUtils.getAppPosition(beginRotation)
+ val endingPos = WindowUtils.getAppPosition(endRotation)
+ Log.e(TAG, "startingPos=$startingPos endingPos=$endingPos")
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .hasVisibleRegion(testApp.getPackage(), startingPos).inTheBeginning()
+ LayersTraceSubject.assertThat(it)
+ .hasVisibleRegion(testApp.getPackage(), endingPos).atTheEnd()
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun checkVisibility_screenshotLayerBecomesInvisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(testApp.getPackage())
+ .then()
+ .replaceVisibleLayer(testApp.getPackage(), SCREENSHOT_LAYER)
+ .then()
+ .showsLayer(testApp.getPackage()).and().showsLayer(SCREENSHOT_LAYER)
+ .then()
+ .replaceVisibleLayer(SCREENSHOT_LAYER, testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
new file mode 100644
index 000000000000..1cd19983f3b1
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.rotation
+
+import android.content.Intent
+import android.view.Surface
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.RotationTestBase
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WindowUtils
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Cycle through supported app rotations using seamless rotations.
+ * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 147659548)
+class SeamlessAppRotationTest(
+ private val intent: Intent,
+ beginRotationName: String,
+ endRotationName: String,
+ beginRotation: Int,
+ endRotation: Int
+) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
+ override val transitionToRun: TransitionRunner
+ get() {
+ var intentId = ""
+ if (intent.extras?.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
+ intentId = "BUSY_UI_THREAD"
+ }
+ return CommonTransitions.changeAppRotation(intent, intentId,
+ InstrumentationRegistry.getContext(), instrumentation, uiDevice,
+ beginRotation, endRotation).build()
+ }
+
+ @Test
+ fun checkPosition_appLayerRotates() {
+ val startingPos = WindowUtils.getAppPosition(beginRotation)
+ val endingPos = WindowUtils.getAppPosition(endRotation)
+ if (startingPos == endingPos) {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .hasVisibleRegion(intent.component?.packageName ?: "", startingPos)
+ .forAllEntries()
+ }
+ } else {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .hasVisibleRegion(intent.component?.packageName ?: "", startingPos)
+ .then()
+ .hasVisibleRegion(intent.component?.packageName ?: "", endingPos)
+ .forAllEntries()
+ }
+ }
+ }
+
+ @Test
+ fun checkCoveredRegion_noUncoveredRegions() {
+ val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
+ val endingBounds = WindowUtils.getDisplayBounds(endRotation)
+ if (startingBounds == endingBounds) {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .coversRegion(startingBounds)
+ .forAllEntries()
+ }
+ } else {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .coversRegion(startingBounds)
+ .then()
+ .coversRegion(endingBounds)
+ .forAllEntries()
+ }
+ }
+ }
+
+ companion object {
+ // launch test activity that supports seamless rotation
+
+ // launch test activity that supports seamless rotation with a busy UI thread to miss frames
+ // when the app is asked to redraw
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ val params: MutableCollection<Array<Any>> = ArrayList()
+ val testIntents = ArrayList<Intent>()
+
+ // launch test activity that supports seamless rotation
+ var intent = Intent(Intent.ACTION_MAIN)
+ intent.component = ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME
+ testIntents.add(intent)
+
+ // launch test activity that supports seamless rotation with a busy UI thread to miss frames
+ // when the app is asked to redraw
+ intent = Intent(intent)
+ intent.putExtra(ActivityOptions.EXTRA_STARVE_UI_THREAD, true)
+ testIntents.add(intent)
+ for (testIntent in testIntents) {
+ for (begin in supportedRotations) {
+ for (end in supportedRotations) {
+ if (begin != end) {
+ var testId: String = Surface.rotationToString(begin) +
+ "_" + Surface.rotationToString(end)
+ if (testIntent.extras?.getBoolean(
+ ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
+ testId += "_" + "BUSY_UI_THREAD"
+ }
+ params.add(arrayOf(testId, testIntent, begin, end))
+ }
+ }
+ }
+ }
+ return params
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
new file mode 100644
index 000000000000..b5611a45a2e7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.splitscreen
+
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.NonRotationTestBase
+import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WmTraceSubject
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest FlickerTests:OpenAppToSplitScreenTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppToSplitScreenTest(
+ beginRotationName: String,
+ beginRotation: Int
+) : NonRotationTestBase(beginRotationName, beginRotation) {
+ init {
+ testApp = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.appToSplitScreen(testApp, instrumentation, uiDevice,
+ beginRotation).includeJankyRuns().build()
+
+ @Test
+ fun checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_statusBarWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_dividerLayerBecomesVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .hidesLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ .forAllEntries()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
new file mode 100644
index 000000000000..6b597e5807ea
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.splitscreen
+
+import android.graphics.Region
+import android.util.Rational
+import android.view.Surface
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import androidx.test.runner.AndroidJUnit4
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.FlickerTestBase
+import com.android.server.wm.flicker.LayersTrace
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.TransitionResult
+import com.android.server.wm.flicker.WindowUtils
+import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.helpers.AutomationUtils
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.google.common.truth.Truth
+import org.junit.AfterClass
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+
+/**
+ * Test split screen resizing window transitions.
+ * To run this test: `atest FlickerTests:ResizeSplitScreenTest`
+ *
+ * Currently it runs only in 0 degrees because of b/156100803
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 159096424)
+class ResizeSplitScreenTest : FlickerTestBase() {
+ init {
+ testApp = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() {
+ val bottomApp = ImeAppHelper(instrumentation)
+ return CommonTransitions.resizeSplitScreen(testApp, bottomApp, instrumentation,
+ uiDevice, Surface.ROTATION_0,
+ Rational(1, 3), Rational(2, 3))
+ .includeJankyRuns().build()
+ }
+
+ @Test
+ fun checkVisibility_topAppLayerIsAlwaysVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(sSimpleActivity)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_bottomAppLayerIsAlwaysVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(sImeActivity)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_dividerLayerIsAlwaysVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ @FlakyTest
+ fun checkPosition_appsStartingBounds() {
+ val displayBounds = WindowUtils.getDisplayBounds()
+ checkResults { result: TransitionResult ->
+ val entries = LayersTrace.parseFrom(result.layersTrace,
+ result.layersTracePath, result.layersTraceChecksum)
+ Truth.assertThat(entries.entries).isNotEmpty()
+ val startingDividerBounds = entries.entries[0].getVisibleBounds(
+ DOCKED_STACK_DIVIDER).bounds
+ val startingTopAppBounds = Region(0, 0, startingDividerBounds.right,
+ startingDividerBounds.top + WindowUtils.getDockedStackDividerInset())
+ val startingBottomAppBounds = Region(0,
+ startingDividerBounds.bottom - WindowUtils.getDockedStackDividerInset(),
+ displayBounds.right,
+ displayBounds.bottom - WindowUtils.getNavigationBarHeight())
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
+ .inTheBeginning()
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
+ .inTheBeginning()
+ }
+ }
+
+ @Test
+ @FlakyTest
+ fun checkPosition_appsEndingBounds() {
+ val displayBounds = WindowUtils.getDisplayBounds()
+ checkResults { result: TransitionResult ->
+ val entries = LayersTrace.parseFrom(result.layersTrace,
+ result.layersTracePath, result.layersTraceChecksum)
+ Truth.assertThat(entries.entries).isNotEmpty()
+ val endingDividerBounds = entries.entries[entries.entries.size - 1].getVisibleBounds(
+ DOCKED_STACK_DIVIDER).bounds
+ val startingTopAppBounds = Region(0, 0, endingDividerBounds.right,
+ endingDividerBounds.top + WindowUtils.getDockedStackDividerInset())
+ val startingBottomAppBounds = Region(0,
+ endingDividerBounds.bottom - WindowUtils.getDockedStackDividerInset(),
+ displayBounds.right,
+ displayBounds.bottom - WindowUtils.getNavigationBarHeight())
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(sSimpleActivity, startingTopAppBounds)
+ .atTheEnd()
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(sImeActivity, startingBottomAppBounds)
+ .atTheEnd()
+ }
+ }
+
+ @Test
+ fun checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_statusBarWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ @FlakyTest(bugId = 156223549)
+ fun checkVisibility_topAppWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAppWindow(sSimpleActivity)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ @FlakyTest(bugId = 156223549)
+ fun checkVisibility_bottomAppWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAppWindow(sImeActivity)
+ .forAllEntries()
+ }
+ }
+
+ companion object {
+ private const val sSimpleActivity = "SimpleActivity"
+ private const val sImeActivity = "ImeActivity"
+
+ @AfterClass
+ @JvmStatic
+ fun teardown() {
+ val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ if (AutomationUtils.isInSplitScreen(device)) {
+ AutomationUtils.exitSplitScreen(device)
+ }
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
new file mode 100644
index 000000000000..fdcafdb12a78
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.splitscreen
+
+import android.view.Surface
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.LargeTest
+import androidx.test.runner.AndroidJUnit4
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.CommonTransitions
+import com.android.server.wm.flicker.FlickerTestBase
+import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.WindowUtils
+import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.helpers.AutomationUtils
+import org.junit.AfterClass
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest FlickerTests:SplitScreenToLauncherTest`
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SplitScreenToLauncherTest : FlickerTestBase() {
+ init {
+ testApp = StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ }
+
+ override val transitionToRun: TransitionRunner
+ get() = CommonTransitions.splitScreenToLauncher(testApp, instrumentation, uiDevice,
+ Surface.ROTATION_0).includeJankyRuns().build()
+
+ @Test
+ fun checkCoveredRegion_noUncoveredRegions() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .coversRegion(WindowUtils.getDisplayBounds()).forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_dividerLayerBecomesInVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .hidesLayer(DOCKED_STACK_DIVIDER)
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_appLayerBecomesInVisible() {
+ checkResults {
+ LayersTraceSubject.assertThat(it)
+ .showsLayer(testApp.getPackage())
+ .then()
+ .hidesLayer(testApp.getPackage())
+ .forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ @Test
+ fun checkVisibility_statusBarWindowIsAlwaysVisible() {
+ checkResults {
+ WmTraceSubject.assertThat(it)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries()
+ }
+ }
+
+ companion object {
+ @AfterClass
+ @JvmStatic
+ fun teardown() {
+ val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ if (AutomationUtils.isInSplitScreen(device)) {
+ AutomationUtils.exitSplitScreen(device)
+ }
+ }
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 7770c73d7a73..4d2144061ad4 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -17,8 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.wm.flicker.testapp">
- <uses-sdk android:minSdkVersion="17"
- android:targetSdkVersion="27"/>
+ <uses-sdk android:minSdkVersion="29"
+ android:targetSdkVersion="29"/>
<application android:allowBackup="false"
android:supportsRtl="true">
<activity android:name=".SimpleActivity"
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
index e9bac717daa1..45d3dade82b6 100644
--- a/tests/Internal/src/android/app/WallpaperColorsTest.java
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -47,7 +47,7 @@ public class WallpaperColorsTest {
}
/**
- * Sanity check to guarantee that white supports dark text and black doesn't
+ * Check that white supports dark text and black doesn't
*/
@Test
public void colorHintsTest() {
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 2be4ae6bb214..4f5a30502c91 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -29,7 +29,12 @@ java_test_host {
name: "StagedRollbackTest",
srcs: ["StagedRollbackTest/src/**/*.java"],
libs: ["tradefed"],
- static_libs: ["testng", "compatibility-tradefed", "RollbackTestLib"],
+ static_libs: [
+ "compatibility-tradefed",
+ "frameworks-base-hostutils",
+ "RollbackTestLib",
+ "testng",
+ ],
test_suites: ["general-tests"],
test_config: "StagedRollbackTest.xml",
data: [":com.android.apex.apkrollback.test_v1"],
@@ -39,7 +44,7 @@ java_test_host {
name: "NetworkStagedRollbackTest",
srcs: ["NetworkStagedRollbackTest/src/**/*.java"],
libs: ["tradefed"],
- static_libs: ["RollbackTestLib"],
+ static_libs: ["RollbackTestLib", "frameworks-base-hostutils"],
test_suites: ["general-tests"],
test_config: "NetworkStagedRollbackTest.xml",
}
@@ -48,6 +53,9 @@ java_test_host {
name: "MultiUserRollbackTest",
srcs: ["MultiUserRollbackTest/src/**/*.java"],
libs: ["tradefed"],
+ static_libs: [
+ "frameworks-base-hostutils",
+ ],
test_suites: ["general-tests"],
test_config: "MultiUserRollbackTest.xml",
}
diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
index 42b886f0774f..741745560c61 100644
--- a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
+++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
@@ -24,6 +24,7 @@ import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,6 +41,9 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test {
private static final long SWITCH_USER_COMPLETED_NUMBER_OF_POLLS = 60;
private static final long SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS = 1000;
+ @Rule
+ public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
+
@After
public void tearDown() throws Exception {
removeSecondaryUserIfNecessary();
@@ -59,6 +63,37 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test {
runPhaseForUsers("testBasic", mSecondaryUserId);
}
+ /**
+ * Tests staged install/rollback works correctly on the 2nd user.
+ */
+ @Test
+ public void testStagedRollback() throws Exception {
+ runPhaseForUsers("testStagedRollback_Phase1", mSecondaryUserId);
+ getDevice().reboot();
+
+ // Need to unlock the user for device tests to run successfully
+ getDevice().startUser(mSecondaryUserId);
+ awaitUserUnlocked(mSecondaryUserId);
+ runPhaseForUsers("testStagedRollback_Phase2", mSecondaryUserId);
+ getDevice().reboot();
+
+ getDevice().startUser(mSecondaryUserId);
+ awaitUserUnlocked(mSecondaryUserId);
+ runPhaseForUsers("testStagedRollback_Phase3", mSecondaryUserId);
+ getDevice().reboot();
+
+ getDevice().startUser(mSecondaryUserId);
+ awaitUserUnlocked(mSecondaryUserId);
+ runPhaseForUsers("testStagedRollback_Phase4", mSecondaryUserId);
+ }
+
+ @Test
+ public void testBadUpdateRollback() throws Exception {
+ // Need to switch user in order to send broadcasts in device tests
+ assertTrue(getDevice().switchUser(mSecondaryUserId));
+ runPhaseForUsers("testBadUpdateRollback", mSecondaryUserId);
+ }
+
@Test
public void testMultipleUsers() throws Exception {
runPhaseForUsers("testMultipleUsersInstallV1", mOriginalUserId, mSecondaryUserId);
@@ -83,6 +118,8 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test {
private void removeSecondaryUserIfNecessary() throws Exception {
if (mSecondaryUserId != -1) {
+ // Can't remove the 2nd user without switching out of it
+ assertTrue(getDevice().switchUser(mOriginalUserId));
getDevice().removeUser(mSecondaryUserId);
mSecondaryUserId = -1;
}
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
index e3c7cfaf3bef..b7d72dbe22ae 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -27,6 +27,7 @@ import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,6 +59,9 @@ public class NetworkStagedRollbackTest extends BaseHostJUnit4Test {
private WatchdogEventLogger mLogger = new WatchdogEventLogger();
+ @Rule
+ public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
+
@Before
public void setUp() throws Exception {
runPhase("cleanUp");
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java
index 8641f4d4013a..d37dd7b9ceab 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java
@@ -70,6 +70,11 @@ public class MultiUserRollbackTest {
new RollbackTest().testBasic();
}
+ @Test
+ public void testBadUpdateRollback() throws Exception {
+ new RollbackTest().testBadUpdateRollback();
+ }
+
/**
* Install version 1 of the test app. This method is run for both users.
*/
@@ -115,4 +120,32 @@ public class MultiUserRollbackTest {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
InstallUtils.processUserData(TestApp.A);
}
+
+ @Test
+ public void testStagedRollback_Phase1() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ Install.single(TestApp.A1).setStaged().commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ }
+
+ @Test
+ public void testStagedRollback_Phase2() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ Install.single(TestApp.A2).setStaged().setEnableRollback().commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ }
+
+ @Test
+ public void testStagedRollback_Phase3() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ RollbackInfo rollback = RollbackUtils.waitForAvailableRollback(TestApp.A);
+ assertThat(rollback).packagesContainsExactly(Rollback.from(TestApp.A2).to(TestApp.A1));
+ RollbackUtils.rollback(rollback.getRollbackId());
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ }
+
+ @Test
+ public void testStagedRollback_Phase4() {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ }
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
index 42b0c608822e..314e95229d29 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
@@ -61,11 +61,12 @@ public class NetworkStagedRollbackTest {
private static final TestApp NETWORK_STACK = new TestApp("NetworkStack",
getNetworkStackPackageName(), -1, false, findNetworkStackApk());
- private static File findNetworkStackApk() {
+ private static File[] findNetworkStackApk() {
for (String name : NETWORK_STACK_APK_NAMES) {
final File apk = new File("/system/priv-app/" + name + "/" + name + ".apk");
if (apk.isFile()) {
- return apk;
+ final File dir = new File("/system/priv-app/" + name);
+ return dir.listFiles((d, f) -> f.startsWith(name));
}
}
throw new RuntimeException("Can't find NetworkStackApk");
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 8104a3ff47bf..c2fd0c39221e 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -34,6 +34,7 @@ import com.android.tradefed.util.CommandStatus;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -87,6 +88,9 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
private WatchdogEventLogger mLogger = new WatchdogEventLogger();
+ @Rule
+ public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
+
@Before
public void setUp() throws Exception {
deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
@@ -449,6 +453,32 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
after.forEach(dir -> assertDirectoryIsEmpty(dir));
}
+ @Test
+ public void testExpireApexRollback() throws Exception {
+ List<String> before = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
+ pushTestApex();
+
+ // Push files to apex data directory
+ String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
+ String oldFilePath2 =
+ apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
+ assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
+ assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+
+ // Install new version of the APEX with rollback enabled
+ runPhase("testRollbackApexDataDirectories_Phase1");
+ getDevice().reboot();
+
+ List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
+ // Only check directories newly created during the test
+ after.removeAll(before);
+ // Expire all rollbacks and check CE snapshot directories are deleted
+ runPhase("testCleanUp");
+ for (String dir : after) {
+ assertNull(getDevice().getFileEntry(dir));
+ }
+ }
+
private void pushTestApex() throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index c3fdd695c2b7..da6018e2e2c9 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -24,7 +24,16 @@ java_test_host {
name: "StagedInstallInternalTest",
srcs: ["src/**/*.java"],
libs: ["tradefed"],
- static_libs: ["testng", "compatibility-tradefed"],
+ static_libs: [
+ "testng",
+ "compatibility-tradefed",
+ "frameworks-base-hostutils",
+ "module_test_util",
+ ],
+ data: [
+ ":com.android.apex.cts.shim.v2_prebuilt",
+ ":TestAppAv1",
+ ],
test_suites: ["general-tests"],
test_config: "StagedInstallInternalTest.xml",
}
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 9b432f7d0ca5..7cfbdc2b5062 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -19,23 +19,36 @@ package com.android.tests.stagedinstallinternal.host;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import com.android.ddmlib.Log;
+import com.android.tests.rollback.host.AbandonSessionsRule;
+import com.android.tests.util.ModuleTestUtils;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.ProcessInfo;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
+
@RunWith(DeviceJUnit4ClassRunner.class)
public class StagedInstallInternalTest extends BaseHostJUnit4Test {
private static final String TAG = StagedInstallInternalTest.class.getSimpleName();
private static final long SYSTEM_SERVER_TIMEOUT_MS = 60 * 1000;
- private boolean mWasRoot = false;
+
+ @Rule
+ public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
+ private static final String SHIM_V2 = "com.android.apex.cts.shim.v2.apex";
+ private static final String APK_A = "TestAppAv1.apk";
+
+ private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this);
/**
* Runs the given phase of a test by calling into the device.
@@ -62,21 +75,11 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
@Before
public void setUp() throws Exception {
- mWasRoot = getDevice().isAdbRoot();
- if (!mWasRoot) {
- getDevice().enableAdbRoot();
- }
cleanUp();
- // Abandon all staged sessions
- getDevice().executeShellCommand("pm install-abandon $(pm get-stagedsessions --only-ready "
- + "--only-parent --only-sessionid)");
}
@After
public void tearDown() throws Exception {
- if (!mWasRoot) {
- getDevice().disableAdbRoot();
- }
cleanUp();
}
@@ -87,25 +90,78 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Verify");
}
+ @Test
+ public void testAdbStagedInstallWaitForReadyFlagWorks() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mTestUtils.isApexUpdateSupported());
+
+ File apexFile = mTestUtils.getTestFile(SHIM_V2);
+ String output = getDevice().executeAdbCommand("install", "--staged",
+ "--wait-for-staged-ready", "60000", apexFile.getAbsolutePath());
+ assertThat(output).contains("Reboot device to apply staged session");
+ String sessionId = getDevice().executeShellCommand(
+ "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim();
+ assertThat(sessionId).isNotEmpty();
+ }
+
+ @Test
+ public void testAdbStagedInstallNoWaitFlagWorks() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mTestUtils.isApexUpdateSupported());
+
+ File apexFile = mTestUtils.getTestFile(SHIM_V2);
+ String output = getDevice().executeAdbCommand("install", "--staged",
+ "--no-wait", apexFile.getAbsolutePath());
+ assertThat(output).doesNotContain("Reboot device to apply staged session");
+ assertThat(output).contains("Success");
+ String sessionId = getDevice().executeShellCommand(
+ "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim();
+ assertThat(sessionId).isEmpty();
+ }
+
+ @Test
+ public void testAdbInstallMultiPackageCommandWorks() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mTestUtils.isApexUpdateSupported());
+
+ File apexFile = mTestUtils.getTestFile(SHIM_V2);
+ File apkFile = mTestUtils.getTestFile(APK_A);
+ String output = getDevice().executeAdbCommand("install-multi-package",
+ apexFile.getAbsolutePath(), apkFile.getAbsolutePath());
+ assertThat(output).contains("Created parent session");
+ assertThat(output).contains("Created child session");
+ assertThat(output).contains("Success. Reboot device to apply staged session");
+
+ // Ensure there is only one parent session
+ String[] sessionIds = getDevice().executeShellCommand(
+ "pm get-stagedsessions --only-ready --only-parent --only-sessionid").split("\n");
+ assertThat(sessionIds.length).isEqualTo(1);
+ // Ensure there are two children session
+ sessionIds = getDevice().executeShellCommand(
+ "pm get-stagedsessions --only-ready --only-sessionid").split("\n");
+ assertThat(sessionIds.length).isEqualTo(3);
+ }
+
private void restartSystemServer() throws Exception {
// Restart the system server
- long oldStartTime = getDevice().getProcessByName("system_server").getStartTime();
+ ProcessInfo oldPs = getDevice().getProcessByName("system_server");
+
+ getDevice().enableAdbRoot(); // Need root to restart system server
assertThat(getDevice().executeShellCommand("am restart")).contains("Restart the system");
+ getDevice().disableAdbRoot();
// Wait for new system server process to start
long start = System.currentTimeMillis();
- long newStartTime = oldStartTime;
while (System.currentTimeMillis() < start + SYSTEM_SERVER_TIMEOUT_MS) {
ProcessInfo newPs = getDevice().getProcessByName("system_server");
if (newPs != null) {
- newStartTime = newPs.getStartTime();
- if (newStartTime != oldStartTime) {
- break;
+ if (newPs.getPid() != oldPs.getPid()) {
+ getDevice().waitForDeviceAvailable();
+ return;
}
}
Thread.sleep(500);
}
- assertThat(newStartTime).isNotEqualTo(oldStartTime);
- getDevice().waitForDeviceAvailable();
+ fail("Timed out in restarting system server");
}
}
diff --git a/tests/WindowInsetsTests/AndroidManifest.xml b/tests/WindowInsetsTests/AndroidManifest.xml
index 597805451b95..61dd9d4cd021 100644
--- a/tests/WindowInsetsTests/AndroidManifest.xml
+++ b/tests/WindowInsetsTests/AndroidManifest.xml
@@ -16,18 +16,24 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.test.windowinsetstests">
-
- <application android:label="@string/activity_title">
- <activity android:name=".WindowInsetsActivity"
- android:theme="@style/appTheme"
- android:windowSoftInputMode="adjustResize"
- android:exported="true">
+ package="com.google.android.test.windowinsetstests">
+ <application android:label="@string/application_title">
+ <activity android:name=".WindowInsetsTestsMainActivity"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity android:name=".ChatActivity"
+ android:label="@string/chat_activity_title"
+ android:theme="@style/chat"
+ android:windowSoftInputMode="adjustResize" />
+
+ <activity android:name=".ControllerActivity"
+ android:label="@string/controller_activity_title"
+ android:theme="@style/controller" />
</application>
</manifest>
diff --git a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml b/tests/WindowInsetsTests/res/layout/chat_activity.xml
index 1b51c4f83fe0..1b51c4f83fe0 100644
--- a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml
+++ b/tests/WindowInsetsTests/res/layout/chat_activity.xml
diff --git a/tests/WindowInsetsTests/res/layout/controller_activity.xml b/tests/WindowInsetsTests/res/layout/controller_activity.xml
new file mode 100644
index 000000000000..d51a4ddd43e8
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/controller_activity.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Spinner
+ android:id="@+id/spinnerBehavior"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonStatus"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="Status Bars Toggle Button"
+ android:textOff="Status Bars Invisible"
+ android:textOn="Status Bars Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarStatus"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="10000" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonNavigation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="Navigation Bars Toggle Button"
+ android:textOff="Navigation Bars Invisible"
+ android:textOn="Navigation Bars Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarNavigation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="10000" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonIme"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="IME Toggle Button"
+ android:textOff="IME Invisible"
+ android:textOn="IME Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarIme"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="0" />
+
+ <TextView
+ android:id="@+id/textViewControllableInsets"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dp" />
+
+ <EditText
+ android:id="@+id/editText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="For testing IME..."
+ android:inputType="text"
+ android:text="" />
+
+ </LinearLayout>
+
+</ScrollView> \ No newline at end of file
diff --git a/tests/WindowInsetsTests/res/layout/main_activity.xml b/tests/WindowInsetsTests/res/layout/main_activity.xml
new file mode 100644
index 000000000000..621ed89204d1
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/main_activity.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/chat_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/chat_activity_title"
+ android:textAllCaps="false"/>
+
+ <Button
+ android:id="@+id/controller_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/controller_activity_title"
+ android:textAllCaps="false"/>
+
+</LinearLayout>
diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml
index 2b8e5f3da362..1a236c6f751d 100644
--- a/tests/WindowInsetsTests/res/values/strings.xml
+++ b/tests/WindowInsetsTests/res/values/strings.xml
@@ -16,5 +16,14 @@
-->
<resources>
- <string name="activity_title">New Insets Chat</string>
+ <string name="application_title">Window Insets Tests</string>
+ <string name="chat_activity_title">New Insets Chat</string>
+ <string name="controller_activity_title">Window Insets Controller</string>
+
+ <!-- The item positions should match the flag values respectively. -->
+ <string-array name="behaviors">
+ <item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item>
+ <item>BEHAVIOR_SHOW_BARS_BY_SWIPE</item>
+ <item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item>
+ </string-array>
</resources>
diff --git a/tests/WindowInsetsTests/res/values/styles.xml b/tests/WindowInsetsTests/res/values/styles.xml
index 220671fb8e71..a84ffbed600d 100644
--- a/tests/WindowInsetsTests/res/values/styles.xml
+++ b/tests/WindowInsetsTests/res/values/styles.xml
@@ -17,7 +17,7 @@
<resources>
- <style name="appTheme" parent="@style/Theme.MaterialComponents.Light">
+ <style name="chat" parent="@style/Theme.MaterialComponents.Light">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
@@ -63,5 +63,11 @@
<dimen name="bubble_padding">8dp</dimen>
<dimen name="bubble_padding_side">16dp</dimen>
+ <style name="controller" parent="android:Theme.Material">
+ <item name="android:colorPrimaryDark">#111111</item>
+ <item name="android:navigationBarColor">#111111</item>
+ <item name="android:colorPrimary">#222222</item>
+ <item name="android:colorAccent">#33ccff</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
index 498cb7c1c710..ba12acb2c877 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
@@ -30,7 +30,6 @@ import android.content.Context;
import android.graphics.Insets;
import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -39,8 +38,6 @@ import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
-import android.view.WindowInsetsController;
-import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
@@ -49,7 +46,7 @@ import java.util.List;
import androidx.appcompat.app.AppCompatActivity;
-public class WindowInsetsActivity extends AppCompatActivity {
+public class ChatActivity extends AppCompatActivity {
private View mRoot;
@@ -58,7 +55,7 @@ public class WindowInsetsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.window_inset_activity);
+ setContentView(R.layout.chat_activity);
setSupportActionBar(findViewById(R.id.toolbar));
@@ -71,7 +68,7 @@ public class WindowInsetsActivity extends AppCompatActivity {
mRoot.setOnTouchListener(new View.OnTouchListener() {
private final ViewConfiguration mViewConfiguration =
- ViewConfiguration.get(WindowInsetsActivity.this);
+ ViewConfiguration.get(ChatActivity.this);
WindowInsetsAnimationController mAnimationController;
WindowInsetsAnimationControlListener mCurrentRequest;
boolean mRequestedController = false;
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
new file mode 100644
index 000000000000..beb4049cb230
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.windowinsetstests;
+
+import android.app.Activity;
+import android.graphics.Insets;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+public class ControllerActivity extends Activity implements View.OnApplyWindowInsetsListener {
+
+ private ToggleButton mToggleStatus;
+ private SeekBar mSeekStatus;
+ private ToggleButton mToggleNavigation;
+ private SeekBar mSeekNavigation;
+ private ToggleButton mToggleIme;
+ private SeekBar mSeekIme;
+ private TextView mTextControllableInsets;
+ private boolean[] mNotFromUser = {false};
+ private WindowInsets mLastInsets;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.controller_activity);
+ final Spinner spinnerBehavior = findViewById(R.id.spinnerBehavior);
+ ArrayAdapter<CharSequence> adapterBehavior = ArrayAdapter.createFromResource(this,
+ R.array.behaviors, android.R.layout.simple_spinner_item);
+ adapterBehavior.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinnerBehavior.setAdapter(adapterBehavior);
+ spinnerBehavior.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ parent.getWindowInsetsController().setSystemBarsBehavior(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) { }
+ });
+ mToggleStatus = findViewById(R.id.toggleButtonStatus);
+ mToggleStatus.setTag(mNotFromUser);
+ mToggleStatus.setOnCheckedChangeListener(new ToggleListener(Type.statusBars()));
+ mSeekStatus = findViewById(R.id.seekBarStatus);
+ mSeekStatus.setOnSeekBarChangeListener(new SeekBarListener(Type.statusBars()));
+ mToggleNavigation = findViewById(R.id.toggleButtonNavigation);
+ mToggleNavigation.setTag(mNotFromUser);
+ mToggleNavigation.setOnCheckedChangeListener(new ToggleListener(Type.navigationBars()));
+ mSeekNavigation = findViewById(R.id.seekBarNavigation);
+ mSeekNavigation.setOnSeekBarChangeListener(new SeekBarListener(Type.navigationBars()));
+ mToggleIme = findViewById(R.id.toggleButtonIme);
+ mToggleIme.setTag(mNotFromUser);
+ mToggleIme.setOnCheckedChangeListener(new ToggleListener(Type.ime()));
+ mSeekIme = findViewById(R.id.seekBarIme);
+ mSeekIme.setOnSeekBarChangeListener(new SeekBarListener(Type.ime()));
+ mTextControllableInsets = findViewById(R.id.textViewControllableInsets);
+ final View contentView = findViewById(R.id.content);
+ contentView.setOnApplyWindowInsetsListener(this);
+ contentView.getWindowInsetsController().addOnControllableInsetsChangedListener(
+ (c, types) -> mTextControllableInsets.setText("ControllableInsetsTypes=" + types));
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ mNotFromUser[0] = true;
+ updateWidgets(insets, Type.statusBars(), mToggleStatus, mSeekStatus);
+ updateWidgets(insets, Type.navigationBars(), mToggleNavigation, mSeekNavigation);
+ updateWidgets(insets, Type.ime(), mToggleIme, mSeekIme);
+ mLastInsets = insets;
+ mNotFromUser[0] = false;
+
+ // Prevent triggering system gestures while controlling seek bars.
+ final Insets gestureInsets = insets.getInsets(Type.systemGestures());
+ v.setPadding(gestureInsets.left, 0, gestureInsets.right, 0);
+
+ return v.onApplyWindowInsets(insets);
+ }
+
+ private void updateWidgets(WindowInsets insets, int types, ToggleButton toggle, SeekBar seek) {
+ final boolean isVisible = insets.isVisible(types);
+ final boolean wasVisible = mLastInsets != null ? mLastInsets.isVisible(types) : !isVisible;
+ if (isVisible != wasVisible) {
+ toggle.setChecked(isVisible);
+ if (!seek.isPressed()) {
+ seek.setProgress(isVisible ? seek.getMax() : seek.getMin(), true /* animate*/);
+ }
+ }
+
+ }
+
+ private static class ToggleListener implements CompoundButton.OnCheckedChangeListener {
+
+ private final @Type.InsetsType int mTypes;
+
+ ToggleListener(int types) {
+ mTypes = types;
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (((boolean[]) buttonView.getTag())[0]) {
+ // not from user
+ return;
+ }
+ if (isChecked) {
+ buttonView.getWindowInsetsController().show(mTypes);
+ } else {
+ buttonView.getWindowInsetsController().hide(mTypes);
+ }
+ }
+ }
+
+ private static class SeekBarListener implements SeekBar.OnSeekBarChangeListener {
+
+ private final @Type.InsetsType int mTypes;
+
+ private WindowInsetsAnimationController mController;
+
+ SeekBarListener(int types) {
+ mTypes = types;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mController != null && fromUser) {
+ final int min = seekBar.getMin();
+ final float fraction = (progress - min) / (float) (seekBar.getMax() - min);
+ final Insets shownInsets = mController.getShownStateInsets();
+ final Insets hiddenInsets = mController.getHiddenStateInsets();
+ final Insets currentInsets = Insets.of(
+ (int) (0.5f + fraction * (shownInsets.left - hiddenInsets.left)),
+ (int) (0.5f + fraction * (shownInsets.top - hiddenInsets.top)),
+ (int) (0.5f + fraction * (shownInsets.right - hiddenInsets.right)),
+ (int) (0.5f + fraction * (shownInsets.bottom - hiddenInsets.bottom)));
+ mController.setInsetsAndAlpha(currentInsets, 1f /* alpha */, fraction);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (mController != null) {
+ return;
+ }
+ seekBar.getWindowInsetsController().controlWindowInsetsAnimation(mTypes,
+ -1 /* durationMs */, null /* interpolator */, null /* cancellationSignal */,
+ new WindowInsetsAnimationControlListener() {
+ @Override
+ public void onReady(WindowInsetsAnimationController controller, int types) {
+ mController = controller;
+ if (!seekBar.isPressed()) {
+ onStopTrackingTouch(seekBar);
+ }
+ }
+
+ @Override
+ public void onFinished(WindowInsetsAnimationController controller) {
+ mController = null;
+ }
+
+ @Override
+ public void onCancelled(WindowInsetsAnimationController controller) {
+ mController = null;
+ }
+ });
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ final int min = seekBar.getMin();
+ final int max = seekBar.getMax();
+ final boolean shown = (seekBar.getProgress() - min) * 2 > max - min;
+ seekBar.setProgress(shown ? max : min);
+ if (mController != null) {
+ mController.finish(shown);
+ }
+ }
+ }
+}
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java
new file mode 100644
index 000000000000..8b77a78ff51e
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.windowinsetstests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class WindowInsetsTestsMainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main_activity);
+
+ findViewById(R.id.chat_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ChatActivity.class)));
+
+ findViewById(R.id.controller_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ControllerActivity.class)));
+ }
+}
diff --git a/tests/benchmarks/internal/Android.bp b/tests/benchmarks/internal/Android.bp
new file mode 100644
index 000000000000..9c34eaf2af01
--- /dev/null
+++ b/tests/benchmarks/internal/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "InternalBenchTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.annotation_annotation",
+ ],
+ test_suites: ["device-tests"],
+ platform_apis: true,
+ certificate: "platform"
+}
+
diff --git a/packages/SystemUI/res/drawable/floating_dismiss_gradient.xml b/tests/benchmarks/internal/AndroidManifest.xml
index 8f7fb1011cf4..16023c6f3617 100644
--- a/packages/SystemUI/res/drawable/floating_dismiss_gradient.xml
+++ b/tests/benchmarks/internal/AndroidManifest.xml
@@ -13,12 +13,16 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <gradient
- android:angle="270"
- android:startColor="#00000000"
- android:endColor="#77000000"
- android:type="linear" />
-</shape> \ No newline at end of file
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.bench">
+
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.internal.bench"/>
+</manifest>
+
diff --git a/packages/SystemUI/res/layout/pip_menu_action.xml b/tests/benchmarks/internal/AndroidTest.xml
index 3ad35460d8b9..d776ee681c04 100644
--- a/packages/SystemUI/res/layout/pip_menu_action.xml
+++ b/tests/benchmarks/internal/AndroidTest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,16 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<ImageButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/pip_action_size"
- android:layout_height="@dimen/pip_action_size"
- android:padding="@dimen/pip_action_padding"
- android:background="?android:selectableItemBackgroundBorderless"
- android:forceHasOverlappingRendering="false" />
+<configuration description="Benchmark for internal classes/utilities.">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="InternalBenchTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.internal.bench" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+
+</configuration>
+
diff --git a/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java b/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java
new file mode 100644
index 000000000000..388548691b77
--- /dev/null
+++ b/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
+
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Compares the performance of regular lambda and pooled lambda. */
+@LargeTest
+public class LambdaPerfTest {
+ private static final boolean DEBUG = false;
+ private static final String TAG = LambdaPerfTest.class.getSimpleName();
+
+ private static final String LAMBDA_FORM_REGULAR = "regular";
+ private static final String LAMBDA_FORM_POOLED = "pooled";
+
+ private static final int WARMUP_ITERATIONS = 1000;
+ private static final int TEST_ITERATIONS = 3000000;
+ private static final int TASK_COUNT = 10;
+ private static final long DELAY_AFTER_BENCH_MS = 1000;
+
+ private String mMethodName;
+
+ private final Bundle mTestResults = new Bundle();
+ private final ArrayList<Task> mTasks = new ArrayList<>();
+
+ // The member fields are used to ensure lambda capturing. They don't have the actual meaning.
+ private final Task mTask = new Task();
+ private final Rect mBounds = new Rect();
+ private int mTaskId;
+ private long mTime;
+ private boolean mTop;
+
+ @Rule
+ public final TestRule mRule = (base, description) -> new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ mMethodName = description.getMethodName();
+ mTasks.clear();
+ for (int i = 0; i < TASK_COUNT; i++) {
+ final Task t = new Task();
+ mTasks.add(t);
+ }
+ base.evaluate();
+
+ getInstrumentation().sendStatus(Activity.RESULT_OK, mTestResults);
+ }
+ };
+
+ @Test
+ public void test1ParamConsumer() {
+ evaluate(LAMBDA_FORM_REGULAR, () -> forAllTask(t -> t.doSomething(mTask)));
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
+ PooledLambda.__(Task.class), mTask);
+ forAllTask(c);
+ c.recycle();
+ });
+ }
+
+ @Test
+ public void test2PrimitiveParamsConsumer() {
+ // Not in Integer#IntegerCache (-128~127) for autoboxing, that will create new object.
+ mTaskId = 12345;
+ mTime = 54321;
+
+ evaluate(LAMBDA_FORM_REGULAR, () -> forAllTask(t -> t.doSomething(mTaskId, mTime)));
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
+ PooledLambda.__(Task.class), mTaskId, mTime);
+ forAllTask(c);
+ c.recycle();
+ });
+ }
+
+ @Test
+ public void test3ParamsPredicate() {
+ mTop = true;
+ // In Integer#IntegerCache.
+ mTaskId = 10;
+
+ evaluate(LAMBDA_FORM_REGULAR, () -> handleTask(t -> t.doSomething(mBounds, mTop, mTaskId)));
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final PooledPredicate c = PooledLambda.obtainPredicate(Task::doSomething,
+ PooledLambda.__(Task.class), mBounds, mTop, mTaskId);
+ handleTask(c);
+ c.recycle();
+ });
+ }
+
+ @Test
+ public void testMessage() {
+ evaluate(LAMBDA_FORM_REGULAR, () -> {
+ final Message m = Message.obtain().setCallback(() -> mTask.doSomething(mTaskId, mTime));
+ m.getCallback().run();
+ m.recycle();
+ });
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final Message m = PooledLambda.obtainMessage(Task::doSomething, mTask, mTaskId, mTime);
+ m.getCallback().run();
+ m.recycle();
+ });
+ }
+
+ @Test
+ public void testRunnable() {
+ evaluate(LAMBDA_FORM_REGULAR, () -> {
+ final Runnable r = mTask::doSomething;
+ r.run();
+ });
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final Runnable r = PooledLambda.obtainRunnable(Task::doSomething, mTask).recycleOnUse();
+ r.run();
+ });
+ }
+
+ @Test
+ public void testMultiThread() {
+ final int numThread = 3;
+
+ final Runnable regularAction = () -> forAllTask(t -> t.doSomething(mTask));
+ final Runnable[] regularActions = new Runnable[numThread];
+ Arrays.fill(regularActions, regularAction);
+ evaluateMultiThread(LAMBDA_FORM_REGULAR, regularActions);
+
+ final Runnable pooledAction = () -> {
+ final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
+ PooledLambda.__(Task.class), mTask);
+ forAllTask(c);
+ c.recycle();
+ };
+ final Runnable[] pooledActions = new Runnable[numThread];
+ Arrays.fill(pooledActions, pooledAction);
+ evaluateMultiThread(LAMBDA_FORM_POOLED, pooledActions);
+ }
+
+ private void forAllTask(Consumer<Task> callback) {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ callback.accept(mTasks.get(i));
+ }
+ }
+
+ private void handleTask(Predicate<Task> callback) {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ final Task task = mTasks.get(i);
+ if (callback.test(task)) {
+ return;
+ }
+ }
+ }
+
+ private void evaluate(String title, Runnable action) {
+ for (int i = 0; i < WARMUP_ITERATIONS; i++) {
+ action.run();
+ }
+ performGc();
+
+ final GcStatus startGcStatus = getGcStatus();
+ final long startTime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ action.run();
+ }
+ evaluateResult(title, startGcStatus, startTime);
+ }
+
+ private void evaluateMultiThread(String title, Runnable[] actions) {
+ performGc();
+
+ final CountDownLatch latch = new CountDownLatch(actions.length);
+ final GcStatus startGcStatus = getGcStatus();
+ final long startTime = SystemClock.elapsedRealtime();
+ for (Runnable action : actions) {
+ new Thread() {
+ @Override
+ public void run() {
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ action.run();
+ }
+ latch.countDown();
+ };
+ }.start();
+ }
+ try {
+ latch.await();
+ } catch (InterruptedException ignored) {
+ }
+ evaluateResult(title, startGcStatus, startTime);
+ }
+
+ private void evaluateResult(String title, GcStatus startStatus, long startTime) {
+ final float elapsed = SystemClock.elapsedRealtime() - startTime;
+ // Sleep a while to see if GC may happen.
+ SystemClock.sleep(DELAY_AFTER_BENCH_MS);
+ final GcStatus endStatus = getGcStatus();
+ final GcInfo info = startStatus.calculateGcTime(endStatus, title, mTestResults);
+ Log.i(TAG, mMethodName + "_" + title + " execution time: "
+ + elapsed + "ms (avg=" + String.format("%.5f", elapsed / TEST_ITERATIONS) + "ms)"
+ + " GC time: " + String.format("%.3f", info.mTotalGcTime) + "ms"
+ + " GC paused time: " + String.format("%.3f", info.mTotalGcPausedTime) + "ms");
+ }
+
+ /** Cleans the test environment. */
+ private static void performGc() {
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ }
+
+ private static GcStatus getGcStatus() {
+ if (DEBUG) {
+ Log.i(TAG, "===== Read GC dump =====");
+ }
+ final GcStatus status = new GcStatus();
+ final List<String> vmDump = getVmDump();
+ Assume.assumeFalse("VM dump is empty", vmDump.isEmpty());
+ for (String line : vmDump) {
+ status.visit(line);
+ if (line.startsWith("DALVIK THREADS")) {
+ break;
+ }
+ }
+ return status;
+ }
+
+ private static List<String> getVmDump() {
+ final int myPid = Process.myPid();
+ // Another approach Debug#dumpJavaBacktraceToFileTimeout requires setenforce 0.
+ Process.sendSignal(myPid, Process.SIGNAL_QUIT);
+ // Give a chance to handle the signal.
+ SystemClock.sleep(100);
+
+ String dump = null;
+ final String pattern = myPid + " written to: ";
+ final List<String> logs = shell("logcat -v brief -d tombstoned:I *:S");
+ for (int i = logs.size() - 1; i >= 0; i--) {
+ final String log = logs.get(i);
+ // Log pattern: Traces for pid 9717 written to: /data/anr/trace_07
+ final int pos = log.indexOf(pattern);
+ if (pos > 0) {
+ dump = log.substring(pattern.length() + pos);
+ break;
+ }
+ }
+
+ Assume.assumeNotNull("Unable to find VM dump", dump);
+ // It requires system or root uid to read the trace.
+ return shell("cat " + dump);
+ }
+
+ private static List<String> shell(String command) {
+ final ParcelFileDescriptor.AutoCloseInputStream stream =
+ new ParcelFileDescriptor.AutoCloseInputStream(
+ getInstrumentation().getUiAutomation().executeShellCommand(command));
+ final ArrayList<String> lines = new ArrayList<>();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ lines.add(line);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return lines;
+ }
+
+ /** An empty class which provides some methods with different type arguments. */
+ static class Task {
+ void doSomething() {
+ }
+
+ void doSomething(Task t) {
+ }
+
+ void doSomething(int taskId, long time) {
+ }
+
+ boolean doSomething(Rect bounds, boolean top, int taskId) {
+ return false;
+ }
+ }
+
+ static class ValPattern {
+ static final int TYPE_COUNT = 0;
+ static final int TYPE_TIME = 1;
+ static final String PATTERN_COUNT = "(\\d+)";
+ static final String PATTERN_TIME = "(\\d+\\.?\\d+)(\\w+)";
+ final String mRawPattern;
+ final Pattern mPattern;
+ final int mType;
+
+ int mIntValue;
+ float mFloatValue;
+
+ ValPattern(String p, int type) {
+ mRawPattern = p;
+ mPattern = Pattern.compile(
+ p + (type == TYPE_TIME ? PATTERN_TIME : PATTERN_COUNT) + ".*");
+ mType = type;
+ }
+
+ boolean visit(String line) {
+ final Matcher matcher = mPattern.matcher(line);
+ if (!matcher.matches()) {
+ return false;
+ }
+ final String value = matcher.group(1);
+ if (value == null) {
+ return false;
+ }
+ if (mType == TYPE_COUNT) {
+ mIntValue = Integer.parseInt(value);
+ return true;
+ }
+ final float time = Float.parseFloat(value);
+ final String unit = matcher.group(2);
+ if (unit == null) {
+ return false;
+ }
+ // Refer to art/libartbase/base/time_utils.cc
+ switch (unit) {
+ case "s":
+ mFloatValue = time * 1000;
+ break;
+ case "ms":
+ mFloatValue = time;
+ break;
+ case "us":
+ mFloatValue = time / 1000;
+ break;
+ case "ns":
+ mFloatValue = time / 1000 / 1000;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return mRawPattern + (mType == TYPE_TIME ? (mFloatValue + "ms") : mIntValue);
+ }
+ }
+
+ /** Parses the dump pattern of Heap::DumpGcPerformanceInfo. */
+ private static class GcStatus {
+ private static final int TOTAL_GC_TIME_INDEX = 1;
+ private static final int TOTAL_GC_PAUSED_TIME_INDEX = 5;
+
+ // Refer to art/runtime/gc/heap.cc
+ final ValPattern[] mPatterns = {
+ new ValPattern("Total GC count: ", ValPattern.TYPE_COUNT),
+ new ValPattern("Total GC time: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total time waiting for GC to complete: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total blocking GC count: ", ValPattern.TYPE_COUNT),
+ new ValPattern("Total blocking GC time: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total mutator paused time: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total number of allocations ", ValPattern.TYPE_COUNT),
+ new ValPattern("concurrent copying paused: Sum: ", ValPattern.TYPE_TIME),
+ new ValPattern("concurrent copying total time: ", ValPattern.TYPE_TIME),
+ new ValPattern("concurrent copying freed: ", ValPattern.TYPE_COUNT),
+ new ValPattern("Peak regions allocated ", ValPattern.TYPE_COUNT),
+ };
+
+ void visit(String dumpLine) {
+ for (ValPattern p : mPatterns) {
+ if (p.visit(dumpLine)) {
+ if (DEBUG) {
+ Log.i(TAG, " " + p);
+ }
+ }
+ }
+ }
+
+ GcInfo calculateGcTime(GcStatus newStatus, String title, Bundle result) {
+ Log.i(TAG, "===== GC status of " + title + " =====");
+ final GcInfo info = new GcInfo();
+ for (int i = 0; i < mPatterns.length; i++) {
+ final ValPattern p = mPatterns[i];
+ if (p.mType == ValPattern.TYPE_COUNT) {
+ final int diff = newStatus.mPatterns[i].mIntValue - p.mIntValue;
+ Log.i(TAG, " " + p.mRawPattern + diff);
+ if (diff > 0) {
+ result.putInt("[" + title + "] " + p.mRawPattern, diff);
+ }
+ continue;
+ }
+ final float diff = newStatus.mPatterns[i].mFloatValue - p.mFloatValue;
+ Log.i(TAG, " " + p.mRawPattern + diff + "ms");
+ if (diff > 0) {
+ result.putFloat("[" + title + "] " + p.mRawPattern + "(ms)", diff);
+ }
+ if (i == TOTAL_GC_TIME_INDEX) {
+ info.mTotalGcTime = diff;
+ } else if (i == TOTAL_GC_PAUSED_TIME_INDEX) {
+ info.mTotalGcPausedTime = diff;
+ }
+ }
+ return info;
+ }
+ }
+
+ private static class GcInfo {
+ float mTotalGcTime;
+ float mTotalGcPausedTime;
+ }
+}
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 5dd0fda4da28..9ba56e44fe88 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -26,6 +26,7 @@ import android.net.NetworkStats.METERED_ALL
import android.net.NetworkStats.ROAMING_ALL
import android.net.NetworkTemplate.MATCH_MOBILE
import android.net.NetworkTemplate.MATCH_WIFI
+import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA
import android.net.NetworkTemplate.NETWORK_TYPE_ALL
import android.net.NetworkTemplate.buildTemplateMobileWithRatType
import android.telephony.TelephonyManager
@@ -145,11 +146,13 @@ class NetworkTemplateTest {
assertParcelSane(templateWifi, 8)
}
- // Verify NETWORK_TYPE_ALL does not conflict with TelephonyManager#NETWORK_TYPE_* constants.
+ // Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with
+ // TelephonyManager#NETWORK_TYPE_* constants.
@Test
- fun testNetworkTypeAll() {
+ fun testNetworkTypeConstants() {
for (ratType in TelephonyManager.getAllNetworkTypes()) {
assertNotEquals(NETWORK_TYPE_ALL, ratType)
+ assertNotEquals(NETWORK_TYPE_5G_NSA, ratType)
}
}
}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 39f849c340f7..5a29c2c96ba7 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -76,7 +76,6 @@ import com.android.server.pm.PackageList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
@@ -88,7 +87,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PermissionMonitorTest {
@@ -116,8 +114,8 @@ public class PermissionMonitorTest {
@Mock private INetd mNetdService;
@Mock private PackageManagerInternal mMockPmi;
@Mock private UserManager mUserManager;
+ @Mock private PermissionMonitor.Dependencies mDeps;
- private PackageManagerInternal.PackageListObserver mObserver;
private PermissionMonitor mPermissionMonitor;
@Before
@@ -131,7 +129,7 @@ public class PermissionMonitorTest {
new UserInfo(MOCK_USER2, "", 0),
}));
- mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService));
+ mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPmi);
@@ -139,11 +137,7 @@ public class PermissionMonitorTest {
/* observer */ null));
when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null);
mPermissionMonitor.startMonitoring();
-
- final ArgumentCaptor<PackageManagerInternal.PackageListObserver> observerCaptor =
- ArgumentCaptor.forClass(PackageManagerInternal.PackageListObserver.class);
- verify(mMockPmi).getPackageList(observerCaptor.capture());
- mObserver = observerCaptor.getValue();
+ verify(mMockPmi).getPackageList(mPermissionMonitor);
}
private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
@@ -290,14 +284,14 @@ public class PermissionMonitorTest {
@Test
public void testHasRestrictedNetworkPermissionSystemUid() {
- doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt();
+ doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
assertTrue(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
- doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt();
+ doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
assertFalse(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));
@@ -450,13 +444,13 @@ public class PermissionMonitorTest {
new int[]{MOCK_UID1});
// Remove MOCK_UID1, expect no permission left for all user.
- mPermissionMonitor.onPackageRemoved(MOCK_UID1);
- removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
// Remove SYSTEM_PACKAGE1, expect permission downgrade.
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
- removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID);
+ removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_PACKAGE1, SYSTEM_UID);
mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID});
@@ -465,7 +459,7 @@ public class PermissionMonitorTest {
// Remove all packages, expect no permission left.
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
- removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID);
+ removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_UID);
mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID, MOCK_UID1});
@@ -501,7 +495,8 @@ public class PermissionMonitorTest {
reset(mNetdService);
// When MOCK_UID1 package is uninstalled and reinstalled, expect Netd to be updated
- mPermissionMonitor.onPackageRemoved(UserHandle.getUid(MOCK_USER1, MOCK_UID1));
+ mPermissionMonitor.onPackageRemoved(
+ MOCK_PACKAGE1, UserHandle.getUid(MOCK_USER1, MOCK_UID1));
verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, UserHandle.getUid(MOCK_USER1, MOCK_UID1));
verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"),
@@ -545,7 +540,8 @@ public class PermissionMonitorTest {
aryEq(new int[] {MOCK_UID1}));
// Removed package should have its uid rules removed
- mPermissionMonitor.onPackageRemoved(UserHandle.getUid(MOCK_USER1, MOCK_UID1));
+ mPermissionMonitor.onPackageRemoved(
+ MOCK_PACKAGE1, UserHandle.getUid(MOCK_USER1, MOCK_UID1));
verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
}
@@ -559,9 +555,9 @@ public class PermissionMonitorTest {
}
}
- private void removePackageForUsers(int[] users, int uid) {
+ private void removePackageForUsers(int[] users, String packageName, int uid) {
for (final int user : users) {
- mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid));
+ mPermissionMonitor.onPackageRemoved(packageName, UserHandle.getUid(user, uid));
}
}
@@ -647,7 +643,7 @@ public class PermissionMonitorTest {
private PackageInfo addPackage(String packageName, int uid, String[] permissions)
throws Exception {
PackageInfo packageInfo = setPackagePermissions(packageName, uid, permissions);
- mObserver.onPackageAdded(packageName, uid);
+ mPermissionMonitor.onPackageAdded(packageName, uid);
return packageInfo;
}
@@ -678,7 +674,7 @@ public class PermissionMonitorTest {
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
when(mPackageManager.getPackagesForUid(MOCK_UID1))
.thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
- mObserver.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1);
+ mPermissionMonitor.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1);
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
| INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
}
@@ -692,7 +688,7 @@ public class PermissionMonitorTest {
| INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
- mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
}
@@ -705,7 +701,7 @@ public class PermissionMonitorTest {
| INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
- mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
@@ -719,10 +715,7 @@ public class PermissionMonitorTest {
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {});
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID1});
- // When updating a package, the broadcast receiver gets two broadcasts (a remove and then an
- // add), but the observer sees only one callback (an update).
- setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
- mObserver.onPackageChanged(MOCK_PACKAGE1, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
}
@@ -740,7 +733,7 @@ public class PermissionMonitorTest {
when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{
MOCK_PACKAGE2});
- mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 16fed39bcc74..6dc4fced19a2 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -17,6 +17,7 @@
package com.android.server.net;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -30,7 +31,9 @@ import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.content.Context;
+import android.net.NetworkTemplate;
import android.os.test.TestLooper;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
@@ -61,7 +64,6 @@ public final class NetworkStatsSubscriptionsMonitorTest {
private static final String TEST_IMSI3 = "466929999999999";
@Mock private Context mContext;
- @Mock private PhoneStateListener mPhoneStateListener;
@Mock private SubscriptionManager mSubscriptionManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private NetworkStatsSubscriptionsMonitor.Delegate mDelegate;
@@ -215,4 +217,55 @@ public final class NetworkStatsSubscriptionsMonitorTest {
verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
}
+
+
+ @Test
+ public void test5g() {
+ mMonitor.start();
+ // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
+ // before changing RAT type. Also capture listener for later use.
+ addTestSub(TEST_SUBID1, TEST_IMSI1);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+ verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
+ eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ final RatTypeListener listener = CollectionUtils
+ .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == TEST_SUBID1);
+ assertNotNull(listener);
+
+ // Set RAT type to 5G NSA (non-standalone) mode, verify the monitor outputs
+ // NETWORK_TYPE_5G_NSA.
+ final ServiceState serviceState = mock(ServiceState.class);
+ when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeChangedForSub(TEST_IMSI1, NetworkTemplate.NETWORK_TYPE_5G_NSA);
+ reset(mDelegate);
+
+ // Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE.
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
+ reset(mDelegate);
+
+ // Verify NR connected with other RAT type does not take effect.
+ when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_UMTS);
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ reset(mDelegate);
+
+ // Set RAT type to 5G standalone mode, the RAT type should be NR.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_NR);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
+ reset(mDelegate);
+
+ // Set NR state to none in standalone mode does not change anything.
+ when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_NR);
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
+ }
}
diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
index 6bd6985f9675..f30c35aca8da 100644
--- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
+++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
@@ -356,6 +356,9 @@ public class SystemPreparer extends ExternalResource {
/**
* Uses shell stop && start to "reboot" the device. May leave invalid state after each test.
* Whether this matters or not depends on what's being tested.
+ *
+ * TODO(b/159540015): There's a bug with this causing unnecessary disk space usage, which
+ * can eventually lead to an insufficient storage space error.
*/
START_STOP
}
diff --git a/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java b/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java
new file mode 100644
index 000000000000..b08621314ee0
--- /dev/null
+++ b/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback.host;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.rules.ExternalResource;
+
+public class AbandonSessionsRule extends ExternalResource {
+ private final BaseHostJUnit4Test mHost;
+
+ public AbandonSessionsRule(BaseHostJUnit4Test host) {
+ mHost = host;
+ }
+
+ @Override
+ protected void before() throws Throwable {
+ abandonSessions(mHost.getDevice());
+ }
+
+ @Override
+ protected void after() {
+ try {
+ abandonSessions(mHost.getDevice());
+ } catch (Exception ignore) {
+ }
+ }
+
+ /**
+ * Abandons all sessions to prevent interference in our tests.
+ */
+ private static void abandonSessions(ITestDevice device) throws Exception {
+ // No point in abandoning applied or failed sessions. We care about ready sessions only.
+ String cmdListReadySessions =
+ "pm list staged-sessions --only-sessionid --only-parent --only-ready";
+ String output = device.executeShellCommand(cmdListReadySessions);
+ if (output.trim().isEmpty()) {
+ // No sessions to abandon
+ return;
+ }
+ // Ensure we have sufficient privilege to abandon sessions from other apps
+ device.enableAdbRoot();
+ device.executeShellCommand("for i in $(" + cmdListReadySessions
+ + "); do pm install-abandon $i; done");
+ device.disableAdbRoot();
+ }
+}
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 32686538c10d..ff54fccda767 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -75,8 +75,10 @@ struct ResourcePathData {
};
// Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, const char dir_sep,
- std::string* out_error) {
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+ const char dir_sep,
+ std::string* out_error,
+ const CompileOptions& options) {
std::vector<std::string> parts = util::Split(path, dir_sep);
if (parts.size() < 2) {
if (out_error) *out_error = "bad resource path";
@@ -121,7 +123,11 @@ static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
}
}
- return ResourcePathData{Source(path), dir_str.to_string(), name.to_string(),
+ const Source res_path = options.source_path
+ ? StringPiece(options.source_path.value())
+ : StringPiece(path);
+
+ return ResourcePathData{res_path, dir_str.to_string(), name.to_string(),
extension.to_string(), config_str.to_string(), config};
}
@@ -667,7 +673,8 @@ int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter*
// Extract resource type information from the full path
std::string err_str;
ResourcePathData path_data;
- if (auto maybe_path_data = ExtractResourcePathData(path, inputs->GetDirSeparator(), &err_str)) {
+ if (auto maybe_path_data = ExtractResourcePathData(
+ path, inputs->GetDirSeparator(), &err_str, options)) {
path_data = maybe_path_data.value();
} else {
context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
@@ -747,6 +754,11 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
context.GetDiagnostics()->Error(DiagMessage()
<< "only one of --dir and --zip can be specified");
return 1;
+ } else if ((options_.res_dir || options_.res_zip) &&
+ options_.source_path && args.size() > 1) {
+ context.GetDiagnostics()->Error(DiagMessage(kPath)
+ << "Cannot use an overriding source path with multiple files.");
+ return 1;
} else if (options_.res_dir) {
if (!args.empty()) {
context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 1752a1adac24..1bc1f6651f85 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -28,6 +28,7 @@ namespace aapt {
struct CompileOptions {
std::string output_path;
+ Maybe<std::string> source_path;
Maybe<std::string> res_dir;
Maybe<std::string> res_zip;
Maybe<std::string> generate_text_symbols_path;
@@ -69,6 +70,9 @@ class CompileCommand : public Command {
AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
AddOptionalFlag("--trace-folder", "Generate systrace json trace fragment to specified folder.",
&trace_folder_);
+ AddOptionalFlag("--source-path",
+ "Sets the compiled resource file source file path to the given string.",
+ &options_.source_path);
}
int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index fb786a31360e..0aab94d3299f 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -24,6 +24,7 @@
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
#include "test/Test.h"
+#include "format/proto/ProtoDeserialize.h"
namespace aapt {
@@ -253,4 +254,90 @@ TEST_F(CompilerTest, DoNotTranslateTest) {
AssertTranslations(this, "donottranslate_foo", expected_not_translatable);
}
+TEST_F(CompilerTest, RelativePathTest) {
+ StdErrDiagnostics diag;
+ const std::string res_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "res"});
+
+ const std::string path_values_colors = GetTestPath("values/colors.xml");
+ WriteFile(path_values_colors, "<resources>"
+ "<color name=\"color_one\">#008577</color>"
+ "</resources>");
+
+ const std::string path_layout_layout_one = GetTestPath("layout/layout_one.xml");
+ WriteFile(path_layout_layout_one, "<LinearLayout "
+ "xmlns:android=\"http://schemas.android.com/apk/res/android\">"
+ "<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
+ "</LinearLayout>");
+
+ const std::string compiled_files_dir = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "compiled"});
+ CHECK(file::mkdirs(compiled_files_dir.data()));
+
+ const std::string path_values_colors_out =
+ BuildPath({compiled_files_dir,"values_colors.arsc.flat"});
+ const std::string path_layout_layout_one_out =
+ BuildPath({compiled_files_dir, "layout_layout_one.flat"});
+ ::android::base::utf8::unlink(path_values_colors_out.c_str());
+ ::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
+ const std::string apk_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "out.apk"});
+
+ const std::string source_set_res = BuildPath({"main", "res"});
+ const std::string relative_path_values_colors =
+ BuildPath({source_set_res, "values", "colors.xml"});
+ const std::string relative_path_layout_layout_one =
+ BuildPath({source_set_res, "layout", "layout_one.xml"});
+
+ CompileCommand(&diag).Execute({
+ path_values_colors,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_values_colors},
+ &std::cerr);
+
+ CompileCommand(&diag).Execute({
+ path_layout_layout_one,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_layout_layout_one},
+ &std::cerr);
+
+ std::ifstream ifs_values(path_values_colors_out);
+ std::string content_values((std::istreambuf_iterator<char>(ifs_values)),
+ (std::istreambuf_iterator<char>()));
+ ASSERT_NE(content_values.find(relative_path_values_colors), -1);
+ ASSERT_EQ(content_values.find(path_values_colors), -1);
+
+ Link({"-o", apk_path, "--manifest", GetDefaultManifest(), "--proto-format"},
+ compiled_files_dir, &diag);
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, &diag);
+ ResourceTable* resource_table = apk.get()->GetResourceTable();
+ const std::vector<std::unique_ptr<StringPool::Entry>>& pool_strings =
+ resource_table->string_pool.strings();
+
+ ASSERT_EQ(pool_strings.size(), 2);
+ ASSERT_EQ(pool_strings[0]->value, "res/layout/layout_one.xml");
+ ASSERT_EQ(pool_strings[1]->value, "res/layout-v1/layout_one.xml");
+
+ // Check resources.pb contains relative sources.
+ io::IFile* proto_file =
+ apk.get()->GetFileCollection()->FindFile("resources.pb");
+ std::unique_ptr<io::InputStream> proto_stream = proto_file->OpenInputStream();
+ io::ProtoInputStreamReader proto_reader(proto_stream.get());
+ pb::ResourceTable pb_table;
+ proto_reader.ReadMessage(&pb_table);
+
+ const std::string pool_strings_proto = pb_table.source_pool().data();
+
+ ASSERT_NE(pool_strings_proto.find(relative_path_values_colors), -1);
+ ASSERT_NE(pool_strings_proto.find(relative_path_layout_layout_one), -1);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 4a6bfd031284..53d9ffe21949 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1405,6 +1405,29 @@ class UsesStaticLibrary : public ManifestExtractor::Element {
}
};
+/** Represents <uses-native-library> elements. **/
+class UsesNativeLibrary : public ManifestExtractor::Element {
+ public:
+ UsesNativeLibrary() = default;
+ std::string name;
+ int required;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ if (!name.empty()) {
+ printer->Print(StringPrintf("uses-native-library%s:'%s'\n",
+ (required == 0) ? "-not-required" : "", name.data()));
+ }
+ }
+};
+
/**
* Represents <meta-data> elements. These tags are only printed when a flag is passed in to
* explicitly enable meta data printing.
@@ -2245,6 +2268,7 @@ T* ElementCast(ManifestExtractor::Element* element) {
{"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
{"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
};
auto check = kTagCheck.find(element->tag());
@@ -2295,6 +2319,7 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
{"uses-package", &CreateType<UsesPackage>},
{"additional-certificate", &CreateType<AdditionalCertificate>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
+ {"uses-native-library", &CreateType<UsesNativeLibrary>},
};
// Attempt to map the xml tag to a element inflater
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 3d69093a936a..49f8e1bcd30b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -422,6 +422,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
application_action.Action(OptionalNameIsJavaClassName);
application_action["uses-library"].Action(RequiredNameIsNotEmpty);
+ application_action["uses-native-library"].Action(RequiredNameIsNotEmpty);
application_action["library"].Action(RequiredNameIsNotEmpty);
application_action["profileable"];
diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp
new file mode 100644
index 000000000000..e255f7c784d3
--- /dev/null
+++ b/tools/processors/intdef_mappings/Android.bp
@@ -0,0 +1,33 @@
+java_plugin {
+ name: "intdef-annotation-processor",
+
+ processor_class: "android.processor.IntDefProcessor",
+
+ srcs: [
+ ":framework-annotations",
+ "src/**/*.java",
+ "src/**/*.kt"
+ ],
+
+ use_tools_jar: true,
+}
+
+java_test_host {
+ name: "intdef-annotation-processor-test",
+
+ srcs: [
+ "test/**/*.java",
+ "test/**/*.kt"
+ ],
+ java_resource_dirs: ["test/resources"],
+
+ static_libs: [
+ "compile-testing-prebuilt",
+ "truth-prebuilt",
+ "junit",
+ "guava",
+ "intdef-annotation-processor"
+ ],
+
+ test_suites: ["general-tests"],
+} \ No newline at end of file
diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
new file mode 100644
index 000000000000..84faeea36eea
--- /dev/null
+++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor
+
+import android.annotation.IntDef
+import com.sun.source.tree.IdentifierTree
+import com.sun.source.tree.MemberSelectTree
+import com.sun.source.tree.NewArrayTree
+import com.sun.source.util.SimpleTreeVisitor
+import com.sun.source.util.Trees
+import java.io.IOException
+import java.io.Writer
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.AnnotationValue
+import javax.lang.model.element.TypeElement
+import javax.tools.Diagnostic.Kind
+import javax.tools.StandardLocation.CLASS_OUTPUT
+import kotlin.collections.set
+
+
+/**
+ * The IntDefProcessor is intended to generate a mapping from ints to their respective string
+ * identifier for each IntDef for use by Winscope or any other tool which requires such a mapping.
+ *
+ * The processor will run when building :frameworks-all and dump all the IntDef mappings found the
+ * files the make up :frameworks-all as json to outputPath.
+ */
+class IntDefProcessor : AbstractProcessor() {
+ private val outputName = "intDefMapping.json"
+
+ override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
+
+ // Define what the annotation we care about are for compiler optimization
+ override fun getSupportedAnnotationTypes() = LinkedHashSet<String>().apply {
+ add(IntDef::class.java.name)
+ }
+
+ override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
+ // There should only be one matching annotation definition for intDef
+ val annotationType = annotations.firstOrNull() ?: return false
+ val annotatedElements = roundEnv.getElementsAnnotatedWith(annotationType)
+
+ val annotationTypeToIntDefMapping = annotatedElements.associate { annotatedElement ->
+ val type = (annotatedElement as TypeElement).qualifiedName.toString()
+ val mapping = generateIntDefMapping(annotatedElement, annotationType)
+ val intDef = annotatedElement.getAnnotation(IntDef::class.java)
+ type to IntDefMapping(mapping, intDef.flag)
+ }
+
+ try {
+ outputToFile(annotationTypeToIntDefMapping)
+ } catch (e: IOException) {
+ error("Failed to write IntDef mappings :: $e")
+ }
+ return false
+ }
+
+ private fun generateIntDefMapping(
+ annotatedElement: TypeElement,
+ annotationType: TypeElement
+ ): Map<Int, String> {
+ // LinkedHashMap makes sure ordering is the same as in the code
+ val mapping = LinkedHashMap<Int, String>()
+
+ val annotationMirror = annotatedElement.annotationMirrors
+ // Should only ever be one matching this condition
+ .first { it.annotationType.asElement() == annotationType }
+
+ val value = annotationMirror.elementValues.entries
+ .first { entry -> entry.key.simpleName.contentEquals("value") }
+ .value
+
+ val trees = Trees.instance(processingEnv)
+ val tree = trees.getTree(annotatedElement, annotationMirror, value)
+
+ val identifiers = ArrayList<String>()
+ tree.accept(IdentifierVisitor(), identifiers)
+
+ val values = value.value as List<AnnotationValue>
+
+ for (i in identifiers.indices) {
+ mapping[values[i].value as Int] = identifiers[i]
+ }
+
+ return mapping
+ }
+
+ private class IdentifierVisitor : SimpleTreeVisitor<Void, ArrayList<String>>() {
+ override fun visitNewArray(node: NewArrayTree, indentifiers: ArrayList<String>): Void? {
+ for (initializer in node.initializers) {
+ initializer.accept(this, indentifiers)
+ }
+
+ return null
+ }
+
+ override fun visitMemberSelect(node: MemberSelectTree, indentifiers: ArrayList<String>):
+ Void? {
+ indentifiers.add(node.identifier.toString())
+
+ return null
+ }
+
+ override fun visitIdentifier(node: IdentifierTree, indentifiers: ArrayList<String>): Void? {
+ indentifiers.add(node.name.toString())
+
+ return null
+ }
+ }
+
+ @Throws(IOException::class)
+ private fun outputToFile(annotationTypeToIntDefMapping: Map<String, IntDefMapping>) {
+ val resource = processingEnv.filer.createResource(
+ CLASS_OUTPUT, "com.android.winscope", outputName)
+ val writer = resource.openWriter()
+ serializeTo(annotationTypeToIntDefMapping, writer)
+ writer.close()
+ }
+
+ private fun error(message: String) {
+ processingEnv.messager.printMessage(Kind.ERROR, message)
+ }
+
+ private fun note(message: String) {
+ processingEnv.messager.printMessage(Kind.NOTE, message)
+ }
+
+ class IntDefMapping(val mapping: Map<Int, String>, val flag: Boolean) {
+ val size
+ get() = this.mapping.size
+
+ val entries
+ get() = this.mapping.entries
+ }
+
+ companion object {
+ fun serializeTo(
+ annotationTypeToIntDefMapping: Map<String, IntDefMapping>,
+ writer: Writer
+ ) {
+ val indent = " "
+
+ writer.appendln("{")
+
+ val intDefTypesCount = annotationTypeToIntDefMapping.size
+ var currentIntDefTypesCount = 0
+ for ((field, intDefMapping) in annotationTypeToIntDefMapping) {
+ writer.appendln("""$indent"$field": {""")
+
+ // Start IntDef
+
+ writer.appendln("""$indent$indent"flag": ${intDefMapping.flag},""")
+
+ writer.appendln("""$indent$indent"values": {""")
+ intDefMapping.entries.joinTo(writer, separator = ",\n") { (value, identifier) ->
+ """$indent$indent$indent"$value": "$identifier""""
+ }
+ writer.appendln()
+ writer.appendln("$indent$indent}")
+
+ // End IntDef
+
+ writer.append("$indent}")
+ if (++currentIntDefTypesCount < intDefTypesCount) {
+ writer.appendln(",")
+ } else {
+ writer.appendln("")
+ }
+ }
+
+ writer.appendln("}")
+ }
+ }
+}
diff --git a/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt b/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
new file mode 100644
index 000000000000..c0c159c98aac
--- /dev/null
+++ b/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.processor
+
+import android.processor.IntDefProcessor.IntDefMapping
+import com.google.common.collect.ObjectArrays.concat
+import com.google.testing.compile.CompilationSubject.assertThat
+import com.google.testing.compile.Compiler.javac
+import com.google.testing.compile.JavaFileObjects
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import java.io.StringWriter
+import javax.tools.JavaFileObject
+import javax.tools.StandardLocation.CLASS_OUTPUT
+
+/**
+ * Tests for [IntDefProcessor]
+ */
+class IntDefProcessorTest {
+ private val mAnnotations = arrayOf<JavaFileObject>(
+ JavaFileObjects.forSourceLines("android.annotation.IntDef",
+ "package android.annotation;",
+ "import java.lang.annotation.Retention;",
+ "import java.lang.annotation.Target;",
+ "import static java.lang.annotation.ElementType.ANNOTATION_TYPE;",
+ "import static java.lang.annotation.RetentionPolicy.SOURCE;",
+ "@Retention(SOURCE)",
+ "@Target({ANNOTATION_TYPE})",
+ "public @interface IntDef {",
+ " String[] prefix() default {};",
+ " String[] suffix() default {};",
+ " int[] value() default {};",
+ " boolean flag() default false;",
+ "}")
+ )
+
+ @Test
+ public fun annotationProcessorGeneratesMapping() {
+ val sources: Array<JavaFileObject> = arrayOf(
+ JavaFileObjects.forSourceLines(
+ "com.android.server.accessibility.magnification.MagnificationGestureMatcher",
+ "package com.android.server.accessibility.magnification;",
+ "import android.annotation.IntDef;",
+ "import java.lang.annotation.Retention;",
+ "import java.lang.annotation.RetentionPolicy;",
+ "class MagnificationGestureMatcher {",
+ " private static final int GESTURE_BASE = 100;",
+ " public static final int GESTURE_TWO_FINGER_DOWN = GESTURE_BASE + 1;",
+ " public static final int GESTURE_SWIPE = GESTURE_BASE + 2;",
+ " @IntDef(prefix = {\"GESTURE_MAGNIFICATION_\"}, value = {",
+ " GESTURE_TWO_FINGER_DOWN,",
+ " GESTURE_SWIPE",
+ " })",
+ " @Retention(RetentionPolicy.SOURCE)",
+ " @interface GestureId {}",
+ "}"
+ ),
+ JavaFileObjects.forSourceLines(
+ "android.service.storage.ExternalStorageService",
+ "package android.service.storage;",
+ "import android.annotation.IntDef;",
+ "import java.lang.annotation.Retention;",
+ "import java.lang.annotation.RetentionPolicy;",
+ "class MagnificationGestureMatcher {",
+ " public static final int FLAG_SESSION_TYPE_FUSE = 1 << 0;",
+ " public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 1 << 1;",
+ " @IntDef(flag = true, prefix = {\"FLAG_SESSION_\"},",
+ " value = {FLAG_SESSION_TYPE_FUSE, FLAG_SESSION_ATTRIBUTE_INDEXABLE})",
+ " @Retention(RetentionPolicy.SOURCE)",
+ " public @interface SessionFlag {}",
+ "}"
+ )
+ )
+
+ val expectedFile = """
+ {
+ "com.android.server.accessibility.magnification.MagnificationGestureMatcher.GestureId": {
+ "flag": false,
+ "values": {
+ "101": "GESTURE_TWO_FINGER_DOWN",
+ "102": "GESTURE_SWIPE"
+ }
+ },
+ "android.service.storage.MagnificationGestureMatcher.SessionFlag": {
+ "flag": true,
+ "values": {
+ "1": "FLAG_SESSION_TYPE_FUSE",
+ "2": "FLAG_SESSION_ATTRIBUTE_INDEXABLE"
+ }
+ }
+ }
+
+ """.trimIndent()
+
+ val filesToCompile = concat(mAnnotations, sources, JavaFileObject::class.java)
+
+ val compilation = javac()
+ .withProcessors(IntDefProcessor())
+ .compile(filesToCompile.toMutableList())
+
+ assertThat(compilation).succeeded()
+ assertThat(compilation).generatedFile(CLASS_OUTPUT, "com.android.winscope",
+ "intDefMapping.json").contentsAsUtf8String().isEqualTo(expectedFile)
+ }
+
+ @Test
+ public fun serializesMappingCorrectly() {
+ val map = linkedMapOf(
+ "SimpleIntDef" to IntDefMapping(linkedMapOf(
+ 0x0001 to "VAL_1",
+ 0x0002 to "VAL_2",
+ 0x0003 to "VAL_3"
+ ), flag = false),
+ "Flags" to IntDefMapping(linkedMapOf(
+ 0b0001 to "PRIVATE_FLAG_1",
+ 0b0010 to "PRIVATE_FLAG_2",
+ 0b0100 to "PRIVATE_FLAG_3"
+ ), flag = true)
+ )
+
+ val writer = StringWriter()
+ IntDefProcessor.serializeTo(map, writer)
+
+ val actualOutput = writer.toString()
+ val expectedOutput = """
+ {
+ "SimpleIntDef": {
+ "flag": false,
+ "values": {
+ "1": "VAL_1",
+ "2": "VAL_2",
+ "3": "VAL_3"
+ }
+ },
+ "Flags": {
+ "flag": true,
+ "values": {
+ "1": "PRIVATE_FLAG_1",
+ "2": "PRIVATE_FLAG_2",
+ "4": "PRIVATE_FLAG_3"
+ }
+ }
+ }
+
+ """.trimIndent()
+
+ assertEquals(actualOutput, expectedOutput)
+ }
+} \ No newline at end of file
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index a230de46dcf3..4c741c49cfdb 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -25,6 +25,7 @@
namespace android {
namespace stats_log_api_gen {
+using google::protobuf::OneofDescriptor;
using google::protobuf::EnumDescriptor;
using google::protobuf::FieldDescriptor;
using google::protobuf::FileDescriptor;
@@ -396,16 +397,14 @@ int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>
collate_enums(*field->enum_type(), &atField);
}
- // Generate signature for pushed atoms
- if (atomDecl->code < PULL_ATOM_START_ID) {
- if (javaType == JAVA_TYPE_ENUM) {
- // All enums are treated as ints when it comes to function signatures.
- signature->push_back(JAVA_TYPE_INT);
- } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
- signature->push_back(JAVA_TYPE_BYTE_ARRAY);
- } else {
- signature->push_back(javaType);
- }
+ // Generate signature for atom.
+ if (javaType == JAVA_TYPE_ENUM) {
+ // All enums are treated as ints when it comes to function signatures.
+ signature->push_back(JAVA_TYPE_INT);
+ } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
+ signature->push_back(JAVA_TYPE_BYTE_ARRAY);
+ } else {
+ signature->push_back(javaType);
}
atomDecl->fields.push_back(atField);
@@ -518,8 +517,7 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms*
shared_ptr<AtomDecl> atomDecl =
make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name());
- if (atomDecl->code < PULL_ATOM_START_ID &&
- atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
+ if (atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER,
ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL,
AnnotationValue(true));
@@ -537,7 +535,24 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms*
continue;
}
- FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = atoms->signatureInfoMap[signature];
+ const OneofDescriptor* oneofAtom = atomField->containing_oneof();
+ if (oneofAtom == nullptr) {
+ print_error(atomField, "Atom is not declared in a `oneof` field: %s\n",
+ atomField->name().c_str());
+ errorCount++;
+ continue;
+ }
+ else if ((oneofAtom->name() != ONEOF_PUSHED_ATOM_NAME) &&
+ (oneofAtom->name() != ONEOF_PULLED_ATOM_NAME)) {
+ print_error(atomField, "Atom is neither a pushed nor pulled atom: %s\n",
+ atomField->name().c_str());
+ errorCount++;
+ continue;
+ }
+
+ FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = oneofAtom->name() ==
+ ONEOF_PUSHED_ATOM_NAME ? atoms->signatureInfoMap[signature] :
+ atoms->pulledAtomsSignatureInfoMap[signature];
populateFieldNumberToAtomDeclSet(atomDecl, &fieldNumberToAtomDeclSet);
atoms->decls.insert(atomDecl);
@@ -556,6 +571,7 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms*
}
if (dbg) {
+ // Signatures for pushed atoms.
printf("signatures = [\n");
for (SignatureInfoMap::const_iterator it = atoms->signatureInfoMap.begin();
it != atoms->signatureInfoMap.end(); it++) {
@@ -566,6 +582,17 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms*
}
printf("\n");
}
+
+ // Signatures for pull atoms.
+ for (SignatureInfoMap::const_iterator it = atoms->pulledAtomsSignatureInfoMap.begin();
+ it != atoms->pulledAtomsSignatureInfoMap.end(); it++) {
+ printf(" ");
+ for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
+ jt++) {
+ printf(" %d", (int)*jt);
+ }
+ printf("\n");
+ }
printf("]\n");
}
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 10b34ecf5f54..e637ed945a08 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -29,6 +29,7 @@
namespace android {
namespace stats_log_api_gen {
+using google::protobuf::OneofDescriptor;
using google::protobuf::Descriptor;
using google::protobuf::FieldDescriptor;
using std::map;
@@ -41,6 +42,14 @@ const int PULL_ATOM_START_ID = 10000;
const int FIRST_UID_IN_CHAIN_ID = 0;
+/**
+ * The types of oneof atoms.
+ *
+ * `OneofDescriptor::name()` returns the name of the oneof.
+ */
+const string ONEOF_PUSHED_ATOM_NAME = "pushed";
+const string ONEOF_PULLED_ATOM_NAME = "pulled";
+
enum AnnotationId : uint8_t {
ANNOTATION_ID_IS_UID = 1,
ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2,
@@ -184,6 +193,7 @@ using SignatureInfoMap = map<vector<java_type_t>, FieldNumberToAtomDeclSet>;
struct Atoms {
SignatureInfoMap signatureInfoMap;
+ SignatureInfoMap pulledAtomsSignatureInfoMap;
AtomDeclSet decls;
AtomDeclSet non_chained_decls;
SignatureInfoMap nonChainedSignatureInfoMap;
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index f4c937c3f599..ffbe9f800736 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -96,29 +96,160 @@ static void write_annotations(FILE* out, int argIndex,
}
}
+static void write_method_signature(FILE* out, const vector<java_type_t>& signature,
+ const AtomDecl& attributionDecl) {
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+ arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
+ chainField.name.c_str());
+ }
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", android.util.SparseArray<Object> valueMap");
+ } else {
+ fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ }
+ argIndex++;
+ }
+}
+
+static int write_method_body(FILE* out, const vector<java_type_t>& signature,
+ const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
+ const AtomDecl& attributionDecl, const string& indent) {
+ // Start StatsEvent.Builder.
+ fprintf(out,
+ "%s final StatsEvent.Builder builder = "
+ "StatsEvent.newBuilder();\n",
+ indent.c_str());
+
+ // Write atom code.
+ fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str());
+ write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
+
+ // Write the args.
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+ arg++) {
+ switch (*arg) {
+ case JAVA_TYPE_BOOLEAN:
+ fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(),
+ argIndex);
+ break;
+ case JAVA_TYPE_INT:
+ case JAVA_TYPE_ENUM:
+ fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
+ break;
+ case JAVA_TYPE_FLOAT:
+ fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(),
+ argIndex);
+ break;
+ case JAVA_TYPE_LONG:
+ fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
+ break;
+ case JAVA_TYPE_STRING:
+ fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(),
+ argIndex);
+ break;
+ case JAVA_TYPE_BYTE_ARRAY:
+ fprintf(out,
+ "%s builder.writeByteArray(null == arg%d ? new byte[0] : "
+ "arg%d);\n",
+ indent.c_str(), argIndex, argIndex);
+ break;
+ case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+ const char* uidName = attributionDecl.fields.front().name.c_str();
+ const char* tagName = attributionDecl.fields.back().name.c_str();
+
+ fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str());
+ fprintf(out, "%s null == %s ? new int[0] : %s,\n",
+ indent.c_str(), uidName, uidName);
+ fprintf(out, "%s null == %s ? new String[0] : %s);\n",
+ indent.c_str(), tagName, tagName);
+ break;
+ }
+ case JAVA_TYPE_KEY_VALUE_PAIR:
+ fprintf(out, "\n");
+ fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str());
+ fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
+ fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
+ fprintf(out, "%s final int key = valueMap.keyAt(i);\n",
+ indent.c_str());
+ fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
+ indent.c_str());
+ fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
+ fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s intMap = new "
+ "android.util.SparseIntArray();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s intMap.put(key, (Integer) value);\n",
+ indent.c_str());
+ fprintf(out, "%s } else if (value instanceof Long) {\n",
+ indent.c_str());
+ fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s longMap = new "
+ "android.util.SparseLongArray();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s longMap.put(key, (Long) value);\n",
+ indent.c_str());
+ fprintf(out, "%s } else if (value instanceof String) {\n",
+ indent.c_str());
+ fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s stringMap = new "
+ "android.util.SparseArray<>();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s stringMap.put(key, (String) value);\n",
+ indent.c_str());
+ fprintf(out, "%s } else if (value instanceof Float) {\n",
+ indent.c_str());
+ fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s floatMap = new "
+ "android.util.SparseArray<>();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s floatMap.put(key, (Float) value);\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out,
+ "%s builder.writeKeyValuePairs("
+ "intMap, longMap, stringMap, floatMap);\n",
+ indent.c_str());
+ break;
+ default:
+ // Unsupported types: OBJECT, DOUBLE.
+ fprintf(stderr, "Encountered unsupported type.");
+ return 1;
+ }
+ write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
+ argIndex++;
+ }
+ return 0;
+}
+
static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
const AtomDecl& attributionDecl, const bool supportQ) {
for (auto signatureInfoMapIt = signatureInfoMap.begin();
signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
// Print method signature.
fprintf(out, " public static void write(int code");
- const vector<java_type_t>& signature = signatureInfoMapIt->first;
- const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
- chainField.name.c_str());
- }
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", android.util.SparseArray<Object> valueMap");
- } else {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
- }
- argIndex++;
- }
+ write_method_signature(out, signatureInfoMapIt->first, attributionDecl);
fprintf(out, ") {\n");
// Print method body.
@@ -128,130 +259,13 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa
indent = " ";
}
- // Start StatsEvent.Builder.
- fprintf(out,
- "%s final StatsEvent.Builder builder = "
- "StatsEvent.newBuilder();\n",
- indent.c_str());
-
- // Write atom code.
- fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str());
- write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
-
- // Write the args.
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- switch (*arg) {
- case JAVA_TYPE_BOOLEAN:
- fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_INT:
- case JAVA_TYPE_ENUM:
- fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_FLOAT:
- fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_LONG:
- fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_STRING:
- fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_BYTE_ARRAY:
- fprintf(out,
- "%s builder.writeByteArray(null == arg%d ? new byte[0] : "
- "arg%d);\n",
- indent.c_str(), argIndex, argIndex);
- break;
- case JAVA_TYPE_ATTRIBUTION_CHAIN: {
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
-
- fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str());
- fprintf(out, "%s null == %s ? new int[0] : %s,\n",
- indent.c_str(), uidName, uidName);
- fprintf(out, "%s null == %s ? new String[0] : %s);\n",
- indent.c_str(), tagName, tagName);
- break;
- }
- case JAVA_TYPE_KEY_VALUE_PAIR:
- fprintf(out, "\n");
- fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str());
- fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
- fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
- indent.c_str());
- fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
- fprintf(out, "%s final int key = valueMap.keyAt(i);\n",
- indent.c_str());
- fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
- indent.c_str());
- fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
- fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
- fprintf(out,
- "%s intMap = new "
- "android.util.SparseIntArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s intMap.put(key, (Integer) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof Long) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
- fprintf(out,
- "%s longMap = new "
- "android.util.SparseLongArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s longMap.put(key, (Long) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof String) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
- fprintf(out,
- "%s stringMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s stringMap.put(key, (String) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof Float) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
- fprintf(out,
- "%s floatMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s floatMap.put(key, (Float) value);\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out,
- "%s builder.writeKeyValuePairs("
- "intMap, longMap, stringMap, floatMap);\n",
- indent.c_str());
- break;
- default:
- // Unsupported types: OBJECT, DOUBLE.
- fprintf(stderr, "Encountered unsupported type.");
- return 1;
- }
- write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
- argIndex++;
+ int ret = write_method_body(out, signatureInfoMapIt->first, signatureInfoMapIt->second,
+ attributionDecl, indent);
+ if (ret != 0) {
+ return ret;
}
-
fprintf(out, "\n");
+
fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str());
fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str());
@@ -259,9 +273,9 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa
if (supportQ) {
fprintf(out, " } else {\n");
fprintf(out, " QLogger.write(code");
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signatureInfoMapIt->first.begin();
+ arg != signatureInfoMapIt->first.end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
const char* uidName = attributionDecl.fields.front().name.c_str();
const char* tagName = attributionDecl.fields.back().name.c_str();
@@ -285,6 +299,32 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa
return 0;
}
+static int write_java_build_stats_event_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
+ const AtomDecl& attributionDecl) {
+ for (auto signatureInfoMapIt = signatureInfoMap.begin();
+ signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+ // Print method signature.
+ fprintf(out, " public static StatsEvent buildStatsEvent(int code");
+ write_method_signature(out, signatureInfoMapIt->first, attributionDecl);
+ fprintf(out, ") {\n");
+
+ // Print method body.
+ string indent("");
+ int ret = write_method_body(out, signatureInfoMapIt->first, signatureInfoMapIt->second,
+ attributionDecl, indent);
+ if (ret != 0) {
+ return ret;
+ }
+ fprintf(out, "\n");
+
+ fprintf(out, "%s return builder.build();\n", indent.c_str());
+
+ fprintf(out, " }\n"); // method
+ fprintf(out, "\n");
+ }
+ return 0;
+}
+
int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
const string& javaClass, const string& javaPackage, const bool supportQ,
const bool supportWorkSource) {
@@ -319,6 +359,8 @@ int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attribut
fprintf(out, " // Write methods\n");
errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
+ errors += write_java_build_stats_event_methods(out, atoms.pulledAtomsSignatureInfoMap,
+ attributionDecl);
if (supportWorkSource) {
errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
}
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index aaa488e44fee..e658b62b8daa 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -58,7 +58,7 @@ message AllTypesAtom {
}
message Event {
- oneof event {
+ oneof pushed {
OutOfOrderAtom out_of_order_atom = 2;
IntAtom int_atom = 1;
AnotherIntAtom another_int_atom = 3;
@@ -74,7 +74,7 @@ message BadTypesAtom {
}
message BadTypesEvent {
- oneof event {
+ oneof pushed {
BadTypesAtom bad_types_atom = 1;
}
}
@@ -84,7 +84,7 @@ message BadSkippedFieldSingleAtom {
}
message BadSkippedFieldSingle {
- oneof event {
+ oneof pushed {
BadSkippedFieldSingleAtom bad = 1;
}
}
@@ -96,7 +96,7 @@ message BadSkippedFieldMultipleAtom {
}
message BadSkippedFieldMultiple {
- oneof event {
+ oneof pushed {
BadSkippedFieldMultipleAtom bad = 1;
}
}
@@ -107,11 +107,11 @@ message BadAttributionNodePositionAtom {
}
message BadAttributionNodePosition {
- oneof event { BadAttributionNodePositionAtom bad = 1; }
+ oneof pushed { BadAttributionNodePositionAtom bad = 1; }
}
message GoodEventWithBinaryFieldAtom {
- oneof event { GoodBinaryFieldAtom field1 = 1; }
+ oneof pushed { GoodBinaryFieldAtom field1 = 1; }
}
message ComplexField {
@@ -124,7 +124,7 @@ message GoodBinaryFieldAtom {
}
message BadEventWithBinaryFieldAtom {
- oneof event { BadBinaryFieldAtom field1 = 1; }
+ oneof pushed { BadBinaryFieldAtom field1 = 1; }
}
message BadBinaryFieldAtom {
@@ -133,7 +133,7 @@ message BadBinaryFieldAtom {
}
message BadStateAtoms {
- oneof event {
+ oneof pushed {
BadStateAtom1 bad1 = 1;
BadStateAtom2 bad2 = 2;
BadStateAtom3 bad3 = 3;
@@ -141,7 +141,7 @@ message BadStateAtoms {
}
message GoodStateAtoms {
- oneof event {
+ oneof pushed {
GoodStateAtom1 good1 = 1;
GoodStateAtom2 good2 = 2;
}
@@ -204,7 +204,7 @@ message NoModuleAtom {
}
message ModuleAtoms {
- oneof event {
+ oneof pushed {
ModuleOneAtom module_one_atom = 1 [(android.os.statsd.module) = "module1"];
ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.module) = "module2"];
ModuleOneAndTwoAtom module_one_and_two_atom = 3 [
@@ -213,3 +213,24 @@ message ModuleAtoms {
NoModuleAtom no_module_atom = 4;
}
}
+
+message NotAPushNorPullAtom {
+ oneof event {
+ IntAtom int_atom = 1;
+ }
+}
+
+message AtomNotInAOneof {
+ optional IntAtom int_atom = 1;
+}
+
+message PushedAndPulledAtoms {
+ oneof pushed {
+ IntAtom int_atom_1 = 1;
+ }
+
+ oneof pulled {
+ OutOfOrderAtom out_of_order_atom = 11;
+ AnotherIntAtom another_int_atom = 10;
+ }
+}
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index dbae58889333..5fd728a29c07 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -365,5 +365,69 @@ TEST(CollationTest, RecognizeModule1Atom) {
EXPECT_TRUE(annotation->value.boolValue);
}
+/**
+ * Test that an atom is not a pushed nor pulled atom.
+ */
+TEST(CollationTest, InvalidAtomType) {
+ Atoms atoms;
+ int errorCount = collate_atoms(NotAPushNorPullAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+
+ EXPECT_EQ(1, errorCount);
+}
+
+/**
+ * Test that an atom was not declared in a `oneof` field.
+ */
+TEST(CollationTest, AtomNotDeclaredInAOneof) {
+ Atoms atoms;
+ int errorCount = collate_atoms(AtomNotInAOneof::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+
+ EXPECT_EQ(1, errorCount);
+}
+
+/**
+ * Test a correct collation with pushed and pulled atoms.
+ */
+TEST(CollationTest, CollatePushedAndPulledAtoms) {
+ Atoms atoms;
+ int errorCount = collate_atoms(PushedAndPulledAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+
+ EXPECT_EQ(0, errorCount);
+ EXPECT_EQ(1ul, atoms.signatureInfoMap.size());
+ EXPECT_EQ(2ul, atoms.pulledAtomsSignatureInfoMap.size());
+
+ // IntAtom
+ EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT);
+
+ // AnotherIntAtom
+ EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT);
+
+ // OutOfOrderAtom
+ EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT);
+
+ EXPECT_EQ(3ul, atoms.decls.size());
+
+ AtomDeclSet::const_iterator atomIt = atoms.decls.begin();
+ EXPECT_EQ(1, (*atomIt)->code);
+ EXPECT_EQ("int_atom_1", (*atomIt)->name);
+ EXPECT_EQ("IntAtom", (*atomIt)->message);
+ EXPECT_NO_ENUM_FIELD((*atomIt));
+ atomIt++;
+
+ EXPECT_EQ(10, (*atomIt)->code);
+ EXPECT_EQ("another_int_atom", (*atomIt)->name);
+ EXPECT_EQ("AnotherIntAtom", (*atomIt)->message);
+ EXPECT_NO_ENUM_FIELD((*atomIt));
+ atomIt++;
+
+ EXPECT_EQ(11, (*atomIt)->code);
+ EXPECT_EQ("out_of_order_atom", (*atomIt)->name);
+ EXPECT_EQ("OutOfOrderAtom", (*atomIt)->message);
+ EXPECT_NO_ENUM_FIELD((*atomIt));
+ atomIt++;
+
+ EXPECT_EQ(atoms.decls.end(), atomIt);
+}
+
} // namespace stats_log_api_gen
} // namespace android
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index 819e75ba1fed..61ce44c3b5b9 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -20,6 +20,7 @@ cc_binary_host {
"libutils",
"libcutils",
"liblog",
+ "libui-types",
],
// This tool is prebuilt if we're doing an app-only build.
diff --git a/wifi/TEST_MAPPING b/wifi/TEST_MAPPING
new file mode 100644
index 000000000000..fde3a6aa993c
--- /dev/null
+++ b/wifi/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsWifiTestCases",
+ "options": [
+ {
+ "exclude-annotation": "android.net.wifi.cts.VirtualDeviceNotSupported"
+ }
+ ]
+ }
+ ]
+}
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index 985ff1df151f..53c69c47d052 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -241,6 +241,7 @@ package android.net.wifi {
method public boolean areFeaturesSupported(long);
method public int describeContents();
method public int getMaxSupportedClients();
+ method @NonNull public int[] getSupportedChannelList(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR;
field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index dcb57ecc933f..99c4eac7977b 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -20,11 +20,14 @@ import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.net.wifi.SoftApConfiguration.BandType;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -36,6 +39,8 @@ import java.util.Objects;
@SystemApi
public final class SoftApCapability implements Parcelable {
+ private static final String TAG = "SoftApCapability";
+ private static final int[] EMPTY_INT_ARRAY = new int[0];
/**
* Support for automatic channel selection in driver (ACS).
* Driver will auto select best channel based on interference to optimize performance.
@@ -83,6 +88,21 @@ public final class SoftApCapability implements Parcelable {
private int mMaximumSupportedClientNumber;
/**
+ * A list storing supported 2.4G channels.
+ */
+ private int[] mSupportedChannelListIn24g = EMPTY_INT_ARRAY;
+
+ /**
+ * A list storing supported 5G channels.
+ */
+ private int[] mSupportedChannelListIn5g = EMPTY_INT_ARRAY;
+
+ /**
+ * A list storing supported 6G channels.
+ */
+ private int[] mSupportedChannelListIn6g = EMPTY_INT_ARRAY;
+
+ /**
* Get the maximum supported client numbers which AP resides on.
*/
public int getMaxSupportedClients() {
@@ -111,12 +131,76 @@ public final class SoftApCapability implements Parcelable {
}
/**
+ * Set supported channel list in target band type.
+ *
+ * @param band One of the following band types:
+ * {@link SoftApConfiguation#BAND_2GHZ}, {@link SoftApConfiguation#BAND_5GHZ} or
+ * {@link SoftApConfiguation#BAND_6GHZ}.
+ * @param supportedChannelList supported channel list in target band
+ * @return true if band and supportedChannelList are valid, otherwise false.
+ *
+ * @throws IllegalArgumentException when band type is invalid.
+ * @hide
+ */
+ public boolean setSupportedChannelList(@BandType int band,
+ @Nullable int[] supportedChannelList) {
+ if (supportedChannelList == null) return false;
+ switch (band) {
+ case SoftApConfiguration.BAND_2GHZ:
+ mSupportedChannelListIn24g = supportedChannelList;
+ break;
+ case SoftApConfiguration.BAND_5GHZ:
+ mSupportedChannelListIn5g = supportedChannelList;
+ break;
+ case SoftApConfiguration.BAND_6GHZ:
+ mSupportedChannelListIn6g = supportedChannelList;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid band: " + band);
+ }
+ return true;
+ }
+
+ /**
+ * Returns a list of the supported channels in the given band.
+ * The result depends on the on the country code that has been set.
+ * Can be used to set the channel of the AP with the
+ * {@link SoftapConfiguration.Builder#setChannel(int, int)} API.
+ *
+ * @param band One of the following band types:
+ * {@link SoftApConfiguation#BAND_2GHZ}, {@link SoftApConfiguation#BAND_5GHZ} or
+ * {@link SoftApConfiguation#BAND_6GHZ}.
+ * @return List of supported channels for the band.
+ *
+ * @throws IllegalArgumentException when band type is invalid.
+ */
+ @NonNull
+ public int[] getSupportedChannelList(@BandType int band) {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+ throw new UnsupportedOperationException();
+ }
+ switch (band) {
+ case SoftApConfiguration.BAND_2GHZ:
+ return mSupportedChannelListIn24g;
+ case SoftApConfiguration.BAND_5GHZ:
+ return mSupportedChannelListIn5g;
+ case SoftApConfiguration.BAND_6GHZ:
+ return mSupportedChannelListIn6g;
+ default:
+ throw new IllegalArgumentException("Invalid band: " + band);
+ }
+ }
+
+ /**
* @hide
*/
public SoftApCapability(@Nullable SoftApCapability source) {
if (source != null) {
mSupportedFeatures = source.mSupportedFeatures;
mMaximumSupportedClientNumber = source.mMaximumSupportedClientNumber;
+ mSupportedChannelListIn24g = source.mSupportedChannelListIn24g;
+ mSupportedChannelListIn5g = source.mSupportedChannelListIn5g;
+ mSupportedChannelListIn6g = source.mSupportedChannelListIn6g;
}
}
@@ -144,15 +228,20 @@ public final class SoftApCapability implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(mSupportedFeatures);
dest.writeInt(mMaximumSupportedClientNumber);
+ dest.writeIntArray(mSupportedChannelListIn24g);
+ dest.writeIntArray(mSupportedChannelListIn5g);
+ dest.writeIntArray(mSupportedChannelListIn6g);
}
@NonNull
/** Implement the Parcelable interface */
public static final Creator<SoftApCapability> CREATOR = new Creator<SoftApCapability>() {
public SoftApCapability createFromParcel(Parcel in) {
- long supportedFeatures = in.readLong();
- SoftApCapability capability = new SoftApCapability(supportedFeatures);
+ SoftApCapability capability = new SoftApCapability(in.readLong());
capability.mMaximumSupportedClientNumber = in.readInt();
+ capability.setSupportedChannelList(SoftApConfiguration.BAND_2GHZ, in.createIntArray());
+ capability.setSupportedChannelList(SoftApConfiguration.BAND_5GHZ, in.createIntArray());
+ capability.setSupportedChannelList(SoftApConfiguration.BAND_6GHZ, in.createIntArray());
return capability;
}
@@ -167,6 +256,10 @@ public final class SoftApCapability implements Parcelable {
StringBuilder sbuf = new StringBuilder();
sbuf.append("SupportedFeatures=").append(mSupportedFeatures);
sbuf.append("MaximumSupportedClientNumber=").append(mMaximumSupportedClientNumber);
+ sbuf.append("SupportedChannelListIn24g")
+ .append(Arrays.toString(mSupportedChannelListIn24g));
+ sbuf.append("SupportedChannelListIn5g").append(Arrays.toString(mSupportedChannelListIn5g));
+ sbuf.append("SupportedChannelListIn6g").append(Arrays.toString(mSupportedChannelListIn6g));
return sbuf.toString();
}
@@ -176,11 +269,17 @@ public final class SoftApCapability implements Parcelable {
if (!(o instanceof SoftApCapability)) return false;
SoftApCapability capability = (SoftApCapability) o;
return mSupportedFeatures == capability.mSupportedFeatures
- && mMaximumSupportedClientNumber == capability.mMaximumSupportedClientNumber;
+ && mMaximumSupportedClientNumber == capability.mMaximumSupportedClientNumber
+ && Arrays.equals(mSupportedChannelListIn24g, capability.mSupportedChannelListIn24g)
+ && Arrays.equals(mSupportedChannelListIn5g, capability.mSupportedChannelListIn5g)
+ && Arrays.equals(mSupportedChannelListIn6g, capability.mSupportedChannelListIn6g);
}
@Override
public int hashCode() {
- return Objects.hash(mSupportedFeatures, mMaximumSupportedClientNumber);
+ return Objects.hash(mSupportedFeatures, mMaximumSupportedClientNumber,
+ Arrays.hashCode(mSupportedChannelListIn24g),
+ Arrays.hashCode(mSupportedChannelListIn5g),
+ Arrays.hashCode(mSupportedChannelListIn6g));
}
}
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 0f734ceb74c9..f919ea4c4797 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -724,14 +724,17 @@ public final class SoftApConfiguration implements Parcelable {
* @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
* responsible for avoiding collisions.
* @return Builder for chaining.
- * @throws IllegalArgumentException when the given BSSID is the all-zero or broadcast MAC
- * address.
+ * @throws IllegalArgumentException when the given BSSID is the all-zero
+ * , multicast or broadcast MAC address.
*/
@NonNull
public Builder setBssid(@Nullable MacAddress bssid) {
if (bssid != null) {
Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS));
- Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS));
+ if (bssid.getAddressType() != MacAddress.TYPE_UNICAST) {
+ throw new IllegalArgumentException("bssid doesn't support "
+ + "multicast or broadcast mac address");
+ }
}
mBssid = bssid;
return this;
@@ -821,6 +824,9 @@ public final class SoftApConfiguration implements Parcelable {
* Specifies the channel and associated band for the AP.
*
* The channel which AP resides on. Valid channels are country dependent.
+ * The {@link SoftApCapability#getSupportedChannelList(int)} can be used to obtain
+ * valid channels.
+ *
* <p>
* The default for the channel is a the special value 0 to have the framework
* auto-select a valid channel from the band configured with
diff --git a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
index 73b501a4d8f2..b9c640cc3980 100644
--- a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.os.Build;
import android.os.Parcel;
import static org.junit.Assert.assertEquals;
@@ -37,8 +38,12 @@ public class SoftApCapabilityTest {
public void testCopyOperator() throws Exception {
long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT
| SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD;
+ int[] testSupported2Glist = {1, 2, 3, 4};
+ int[] testSupported5Glist = {36, 149};
SoftApCapability capability = new SoftApCapability(testSoftApFeature);
capability.setMaxSupportedClients(10);
+ capability.setSupportedChannelList(SoftApConfiguration.BAND_2GHZ, testSupported2Glist);
+ capability.setSupportedChannelList(SoftApConfiguration.BAND_5GHZ, testSupported5Glist);
SoftApCapability copiedCapability = new SoftApCapability(capability);
@@ -55,6 +60,11 @@ public class SoftApCapabilityTest {
| SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD;
SoftApCapability capability = new SoftApCapability(testSoftApFeature);
capability.setMaxSupportedClients(10);
+ int[] testSupported2Glist = {1, 2, 3, 4};
+ int[] testSupported5Glist = {36, 149};
+
+ capability.setSupportedChannelList(SoftApConfiguration.BAND_2GHZ, testSupported2Glist);
+ capability.setSupportedChannelList(SoftApConfiguration.BAND_5GHZ, testSupported5Glist);
Parcel parcelW = Parcel.obtain();
capability.writeToParcel(parcelW, 0);
@@ -70,4 +80,26 @@ public class SoftApCapabilityTest {
assertEquals(capability.hashCode(), fromParcel.hashCode());
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetSupportedChannelListWithInvalidBand() {
+ long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT
+ | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD;
+ SoftApCapability capability = new SoftApCapability(testSoftApFeature);
+ capability.setSupportedChannelList(
+ SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ, new int[0]);
+
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetSupportedChannelListWithInvalidBand() {
+ long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT
+ | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD;
+ SoftApCapability capability = new SoftApCapability(testSoftApFeature);
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
+ capability.getSupportedChannelList(
+ SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ);
+ } else {
+ throw new IllegalArgumentException("API doesn't support in current SDK version");
+ }
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 40f5c3ed8e5e..c2d0d6d81e84 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -35,6 +35,7 @@ import java.util.Random;
@SmallTest
public class SoftApConfigurationTest {
private static final String TEST_CHAR_SET_AS_STRING = "abcdefghijklmnopqrstuvwxyz0123456789";
+ private static final String TEST_BSSID = "aa:22:33:aa:bb:cc";
private SoftApConfiguration parcelUnparcel(SoftApConfiguration configIn) {
Parcel parcel = Parcel.obtain();
@@ -67,12 +68,13 @@ public class SoftApConfigurationTest {
@Test
public void testBasicSettings() {
+ MacAddress testBssid = MacAddress.fromString(TEST_BSSID);
SoftApConfiguration original = new SoftApConfiguration.Builder()
.setSsid("ssid")
- .setBssid(MacAddress.fromString("11:22:33:44:55:66"))
+ .setBssid(testBssid)
.build();
assertThat(original.getSsid()).isEqualTo("ssid");
- assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66"));
+ assertThat(original.getBssid()).isEqualTo(testBssid);
assertThat(original.getPassphrase()).isNull();
assertThat(original.getSecurityType()).isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN);
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
@@ -221,6 +223,20 @@ public class SoftApConfigurationTest {
}
@Test(expected = IllegalArgumentException.class)
+ public void testInvalidBroadcastBssid() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setBssid(MacAddress.BROADCAST_ADDRESS)
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidMulticastBssid() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setBssid(MacAddress.fromString("01:aa:bb:cc:dd:ee"))
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
public void testInvalidShortPasswordLengthForWpa2() {
SoftApConfiguration original = new SoftApConfiguration.Builder()
.setPassphrase(generateRandomString(SoftApConfiguration.PSK_MIN_LEN - 1),